]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/map.cpp
Merge branch 'illwieckz/files'
[xonotic/netradiant.git] / radiant / map.cpp
index 078f771977a7126232b63c886b34e2be68cf4e99..147cfa98762e7842f734be9f8fdedbc02e839b1f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 1999-2007 id Software, Inc. and contributors.
+   Copyright (C) 1999-2006 Id Software, Inc. and contributors.
    For a list of contributors, see the accompanying CONTRIBUTORS file.
 
    This file is part of GtkRadiant.
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include "stdafx.h"
-#include <string.h>
-#if defined ( __linux__ ) || defined ( __APPLE__ )
-#include <unistd.h>
-#endif
-#include "preferences.h"
+#include "map.h"
+
+#include "debugging/debugging.h"
+
+#include "imap.h"
+MapModules& ReferenceAPI_getMapModules();
+#include "iselection.h"
+#include "iundo.h"
+#include "ibrush.h"
+#include "ifilter.h"
+#include "ireference.h"
+#include "ifiletypes.h"
+#include "ieclass.h"
+#include "irender.h"
+#include "ientity.h"
+#include "editable.h"
+#include "iarchive.h"
+#include "ifilesystem.h"
+#include "namespace.h"
+#include "moduleobserver.h"
+
+#include <set>
+
+#include <gtk/gtkmain.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkcellrenderertext.h>
+
+#include "scenelib.h"
+#include "transformlib.h"
+#include "selectionlib.h"
+#include "instancelib.h"
+#include "traverselib.h"
+#include "maplib.h"
+#include "eclasslib.h"
+#include "cmdlib.h"
+#include "stream/textfilestream.h"
+#include "os/path.h"
+#include "uniquenames.h"
+#include "modulesystem/singletonmodule.h"
+#include "modulesystem/moduleregistry.h"
+#include "stream/stringstream.h"
+#include "signal/signal.h"
+
+#include "gtkutil/filechooser.h"
+#include "timer.h"
+#include "select.h"
+#include "plugin.h"
+#include "filetypes.h"
+#include "gtkdlgs.h"
+#include "entityinspector.h"
+#include "points.h"
+#include "qe3.h"
+#include "camwindow.h"
+#include "xywindow.h"
 #include "mainframe.h"
-#include "gtkmisc.h"
-#include "filters.h"
-
-extern MainFrame* g_pParentWnd;
-
-int modified;   // for quit confirmation (0 = clean, 1 = unsaved,
-// 2 = autosaved, but not regular saved)
-
-char currentmap[1024];
-
-brush_t active_brushes;     // brushes currently being displayed
-brush_t selected_brushes;   // highlighted
-
-face_t  *selected_face;
-brush_t *selected_face_brush;
-
-brush_t filtered_brushes;   // brushes that have been filtered or regioned
-
-entity_t entities;          // head/tail of doubly linked list
-
-entity_t    *world_entity = NULL; // "classname" "worldspawn" !
-
-void Map_Init(){
-       Map_Free();
+#include "preferences.h"
+#include "preferencesystem.h"
+#include "referencecache.h"
+#include "mru.h"
+#include "commands.h"
+#include "autosave.h"
+#include "brushmodule.h"
+#include "brush.h"
+
+class NameObserver
+{
+UniqueNames& m_names;
+CopiedString m_name;
+
+void construct(){
+       if ( !empty() ) {
+               //globalOutputStream() << "construct " << makeQuoted(c_str()) << "\n";
+               m_names.insert( name_read( c_str() ) );
+       }
+}
+void destroy(){
+       if ( !empty() ) {
+               //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
+               m_names.erase( name_read( c_str() ) );
+       }
 }
 
+NameObserver& operator=( const NameObserver& other );
+public:
+NameObserver( UniqueNames& names ) : m_names( names ){
+       construct();
+}
+NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
+       construct();
+}
+~NameObserver(){
+       destroy();
+}
+bool empty() const {
+       return string_empty( c_str() );
+}
+const char* c_str() const {
+       return m_name.c_str();
+}
+void nameChanged( const char* name ){
+       destroy();
+       m_name = name;
+       construct();
+}
+typedef MemberCaller1<NameObserver, const char*, &NameObserver::nameChanged> NameChangedCaller;
+};
+
+class BasicNamespace : public Namespace
+{
+typedef std::map<NameCallback, NameObserver> Names;
+Names m_names;
+UniqueNames m_uniqueNames;
+public:
+~BasicNamespace(){
+       ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
+}
+void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
+       std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
+       ASSERT_MESSAGE( result.second, "cannot attach name" );
+       attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
+       //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
+}
+void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
+       Names::iterator i = m_names.find( setName );
+       ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
+       //globalOutputStream() << "detach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
+       detachObserver( NameObserver::NameChangedCaller( ( *i ).second ) );
+       m_names.erase( i );
+}
 
-bool g_bCancel_Map_LoadFile;  // Hydra: moved this here
-
-// TTimo
-// need that in a variable, will have to tweak depending on the game
-int g_MaxWorldCoord = 64 * 1024;
-int g_MinWorldCoord = -64 * 1024;
+void makeUnique( const char* name, const NameCallback& setName ) const {
+       char buffer[1024];
+       name_write( buffer, m_uniqueNames.make_unique( name_read( name ) ) );
+       setName( buffer );
+}
 
-// the max size we allow on brushes, this is dependant on world coords too
-// makes more sense to say smaller I think?
-int g_MaxBrushSize = ( g_MaxWorldCoord - 1 ) * 2;
+void mergeNames( const BasicNamespace& other ) const {
+       typedef std::list<NameCallback> SetNameCallbacks;
+       typedef std::map<CopiedString, SetNameCallbacks> NameGroups;
+       NameGroups groups;
 
-void AddRegionBrushes( void );
-void RemoveRegionBrushes( void );
+       UniqueNames uniqueNames( other.m_uniqueNames );
 
-/*
-   =============================================================
+       for ( Names::const_iterator i = m_names.begin(); i != m_names.end(); ++i )
+       {
+               groups[( *i ).second.c_str()].push_back( ( *i ).first );
+       }
 
-   Cross map selection saving
+       for ( NameGroups::iterator i = groups.begin(); i != groups.end(); ++i )
+       {
+               name_t uniqueName( uniqueNames.make_unique( name_read( ( *i ).first.c_str() ) ) );
+               uniqueNames.insert( uniqueName );
 
-   this could fuck up if you have only part of a complex entity selected...
-   =============================================================
- */
+               char buffer[1024];
+               name_write( buffer, uniqueName );
 
-brush_t between_brushes;
-entity_t between_entities;
+               //globalOutputStream() << "renaming " << makeQuoted((*i).first.c_str()) << " to " << makeQuoted(buffer) << "\n";
 
-bool g_bRestoreBetween = false;
+               SetNameCallbacks& setNameCallbacks = ( *i ).second;
 
-void Map_SaveBetween( void ){
-       if ( g_pParentWnd->ActiveXY() ) {
-               g_bRestoreBetween = true;
-               g_pParentWnd->ActiveXY()->Copy();
+               for ( SetNameCallbacks::const_iterator j = setNameCallbacks.begin(); j != setNameCallbacks.end(); ++j )
+               {
+                       ( *j )( buffer );
+               }
        }
-       return;
 }
+};
 
-void Map_RestoreBetween( void ){
-       if ( g_pParentWnd->ActiveXY() && g_bRestoreBetween ) {
-               g_pParentWnd->ActiveXY()->Paste();
-       }
-}
+BasicNamespace g_defaultNamespace;
+BasicNamespace g_cloneNamespace;
 
-//============================================================================
+class NamespaceAPI
+{
+Namespace* m_namespace;
+public:
+typedef Namespace Type;
+STRING_CONSTANT( Name, "*" );
 
-bool CheckForTinyBrush( brush_t* b, int n, float fSize ){
-       bool bTiny = false;
-       for ( int i = 0 ; i < 3 ; i++ )
-       {
-               if ( b->maxs[i] - b->mins[i] < fSize ) {
-                       bTiny = true;
-               }
-       }
-       if ( bTiny ) {
-               Sys_Printf( "Possible problem brush (too small) #%i ", n );
-       }
-       return bTiny;
+NamespaceAPI(){
+       m_namespace = &g_defaultNamespace;
 }
+Namespace* getTable(){
+       return m_namespace;
+}
+};
 
-void Map_BuildBrushData( void ){
-       brush_t *b, *next;
+typedef SingletonModule<NamespaceAPI> NamespaceModule;
+typedef Static<NamespaceModule> StaticNamespaceModule;
+StaticRegisterModule staticRegisterDefaultNamespace( StaticNamespaceModule::instance() );
 
-       if ( active_brushes.next == NULL ) {
-               return;
-       }
 
-       Sys_BeginWait(); // this could take a while
+std::list<Namespaced*> g_cloned;
 
-       int n = 0;
-       for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b = next )
-       {
-               next = b->next;
-               Brush_Build( b, true, false, false );
-               if ( !b->brush_faces || ( g_PrefsDlg.m_bCleanTiny && CheckForTinyBrush( b, n++, g_PrefsDlg.m_fTinySize ) ) ) {
-                       Brush_Free( b );
-                       Sys_Printf( "Removed degenerate brush\n" );
-               }
-       }
-       Sys_EndWait();
+inline Namespaced* Node_getNamespaced( scene::Node& node ){
+       return NodeTypeCast<Namespaced>::cast( node );
 }
 
-entity_t *Map_FindClass( const char *cname ){
-       entity_t    *ent;
-
-       for ( ent = entities.next ; ent != &entities ; ent = ent->next )
-       {
-               if ( !strcmp( cname, ValueForKey( ent, "classname" ) ) ) {
-                       return ent;
-               }
+void Node_gatherNamespaced( scene::Node& node ){
+       Namespaced* namespaced = Node_getNamespaced( node );
+       if ( namespaced != 0 ) {
+               g_cloned.push_back( namespaced );
        }
-       return NULL;
 }
 
-/*
-   ================
-   Map_Free
-   free all map elements, reinitialize the structures that depend on them
-   ================
- */
-void Map_Free( void ){
-       g_bRestoreBetween = false;
-       if ( selected_brushes.next &&
-                ( selected_brushes.next != &selected_brushes ) ) {
-               if ( gtk_MessageBox( g_pParentWnd->m_pWidget, "Copy selection?", " ", MB_YESNO ) == IDYES ) {
-                       Map_SaveBetween();
-               }
-       }
+class GatherNamespaced : public scene::Traversable::Walker
+{
+public:
+bool pre( scene::Node& node ) const {
+       Node_gatherNamespaced( node );
+       return true;
+}
+};
 
-       QERApp_ActiveShaders_SetInUse( false );
-       Pointfile_Clear();
-       g_qeglobals.d_num_entities = 0;
+void Map_gatherNamespaced( scene::Node& root ){
+       Node_traverseSubgraph( root, GatherNamespaced() );
+}
 
-       if ( !active_brushes.next ) {
-               // first map
-               active_brushes.prev = active_brushes.next = &active_brushes;
-               selected_brushes.prev = selected_brushes.next = &selected_brushes;
-               filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;
-               entities.prev = entities.next = &entities;
+void Map_mergeClonedNames(){
+       for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
+       {
+               ( *i )->setNamespace( g_cloneNamespace );
        }
-       else
+       g_cloneNamespace.mergeNames( g_defaultNamespace );
+       for ( std::list<Namespaced*>::const_iterator i = g_cloned.begin(); i != g_cloned.end(); ++i )
        {
-               // free selected faces array
-               g_ptrSelectedFaces.RemoveAll();
-               g_ptrSelectedFaceBrushes.RemoveAll();
-               while ( active_brushes.next != &active_brushes )
-                       Brush_Free( active_brushes.next );
-               while ( selected_brushes.next != &selected_brushes )
-                       Brush_Free( selected_brushes.next );
-               while ( filtered_brushes.next != &filtered_brushes )
-                       Brush_Free( filtered_brushes.next );
-               while ( entities.next != &entities )
-                       Entity_Free( entities.next );
+               ( *i )->setNamespace( g_defaultNamespace );
        }
 
-       if ( world_entity ) {
-               Entity_Free( world_entity );
-       }
-       world_entity = NULL;
+       g_cloned.clear();
 }
 
-entity_t *AngledEntity(){
-       entity_t *ent = Map_FindClass( "info_player_start" );
-       if ( !ent ) {
-               ent = Map_FindClass( "info_player_deathmatch" );
-       }
-       if ( !ent ) {
-               ent = Map_FindClass( "info_player_deathmatch" );
-       }
-       if ( !ent ) {
-               ent = Map_FindClass( "team_CTF_redplayer" );
-       }
-       if ( !ent ) {
-               ent = Map_FindClass( "team_CTF_blueplayer" );
-       }
-       if ( !ent ) {
-               ent = Map_FindClass( "team_CTF_redspawn" );
-       }
-       if ( !ent ) {
-               ent = Map_FindClass( "team_CTF_bluespawn" );
-       }
-       return ent;
+class WorldNode
+{
+scene::Node* m_node;
+public:
+WorldNode()
+       : m_node( 0 ){
 }
-
-//
-// move the view to a start position
-//
-void Map_StartPosition(){
-       entity_t *ent = AngledEntity();
-
-       g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;
-       if ( ent ) {
-               GetVectorForKey( ent, "origin", g_pParentWnd->GetCamWnd()->Camera()->origin );
-               GetVectorForKey( ent, "origin", g_pParentWnd->GetXYWnd()->GetOrigin() );
-               g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = FloatForKey( ent, "angle" );
+void set( scene::Node* node ){
+       if ( m_node != 0 ) {
+               m_node->DecRef();
        }
-       else
-       {
-               g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;
-               VectorCopy( vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin );
-               VectorCopy( vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin() );
+       m_node = node;
+       if ( m_node != 0 ) {
+               m_node->IncRef();
        }
 }
+scene::Node* get() const {
+       return m_node;
+}
+};
 
-void Map_FreeEntities( CPtrArray *ents ){
-       int i, j, num_ents, num_brushes;
-       entity_t* e;
-       CPtrArray* brushes;
-
-       num_ents = ents->GetSize();
-       for ( i = 0; i < num_ents; i++ )
-       {
-               e = (entity_t*)ents->GetAt( i );
-               brushes = (CPtrArray*)e->pData;
-               num_brushes = brushes->GetSize();
-               for ( j = 0; j < num_brushes; j++ )
-                       Brush_Free( (brush_t*)brushes->GetAt( j ) );
-               brushes->RemoveAll();
-               delete (CPtrArray*)e->pData;
-               e->pData = NULL;
-               Entity_Free( e );
-       }
-       ents->RemoveAll();
-}
-
-/*!\todo Possibly make the import Undo-friendly by calling Undo_End for new brushes and ents */
-void Map_ImportEntities( CPtrArray *ents, bool bAddSelected = false ){
-       int num_ents, num_brushes;
-       CPtrArray *brushes;
-       vec3_t mins, maxs;
-       entity_t *e;
-       brush_t *b;
-       face_t *f;
-       int i,j;
-
-       GPtrArray *new_ents = g_ptr_array_new();
-
-       g_qeglobals.bPrimitBrushes = false;
-
-       brush_t *pBrushList = ( bAddSelected ) ? &selected_brushes : &active_brushes;
-
-       bool bDoneBPCheck = false;
-       g_qeglobals.bNeedConvert = false;
-       // HACK: find out if this map file was a BP one
-       // check the first brush in the file that is NOT a patch
-       // this will not be necessary when we allow both formats in the same file
-       num_ents = ents->GetSize();
-       for ( i = 0; !bDoneBPCheck && i < num_ents; i++ )
-       {
-               e = (entity_t*)ents->GetAt( i );
-               brushes = (CPtrArray*)e->pData;
-               num_brushes = brushes->GetSize();
-               for ( j = 0; !bDoneBPCheck && j < num_brushes; j++ )
-               {
-                       /*!todo Allow mixing texdef formats per-face. */
-                       b = (brush_t *)brushes->GetAt( j );
-                       if ( b->patchBrush ) {
-                               continue;
-                       }
-                       bDoneBPCheck = true;
-                       int BP_param = -1;
-                       if ( b->bBrushDef && !g_qeglobals.m_bBrushPrimitMode ) {
-                               BP_param = 0;
-                       }
-                       else if ( !b->bBrushDef && g_qeglobals.m_bBrushPrimitMode ) {
-                               BP_param = 1;
-                       }
-
-                       if ( BP_param != -1 ) {
-                               switch ( BP_MessageBox( BP_param ) )
-                               {
-                               case 0:
-                                       Map_FreeEntities( ents );
-                                       return;
-                               case 1:
-                                       g_qeglobals.bNeedConvert = true;
-                                       break;
-                               case 2:
-                                       g_qeglobals.bNeedConvert = false;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       // process the entities into the world geometry
-       num_ents = ents->GetSize();
-       for ( i = 0; i < num_ents; i++ )
-       {
-               num_brushes = 0;
-               e = (entity_t*)ents->GetAt( i );
-               brushes = (CPtrArray*)e->pData;
-
-               num_brushes = brushes->GetSize();
-               // link brushes into entity
-               for ( j = 0; j < num_brushes; j++ )
-               {
-                       Entity_LinkBrush( e, (brush_t *)brushes->GetAt( j ) );
-                       g_qeglobals.d_parsed_brushes++;
-               }
-               brushes->RemoveAll();
-               delete brushes;
-               e->pData = NULL;
-
-               // set entity origin
-               GetVectorForKey( e, "origin", e->origin );
-               // set entity eclass
-               /*!\todo Make SetKeyValue check for "classname" change and assign appropriate eclass */
-               e->eclass = Eclass_ForName( ValueForKey( e, "classname" ),
-                                                                       ( e->brushes.onext != &e->brushes ) );
-
-               // go through all parsed brushes and build stuff
-               for ( b = e->brushes.onext; b != &e->brushes; b = b->onext )
-               {
-                       for ( f = b->brush_faces; f != NULL; f = f->next )
-                       {
-                               f->pShader = QERApp_Shader_ForName( f->texdef.GetName() );
-                               f->d_texture = f->pShader->getTexture();
-                       }
-
-                       // when brushes are in final state, build the planes and windings
-                       // NOTE: also converts BP brushes if g_qeglobals.bNeedConvert is true
-                       Brush_Build( b );
-               }
-
-//#define TERRAIN_HACK
-#undef TERRAIN_HACK
-
-#ifdef TERRAIN_HACK
-               if ( ( strcmp( ValueForKey( e, "terrain" ),"1" ) == 0 && strcmp( e->eclass->name,"func_group" ) == 0 ) ) {
+class Map;
+void Map_SetValid( Map& map, bool valid );
+void Map_UpdateTitle( const Map& map );
+void Map_SetWorldspawn( Map& map, scene::Node* node );
 
-                       // two aux pointers to the shaders used in the terrain entity
-                       // we don't keep refcount on them since they are only temporary
-                       // this avoids doing expensive lookups by name for all faces
-                       IShader *pTerrainShader, *pCaulk;
 
-                       pTerrainShader = NULL;
-                       pCaulk = QERApp_Shader_ForName( SHADER_CAULK );
+class Map : public ModuleObserver
+{
+public:
+CopiedString m_name;
+Resource* m_resource;
+bool m_valid;
 
-                       for ( b = e->brushes.onext; b != &e->brushes; b = b->onext )
-                       {
-                               if ( pTerrainShader == NULL ) {
-                                       for ( f = b->brush_faces; f != NULL; f = f->next )
-                                               if ( strcmp( f->texdef.GetName(), SHADER_CAULK ) != 0 ) {
-                                                       pTerrainShader = f->pShader;
-                                               }
-                               }
+bool m_modified;
+void ( *m_modified_changed )( const Map& );
 
-                               if ( pTerrainShader ) {
-                                       for ( f = b->brush_faces; f != NULL; f = f->next )
-                                       {
-                                               if ( strcmp( f->texdef.GetName(), SHADER_CAULK ) != 0 ) { // not caulk
-                                                       Face_SetShader( f, pTerrainShader->getName() );
-                                               }
-                                               else{
-                                                       Face_SetShader( f, pCaulk->getName() );
-                                               }
-                                       }
-                               }
-                               else{
-                                       Sys_Printf( "WARNING: no terrain shader found for brush\n" );
-                               }
-                       }
-               }
-#endif
+Signal0 m_mapValidCallbacks;
 
-#define PATCH_HACK
-#ifdef PATCH_HACK
-               for ( b = e->brushes.onext; b != &e->brushes; b = b->onext )
-               {
-                       // patch hack, to be removed when dependency on brush_faces is removed
-                       if ( b->patchBrush ) {
-                               Patch_CalcBounds( b->pPatch, mins, maxs );
-                               for ( int i = 0; i < 3; i++ )
-                               {
-                                       if ( (int)mins[i] == (int)maxs[i] ) {
-                                               mins[i] -= 4;
-                                               maxs[i] += 4;
-                                       }
-                               }
-                               Brush_Resize( b, mins, maxs );
-                               Brush_Build( b );
-                       }
-               }
-#endif
-               // add brush for fixedsize entity
-               if ( e->eclass->fixedsize ) {
-                       vec3_t mins, maxs;
-                       VectorAdd( e->eclass->mins, e->origin, mins );
-                       VectorAdd( e->eclass->maxs, e->origin, maxs );
-                       b = Brush_Create( mins, maxs, &e->eclass->texdef );
-                       Entity_LinkBrush( e, b );
-                       Brush_Build( b );
-               }
+WorldNode m_world_node;   // "classname" "worldspawn" !
 
-               for ( b = e->brushes.onext; b != &e->brushes; b = b->onext )
-                       Brush_AddToList( b, pBrushList );
+Map() : m_resource( 0 ), m_valid( false ), m_modified_changed( Map_UpdateTitle ){
+}
 
-               if ( strcmp( e->eclass->name, "worldspawn" ) == 0 ) {
-                       if ( world_entity ) {
-                               while ( e->brushes.onext != &e->brushes )
-                               {
-                                       b = e->brushes.onext;
-                                       Entity_UnlinkBrush( b );
-                                       Entity_LinkBrush( world_entity, b );
-                               }
-                               Entity_Free( e );
-                       }
-                       else
-                       {
-                               world_entity = e;
+void realise(){
+       if ( m_resource != 0 ) {
+               if ( Map_Unnamed( *this ) ) {
+                       g_map.m_resource->setNode( NewMapRoot( "" ).get_pointer() );
+                       MapFile* map = Node_getMapFile( *g_map.m_resource->getNode() );
+                       if ( map != 0 ) {
+                               map->save();
                        }
                }
-               else if ( strcmp( e->eclass->name, "group_info" ) == 0 ) {
-                       // it's a group thing!
-                       Group_Add( e );
-                       Entity_Free( e );
-               }
                else
                {
-                       // fix target/targetname collisions
-                       if ( ( g_PrefsDlg.m_bDoTargetFix ) && ( strcmp( ValueForKey( e, "target" ), "" ) != 0 ) ) {
-                               GPtrArray *t_ents = g_ptr_array_new();
-                               entity_t *e_target;
-                               const char *target = ValueForKey( e, "target" );
-                               qboolean bCollision = FALSE;
-
-                               // check the current map entities for an actual collision
-                               for ( e_target = entities.next; e_target != &entities; e_target = e_target->next )
-                               {
-                                       if ( !strcmp( target, ValueForKey( e_target, "target" ) ) ) {
-                                               bCollision = TRUE;
-                                               // make sure the collision is not between two imported entities
-                                               for ( j = 0; j < (int)new_ents->len; j++ )
-                                               {
-                                                       if ( e_target == g_ptr_array_index( new_ents, j ) ) {
-                                                               bCollision = FALSE;
-                                                       }
-                                               }
-                                       }
-                               }
+                       m_resource->load();
+               }
 
-                               // find the matching targeted entity(s)
-                               if ( bCollision ) {
-                                       for ( j = num_ents - 1; j > 0; j-- )
-                                       {
-                                               e_target = (entity_t*)ents->GetAt( j );
-                                               if ( e_target != NULL && e_target != e ) {
-                                                       const char *targetname = ValueForKey( e_target, "targetname" );
-                                                       if ( ( targetname != NULL ) && ( strcmp( target, targetname ) == 0 ) ) {
-                                                               g_ptr_array_add( t_ents, (gpointer)e_target );
-                                                       }
-                                               }
-                                       }
-                                       if ( t_ents->len > 0 ) {
-                                               // link the first to get a unique target/targetname
-                                               Entity_Connect( e, (entity_t*)g_ptr_array_index( t_ents,0 ) );
-                                               // set the targetname of the rest of them manually
-                                               for ( j = 1; j < (int)t_ents->len; j++ )
-                                                       SetKeyValue( (entity_t*)g_ptr_array_index( t_ents, j ), "targetname", ValueForKey( e, "target" ) );
-                                       }
-                                       g_ptr_array_free( t_ents, FALSE );
-                               }
-                       }
+               GlobalSceneGraph().insert_root( *m_resource->getNode() );
 
-                       // add the entity to the end of the entity list
-                       Entity_AddToList( e, &entities );
-                       g_qeglobals.d_num_entities++;
+               AutoSave_clear();
 
-                       // keep a list of ents added to avoid testing collisions against them
-                       g_ptr_array_add( new_ents, (gpointer)e );
-               }
+               Map_SetValid( g_map, true );
        }
-       g_ptr_array_free( new_ents, FALSE );
+}
+void unrealise(){
+       if ( m_resource != 0 ) {
+               Map_SetValid( g_map, false );
+               Map_SetWorldspawn( g_map, 0 );
+
 
-       ents->RemoveAll();
+               GlobalUndoSystem().clear();
 
-       g_qeglobals.bNeedConvert = false;
+               GlobalSceneGraph().erase_root();
+       }
 }
+};
 
-void Map_Import( IDataStream *in, const char *type, bool bAddSelected ){
-       CPtrArray ents;
+Map g_map;
+Map* g_currentMap = 0;
 
-       g_pParentWnd->GetSynapseClient().ImportMap( in, &ents, type );
-       Map_ImportEntities( &ents, bAddSelected );
+void Map_addValidCallback( Map& map, const SignalHandler& handler ){
+       map.m_mapValidCallbacks.connectLast( handler );
 }
 
-/*
-   ================
-   Map_LoadFile
-   ================
- */
-void Map_LoadFile( const char *filename ){
-       clock_t start, finish;
-       double elapsed_time;
-       start = clock();
+bool Map_Valid( const Map& map ){
+       return map.m_valid;
+}
 
-       Sys_BeginWait();
-       Select_Deselect();
-       /*!
-          \todo FIXME TTimo why is this commented out?
-          stability issues maybe? or duplicate feature?
-          forcing to show the console during map load was a good thing IMO
-        */
-       //SetInspectorMode(W_CONSOLE);
-       Sys_Printf( "Loading map from %s\n", filename );
+void Map_SetValid( Map& map, bool valid ){
+       map.m_valid = valid;
+       map.m_mapValidCallbacks();
+}
 
-       Map_Free();
-       //++timo FIXME: maybe even easier to have Group_Init called from Map_Free?
-       Group_Init();
-       g_qeglobals.d_num_entities = 0;
-       g_qeglobals.d_parsed_brushes = 0;
 
+const char* Map_Name( const Map& map ){
+       return map.m_name.c_str();
+}
 
-       // cancel the map loading process
-       // used when conversion between standard map format and BP format is required and the user cancels the process
-       g_bCancel_Map_LoadFile = false;
+bool Map_Unnamed( const Map& map ){
+       return string_equal( Map_Name( map ), "unnamed.map" );
+}
 
-       strcpy( currentmap, filename );
+inline const MapFormat& MapFormat_forFile( const char* filename ){
+       const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), path_get_extension( filename ) );
+       MapFormat* format = Radiant_getMapModules().findModule( moduleName );
+       ASSERT_MESSAGE( format != 0, "map format not found for file " << makeQuoted( filename ) );
+       return *format;
+}
 
-       g_bScreenUpdates = false; // leo: avoid redraws while loading the map (see fenris:1952)
+const MapFormat& Map_getFormat( const Map& map ){
+       return MapFormat_forFile( Map_Name( map ) );
+}
 
-       // prepare to let the map module do the parsing
-       FileStream file;
-       const char* type = strrchr( filename,'.' );
-       if ( type != NULL ) {
-               type++;
-       }
-       // NOTE TTimo opening has binary doesn't make a lot of sense
-       // but opening as text confuses the scriptlib parser
-       // this may be a problem if we "rb" and use the XML parser, might have an incompatibility
-       if ( file.Open( filename, "rb" ) ) {
-               Map_Import( &file, type );
-       }
-       else{
-               Sys_FPrintf( SYS_ERR, "ERROR: failed to open %s for read\n", filename );
-       }
-       file.Close();
 
-       g_bScreenUpdates = true;
+bool Map_Modified( const Map& map ){
+       return map.m_modified;
+}
 
-       if ( g_bCancel_Map_LoadFile ) {
-               Sys_Printf( "Map_LoadFile canceled\n" );
-               Map_New();
-               Sys_EndWait();
-               return;
-       }
+void Map_SetModified( Map& map, bool modified ){
+       if ( map.m_modified ^ modified ) {
+               map.m_modified = modified;
 
-       if ( !world_entity ) {
-               Sys_Printf( "No worldspawn in map.\n" );
-               Map_New();
-               Sys_EndWait();
-               return;
+               map.m_modified_changed( map );
        }
-       finish = clock();
-       elapsed_time = (double)( finish - start ) / CLOCKS_PER_SEC;
+}
 
-       Sys_Printf( "--- LoadMapFile ---\n" );
-       Sys_Printf( "%s\n", filename );
+void Map_UpdateTitle( const Map& map ){
+       Sys_SetTitle( map.m_name.c_str(), Map_Modified( map ) );
+}
 
-       Sys_Printf( "%5i brushes\n",  g_qeglobals.d_parsed_brushes );
-       Sys_Printf( "%5i entities\n", g_qeglobals.d_num_entities );
-       Sys_Printf( "%5.2f second(s) load time\n", elapsed_time );
 
-       Sys_EndWait();
 
-       Map_RestoreBetween();
+scene::Node* Map_GetWorldspawn( const Map& map ){
+       return map.m_world_node.get();
+}
 
-       //
-       // move the view to a start position
-       //
-       Map_StartPosition();
+void Map_SetWorldspawn( Map& map, scene::Node* node ){
+       map.m_world_node.set( node );
+}
 
-       Map_RegionOff();
 
-       modified = false;
-       Sys_SetTitle( filename );
+// TTimo
+// need that in a variable, will have to tweak depending on the game
+float g_MaxWorldCoord = 64 * 1024;
+float g_MinWorldCoord = -64 * 1024;
 
-       Texture_ShowInuse();
-       QERApp_SortActiveShaders();
+void AddRegionBrushes( void );
+void RemoveRegionBrushes( void );
 
-       Sys_UpdateWindows( W_ALL );
-}
 
-/*!
-   ===========
-   Supporting functions for Map_SaveFile, builds a CPtrArray with the filtered / non filtered brushes
-   ===========
- */
-void CleanFilter( entity_t *ent ){
-       if ( ent->pData ) {
-               delete static_cast<CPtrArray*>( ent->pData );
-               ent->pData = NULL;
-       }
-}
 
-/*!
-   filters out the region brushes if necessary
-   returns true if this entity as a whole is out of the region
-   (if all brushes are filtered out, then the entity will be completely dropped .. except if it's worldspawn of course)
+/*
+   ================
+   Map_Free
+   free all map elements, reinitialize the structures that depend on them
+   ================
  */
-bool FilterChildren( entity_t *ent, bool bRegionOnly = false, bool bSelectedOnly = false ){
-       if ( ent->brushes.onext == &ent->brushes ) {
-               return false;
-       }
-       // entity without a brush, ignore it... this can be caused by Undo
+void Map_Free(){
+       Pointfile_Clear();
 
-       // filter fixedsize ents by their eclass bounding box
-       // don't add their brushes
-       if ( ent->eclass->fixedsize ) {
-               if ( bSelectedOnly && !IsBrushSelected( ent->brushes.onext ) ) {
-                       return false;
-               }
+       g_map.m_resource->detach( g_map );
+       GlobalReferenceCache().release( g_map.m_name.c_str() );
+       g_map.m_resource = 0;
 
-               if ( bRegionOnly && region_active ) {
-                       for ( int i = 0 ; i < 3 ; i++ )
-                       {
-                               if ( ( ent->origin[i] + ent->eclass->mins[i] ) > region_maxs[i] ) {
-                                       return false;
-                               }
-                               if ( ( ent->origin[i] + ent->eclass->maxs[i] ) < region_mins[i] ) {
-                                       return false;
-                               }
-                       }
-               }
-       }
-       else
-       {
-               for ( brush_t *b = ent->brushes.onext ; b != &ent->brushes ; b = b->onext )
-               {
-                       // set flag to use brushprimit_texdef
-                       if ( g_qeglobals.m_bBrushPrimitMode ) {
-                               b->bBrushDef = true;
-                       }
-                       else{
-                               b->bBrushDef = false;
-                       }
+       FlushReferences();
 
-                       // add brush, unless it's excluded by region
-                       if ( !( bRegionOnly && Map_IsBrushFiltered( b ) ) &&
-                                !( bSelectedOnly && !IsBrushSelected( b ) ) ) {
-                               ( (CPtrArray*)ent->pData )->Add( b );
-                       }
-               }
+       g_currentMap = 0;
+       Brush_unlatchPreferences();
+}
 
-               if ( ( (CPtrArray*)ent->pData )->GetSize() <= 0 ) {
-                       return false;
+class EntityFindByClassname : public scene::Graph::Walker
+{
+const char* m_name;
+Entity*& m_entity;
+public:
+EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
+       m_entity = 0;
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if ( m_entity == 0 ) {
+               Entity* entity = Node_getEntity( path.top() );
+               if ( entity != 0
+                        && string_equal( m_name, entity->getKeyValue( "classname" ) ) ) {
+                       m_entity = entity;
                }
        }
        return true;
 }
+};
 
-entity_t *region_startpoint = NULL;
-void Map_ExportEntities( CPtrArray* ents, bool bRegionOnly = false, bool bSelectedOnly = false ){
-       int i;
-       entity_t *e;
-
-       /*!
-          \todo the entity_t needs to be reworked and asbtracted some more
-
-          keeping the entity_t as the struct providing access to a list of map objects, a list of epairs and various other info?
-          but separating some more the data that belongs to the entity_t and the 'sons' data
-          on a side note, I don't think that doing that with linked list would be a good thing
-
-          for now, we use the blind void* in entity_t casted to a CPtrArray of brush_t* to hand out a list of the brushes for map write
-          the next step is very likely to be a change of the brush_t* to a more abstract object?
-        */
-
-       FilterChildren( world_entity, bRegionOnly, bSelectedOnly );
-       ents->Add( world_entity );
+Entity* Scene_FindEntityByClass( const char* name ){
+       Entity* entity;
+       GlobalSceneGraph().traverse( EntityFindByClassname( name, entity ) );
+       return entity;
+}
 
-       for ( e = entities.next ; e != &entities ; e = e->next )
+Entity *Scene_FindPlayerStart(){
+       typedef const char* StaticString;
+       StaticString strings[] = {
+               "info_player_start",
+               "info_player_deathmatch",
+               "team_CTF_redplayer",
+               "team_CTF_blueplayer",
+               "team_CTF_redspawn",
+               "team_CTF_bluespawn",
+       };
+       typedef const StaticString* StaticStringIterator;
+       for ( StaticStringIterator i = strings, end = strings + ( sizeof( strings ) / sizeof( StaticString ) ); i != end; ++i )
        {
-               // not sure this still happens, probably safe to leave it in
-               if ( ( !strcmp( ValueForKey( e, "classname" ), "worldspawn" ) ) && ( e != world_entity ) ) {
-                       Sys_FPrintf( SYS_ERR, "Dropping parasite worldspawn entity\n" );
-                       continue;
-               }
-
-               // entities which brushes are completely filtered out by regioning are not printed to the map
-               if ( FilterChildren( e, bRegionOnly, bSelectedOnly ) ) {
-                       ents->Add( e );
+               Entity* entity = Scene_FindEntityByClass( *i );
+               if ( entity != 0 ) {
+                       return entity;
                }
        }
-
-       if ( bRegionOnly && region_active ) {
-               for ( i = 0; i < 6; i++ )
-                       ( (CPtrArray*)world_entity->pData )->Add( region_sides[i] );
-
-               ents->Add( region_startpoint );
-       }
+       return 0;
 }
 
-void Map_Export( IDataStream *out, const char *type, bool bRegionOnly, bool bSelectedOnly ){
-       entity_t  *e;
-
-       CPtrArray ents;
+//
+// move the view to a start position
+//
 
-       if ( bRegionOnly && region_active ) {
-               AddRegionBrushes();
-       }
 
-       // create the filters
-       world_entity->pData = new CPtrArray();
-       for ( e = entities.next; e != &entities; e = e->next )
-               e->pData = new CPtrArray();
+void FocusViews( const Vector3& point, float angle ){
+       CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
+       Camera_setOrigin( camwnd, point );
+       Vector3 angles( Camera_getAngles( camwnd ) );
+       angles[CAMERA_PITCH] = 0;
+       angles[CAMERA_YAW] = angle;
+       Camera_setAngles( camwnd, angles );
 
-       Map_ExportEntities( &ents, bRegionOnly, bSelectedOnly );
+       XYWnd* xywnd = g_pParentWnd->GetXYWnd();
+       xywnd->SetOrigin( point );
+}
 
-       g_pParentWnd->GetSynapseClient().ExportMap( &ents, out, type );
+#include "stringio.h"
 
-       // cleanup the filters
-       CleanFilter( world_entity );
-       for ( e = entities.next ; e != &entities ; e = e->next )
-               CleanFilter( e );
+void Map_StartPosition(){
+       Entity* entity = Scene_FindPlayerStart();
 
-       if ( bRegionOnly && region_active ) {
-               RemoveRegionBrushes();
+       if ( entity ) {
+               Vector3 origin;
+               string_parse_vector3( entity->getKeyValue( "origin" ), origin );
+               FocusViews( origin, string_read_float( entity->getKeyValue( "angle" ) ) );
        }
-}
-
-const char* filename_get_extension( const char* filename ){
-       const char* type = strrchr( filename,'.' );
-       if ( type != NULL ) {
-               return ++type;
+       else
+       {
+               FocusViews( g_vector3_identity, 0 );
        }
-       return "";
 }
 
-/*
-   ===========
-   Map_SaveFile
-   \todo FIXME remove the use_region, this is broken .. work with a global flag to set region mode or not
-   ===========
- */
-void Map_SaveFile( const char *filename, qboolean use_region ){
-       clock_t start, finish;
-       double elapsed_time;
-       start = clock();
-       Sys_Printf( "Saving map to %s\n",filename );
 
-       Pointfile_Clear();
+inline bool node_is_worldspawn( scene::Node& node ){
+       Entity* entity = Node_getEntity( node );
+       return entity != 0 && string_equal( entity->getKeyValue( "classname" ), "worldspawn" );
+}
 
-       if ( !use_region ) {
-               char backup[1024];
 
-               // rename current to .bak
-               strcpy( backup, filename );
-               StripExtension( backup );
-               strcat( backup, ".bak" );
-               unlink( backup );
-               rename( filename, backup );
+// use first worldspawn
+class entity_updateworldspawn : public scene::Traversable::Walker
+{
+public:
+bool pre( scene::Node& node ) const {
+       if ( node_is_worldspawn( node ) ) {
+               if ( Map_GetWorldspawn( g_map ) == 0 ) {
+                       Map_SetWorldspawn( g_map, &node );
+               }
        }
+       return false;
+}
+};
 
-       Sys_Printf( "Map_SaveFile: %s\n", filename );
+scene::Node* Map_FindWorldspawn( Map& map ){
+       Map_SetWorldspawn( map, 0 );
 
-       // build the out data stream
-       FileStream file;
-       if ( !file.Open( filename,"w" ) ) {
-               Sys_FPrintf( SYS_ERR, "ERROR: couldn't open %s for write\n", filename );
-               return;
-       }
+       Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
 
-       // extract filetype
-       Map_Export( &file, filename_get_extension( filename ), use_region );
+       return Map_GetWorldspawn( map );
+}
 
-       file.Close();
 
-       finish = clock();
-       elapsed_time = (double)( finish - start ) / CLOCKS_PER_SEC;
+class CollectAllWalker : public scene::Traversable::Walker
+{
+scene::Node& m_root;
+UnsortedNodeSet& m_nodes;
+public:
+CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
+}
+bool pre( scene::Node& node ) const {
+       m_nodes.insert( NodeSmartReference( node ) );
+       Node_getTraversable( m_root )->erase( node );
+       return false;
+}
+};
 
-       Sys_Printf( "Saved in %-.2f second(s).\n",elapsed_time );
-       modified = false;
+void Node_insertChildFirst( scene::Node& parent, scene::Node& child ){
+       UnsortedNodeSet nodes;
+       Node_getTraversable( parent )->traverse( CollectAllWalker( parent, nodes ) );
+       Node_getTraversable( parent )->insert( child );
 
-       if ( !strstr( filename, "autosave" ) ) {
-               Sys_SetTitle( filename );
+       for ( UnsortedNodeSet::iterator i = nodes.begin(); i != nodes.end(); ++i )
+       {
+               Node_getTraversable( parent )->insert( ( *i ) );
        }
+}
 
-       if ( !use_region ) {
-               time_t timer;
-
-               time( &timer );
-
-               Sys_Beep();
+scene::Node& createWorldspawn(){
+       NodeSmartReference worldspawn( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "worldspawn", true ) ) );
+       Node_insertChildFirst( GlobalSceneGraph().root(), worldspawn );
+       return worldspawn;
+}
 
-               Sys_Status( "Saved.", 0 );
+void Map_UpdateWorldspawn( Map& map ){
+       if ( Map_FindWorldspawn( map ) == 0 ) {
+               Map_SetWorldspawn( map, &createWorldspawn() );
        }
 }
 
-/*
-   ===========
-   Map_New
+scene::Node& Map_FindOrInsertWorldspawn( Map& map ){
+       Map_UpdateWorldspawn( map );
+       return *Map_GetWorldspawn( map );
+}
 
+
+class MapMergeAll : public scene::Traversable::Walker
+{
+mutable scene::Path m_path;
+public:
+MapMergeAll( const scene::Path& root )
+       : m_path( root ){
+}
+bool pre( scene::Node& node ) const {
+       Node_getTraversable( m_path.top() )->insert( node );
+       m_path.push( makeReference( node ) );
+       selectPath( m_path, true );
+       return false;
+}
+void post( scene::Node& node ) const {
+       m_path.pop();
+}
+};
+
+class MapMergeEntities : public scene::Traversable::Walker
+{
+mutable scene::Path m_path;
+public:
+MapMergeEntities( const scene::Path& root )
+       : m_path( root ){
+}
+bool pre( scene::Node& node ) const {
+       if ( node_is_worldspawn( node ) ) {
+               scene::Node* world_node = Map_FindWorldspawn( g_map );
+               if ( world_node == 0 ) {
+                       Map_SetWorldspawn( g_map, &node );
+                       Node_getTraversable( m_path.top().get() )->insert( node );
+                       m_path.push( makeReference( node ) );
+                       Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
+               }
+               else
+               {
+                       m_path.push( makeReference( *world_node ) );
+                       Node_getTraversable( node )->traverse( MapMergeAll( m_path ) );
+               }
+       }
+       else
+       {
+               Node_getTraversable( m_path.top() )->insert( node );
+               m_path.push( makeReference( node ) );
+               if ( node_is_group( node ) ) {
+                       Node_getTraversable( node )->traverse( SelectChildren( m_path ) );
+               }
+               else
+               {
+                       selectPath( m_path, true );
+               }
+       }
+       return false;
+}
+void post( scene::Node& node ) const {
+       m_path.pop();
+}
+};
+
+class BasicContainer : public scene::Node::Symbiot
+{
+class TypeCasts
+{
+NodeTypeCastTable m_casts;
+public:
+TypeCasts(){
+       NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
+}
+NodeTypeCastTable& get(){
+       return m_casts;
+}
+};
+
+scene::Node m_node;
+TraversableNodeSet m_traverse;
+public:
+
+typedef LazyStatic<TypeCasts> StaticTypeCasts;
+
+scene::Traversable& get( NullType<scene::Traversable>){
+       return m_traverse;
+}
+
+BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
+}
+void release(){
+       delete this;
+}
+scene::Node& node(){
+       return m_node;
+}
+};
+
+/// Merges the map graph rooted at \p node into the global scene-graph.
+void MergeMap( scene::Node& node ){
+       Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
+}
+void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
+       NodeSmartReference node( ( new BasicContainer )->node() );
+       format.readGraph( node, in, GlobalEntityCreator() );
+       Map_gatherNamespaced( node );
+       Map_mergeClonedNames();
+       MergeMap( node );
+}
+
+inline scene::Cloneable* Node_getCloneable( scene::Node& node ){
+       return NodeTypeCast<scene::Cloneable>::cast( node );
+}
+
+inline scene::Node& node_clone( scene::Node& node ){
+       scene::Cloneable* cloneable = Node_getCloneable( node );
+       if ( cloneable != 0 ) {
+               return cloneable->clone();
+       }
+
+       return ( new scene::NullNode )->node();
+}
+
+class CloneAll : public scene::Traversable::Walker
+{
+mutable scene::Path m_path;
+public:
+CloneAll( scene::Node& root )
+       : m_path( makeReference( root ) ){
+}
+bool pre( scene::Node& node ) const {
+       if ( node.isRoot() ) {
+               return false;
+       }
+
+       m_path.push( makeReference( node_clone( node ) ) );
+       m_path.top().get().IncRef();
+
+       return true;
+}
+void post( scene::Node& node ) const {
+       if ( node.isRoot() ) {
+               return;
+       }
+
+       Node_getTraversable( m_path.parent() )->insert( m_path.top() );
+
+       m_path.top().get().DecRef();
+       m_path.pop();
+}
+};
+
+scene::Node& Node_Clone( scene::Node& node ){
+       scene::Node& clone = node_clone( node );
+       scene::Traversable* traversable = Node_getTraversable( node );
+       if ( traversable != 0 ) {
+               traversable->traverse( CloneAll( clone ) );
+       }
+       return clone;
+}
+
+
+typedef std::map<CopiedString, std::size_t> EntityBreakdown;
+
+class EntityBreakdownWalker : public scene::Graph::Walker
+{
+EntityBreakdown& m_entitymap;
+public:
+EntityBreakdownWalker( EntityBreakdown& entitymap )
+       : m_entitymap( entitymap ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       Entity* entity = Node_getEntity( path.top() );
+       if ( entity != 0 ) {
+               const EntityClass& eclass = entity->getEntityClass();
+               if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
+                       m_entitymap[eclass.name()] = 1;
+               }
+               else{ ++m_entitymap[eclass.name()]; }
+       }
+       return true;
+}
+};
+
+void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
+       GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
+}
+
+
+WindowPosition g_posMapInfoWnd( c_default_window_pos );
+
+void DoMapInfo(){
+       ModalDialog dialog;
+       GtkEntry* brushes_entry;
+       GtkEntry* entities_entry;
+       GtkListStore* EntityBreakdownWalker;
+
+       GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
+
+       window_set_position( window, g_posMapInfoWnd );
+
+       {
+               GtkVBox* vbox = create_dialog_vbox( 4, 4 );
+               gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
+
+               {
+                       GtkHBox* hbox = create_dialog_hbox( 4 );
+                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
+
+                       {
+                               GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
+                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
+
+                               {
+                                       GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
+                                       gtk_widget_show( GTK_WIDGET( entry ) );
+                                       gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
+                                                                         (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+                                                                         (GtkAttachOptions) ( 0 ), 0, 0 );
+                                       gtk_entry_set_editable( entry, FALSE );
+
+                                       brushes_entry = entry;
+                               }
+                               {
+                                       GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
+                                       gtk_widget_show( GTK_WIDGET( entry ) );
+                                       gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
+                                                                         (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+                                                                         (GtkAttachOptions) ( 0 ), 0, 0 );
+                                       gtk_entry_set_editable( entry, FALSE );
+
+                                       entities_entry = entry;
+                               }
+                               {
+                                       GtkWidget* label = gtk_label_new( "Total Brushes" );
+                                       gtk_widget_show( label );
+                                       gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
+                                                                         (GtkAttachOptions) ( GTK_FILL ),
+                                                                         (GtkAttachOptions) ( 0 ), 0, 0 );
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
+                               }
+                               {
+                                       GtkWidget* label = gtk_label_new( "Total Entities" );
+                                       gtk_widget_show( label );
+                                       gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
+                                                                         (GtkAttachOptions) ( GTK_FILL ),
+                                                                         (GtkAttachOptions) ( 0 ), 0, 0 );
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
+                               }
+                       }
+                       {
+                               GtkVBox* vbox2 = create_dialog_vbox( 4 );
+                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
+
+                               {
+                                       GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
+                                       gtk_box_pack_start( GTK_BOX( vbox2 ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
+                               }
+                       }
+               }
+               {
+                       GtkWidget* label = gtk_label_new( "Entity breakdown" );
+                       gtk_widget_show( label );
+                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( label ), FALSE, TRUE, 0 );
+                       gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
+               }
+               {
+                       GtkScrolledWindow* scr = create_scrolled_window( GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, 4 );
+                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( scr ), TRUE, TRUE, 0 );
+
+                       {
+                               GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
+
+                               GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
+                               gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
+
+                               {
+                                       GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+                                       GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Entity", renderer, "text", 0, 0 );
+                                       gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
+                                       gtk_tree_view_column_set_sort_column_id( column, 0 );
+                               }
+
+                               {
+                                       GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
+                                       GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Count", renderer, "text", 1, 0 );
+                                       gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
+                                       gtk_tree_view_column_set_sort_column_id( column, 1 );
+                               }
+
+                               gtk_widget_show( view );
+
+                               gtk_container_add( GTK_CONTAINER( scr ), view );
+
+                               EntityBreakdownWalker = store;
+                       }
+               }
+       }
+
+       // Initialize fields
+
+       {
+               EntityBreakdown entitymap;
+               Scene_EntityBreakdown( entitymap );
+
+               for ( EntityBreakdown::iterator i = entitymap.begin(); i != entitymap.end(); ++i )
+               {
+                       char tmp[16];
+                       sprintf( tmp, "%u", Unsigned( ( *i ).second ) );
+                       GtkTreeIter iter;
+                       gtk_list_store_append( GTK_LIST_STORE( EntityBreakdownWalker ), &iter );
+                       gtk_list_store_set( GTK_LIST_STORE( EntityBreakdownWalker ), &iter, 0, ( *i ).first.c_str(), 1, tmp, -1 );
+               }
+       }
+
+       g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
+
+       char tmp[16];
+       sprintf( tmp, "%u", Unsigned( g_brushCount.get() ) );
+       gtk_entry_set_text( GTK_ENTRY( brushes_entry ), tmp );
+       sprintf( tmp, "%u", Unsigned( g_entityCount.get() ) );
+       gtk_entry_set_text( GTK_ENTRY( entities_entry ), tmp );
+
+       modal_dialog_show( window, dialog );
+
+       // save before exit
+       window_get_position( window, g_posMapInfoWnd );
+
+       gtk_widget_destroy( GTK_WIDGET( window ) );
+}
+
+
+
+class ScopeTimer
+{
+Timer m_timer;
+const char* m_message;
+public:
+ScopeTimer( const char* message )
+       : m_message( message ){
+       m_timer.start();
+}
+~ScopeTimer(){
+       double elapsed_time = m_timer.elapsed_msec() / 1000.f;
+       globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
+}
+};
+
+CopiedString g_strLastFolder = "";
+
+/*
+   ================
+   Map_LoadFile
+   ================
+ */
+
+void Map_LoadFile( const char *filename ){
+       globalOutputStream() << "Loading map from " << filename << "\n";
+       ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
+
+       MRU_AddFile( filename );
+       g_strLastFolder = g_path_get_dirname( filename );
+
+       {
+               ScopeTimer timer( "map load" );
+
+               const MapFormat* format = NULL;
+               const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
+               if ( string_not_empty( moduleName ) ) {
+                       format = ReferenceAPI_getMapModules().findModule( moduleName );
+               }
+
+               for ( int i = 0; i < Brush_toggleFormatCount(); ++i )
+               {
+                       if ( i ) {
+                               Map_Free();
+                       }
+                       Brush_toggleFormat( i );
+                       g_map.m_name = filename;
+                       Map_UpdateTitle( g_map );
+                       g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
+                       if ( format ) {
+                               format->wrongFormat = false;
+                       }
+                       g_map.m_resource->attach( g_map );
+                       if ( format ) {
+                               if ( !format->wrongFormat ) {
+                                       break;
+                               }
+                       }
+               }
+
+               Node_getTraversable( GlobalSceneGraph().root() )->traverse( entity_updateworldspawn() );
+       }
+
+       globalOutputStream() << "--- LoadMapFile ---\n";
+       globalOutputStream() << g_map.m_name.c_str() << "\n";
+
+       globalOutputStream() << Unsigned( g_brushCount.get() ) << " primitive\n";
+       globalOutputStream() << Unsigned( g_entityCount.get() ) << " entities\n";
+
+       //GlobalEntityCreator().printStatistics();
+
+       //
+       // move the view to a start position
+       //
+       Map_StartPosition();
+
+       g_currentMap = &g_map;
+
+       // restart VFS to apply new pak filtering based on mapname
+       // needed for daemon DPK VFS
+       VFS_Restart();
+}
+
+class Excluder
+{
+public:
+virtual bool excluded( scene::Node& node ) const = 0;
+};
+
+class ExcludeWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+const Excluder* m_exclude;
+mutable bool m_skip;
+public:
+ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
+       : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
+}
+bool pre( scene::Node& node ) const {
+       if ( m_exclude->excluded( node ) || node.isRoot() ) {
+               m_skip = true;
+               return false;
+       }
+       else
+       {
+               m_walker.pre( node );
+       }
+       return true;
+}
+void post( scene::Node& node ) const {
+       if ( m_skip ) {
+               m_skip = false;
+       }
+       else
+       {
+               m_walker.post( node );
+       }
+}
+};
+
+class AnyInstanceSelected : public scene::Instantiable::Visitor
+{
+bool& m_selected;
+public:
+AnyInstanceSelected( bool& selected ) : m_selected( selected ){
+       m_selected = false;
+}
+void visit( scene::Instance& instance ) const {
+       Selectable* selectable = Instance_getSelectable( instance );
+       if ( selectable != 0
+                && selectable->isSelected() ) {
+               m_selected = true;
+       }
+}
+};
+
+bool Node_instanceSelected( scene::Node& node ){
+       scene::Instantiable* instantiable = Node_getInstantiable( node );
+       ASSERT_NOTNULL( instantiable );
+       bool selected;
+       instantiable->forEachInstance( AnyInstanceSelected( selected ) );
+       return selected;
+}
+
+class SelectedDescendantWalker : public scene::Traversable::Walker
+{
+bool& m_selected;
+public:
+SelectedDescendantWalker( bool& selected ) : m_selected( selected ){
+       m_selected = false;
+}
+
+bool pre( scene::Node& node ) const {
+       if ( node.isRoot() ) {
+               return false;
+       }
+
+       if ( Node_instanceSelected( node ) ) {
+               m_selected = true;
+       }
+
+       return true;
+}
+};
+
+bool Node_selectedDescendant( scene::Node& node ){
+       bool selected;
+       Node_traverseSubgraph( node, SelectedDescendantWalker( selected ) );
+       return selected;
+}
+
+class SelectionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+       return !Node_selectedDescendant( node );
+}
+};
+
+class IncludeSelectedWalker : public scene::Traversable::Walker
+{
+const scene::Traversable::Walker& m_walker;
+mutable std::size_t m_selected;
+mutable bool m_skip;
+
+bool selectedParent() const {
+       return m_selected != 0;
+}
+public:
+IncludeSelectedWalker( const scene::Traversable::Walker& walker )
+       : m_walker( walker ), m_selected( 0 ), m_skip( false ){
+}
+bool pre( scene::Node& node ) const {
+       // include node if:
+       // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
+       if ( !node.isRoot() && ( Node_selectedDescendant( node ) || selectedParent() ) ) {
+               if ( Node_instanceSelected( node ) ) {
+                       ++m_selected;
+               }
+               m_walker.pre( node );
+               return true;
+       }
+       else
+       {
+               m_skip = true;
+               return false;
+       }
+}
+void post( scene::Node& node ) const {
+       if ( m_skip ) {
+               m_skip = false;
+       }
+       else
+       {
+               if ( Node_instanceSelected( node ) ) {
+                       --m_selected;
+               }
+               m_walker.post( node );
+       }
+}
+};
+
+void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker& walker ){
+       scene::Traversable* traversable = Node_getTraversable( root );
+       if ( traversable != 0 ) {
+#if 0
+               traversable->traverse( ExcludeWalker( walker, SelectionExcluder() ) );
+#else
+               traversable->traverse( IncludeSelectedWalker( walker ) );
+#endif
+       }
+}
+
+void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
+       format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
+}
+
+void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
+       scene::Traversable* traversable = Node_getTraversable( root );
+       if ( traversable != 0 ) {
+               traversable->traverse( walker );
+       }
+}
+
+class RegionExcluder : public Excluder
+{
+public:
+bool excluded( scene::Node& node ) const {
+       return node.excluded();
+}
+};
+
+void Map_Traverse_Region( scene::Node& root, const scene::Traversable::Walker& walker ){
+       scene::Traversable* traversable = Node_getTraversable( root );
+       if ( traversable != 0 ) {
+               traversable->traverse( ExcludeWalker( walker, RegionExcluder() ) );
+       }
+}
+
+bool Map_SaveRegion( const char *filename ){
+       AddRegionBrushes();
+
+       bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Region, filename );
+
+       RemoveRegionBrushes();
+
+       return success;
+}
+
+
+void Map_RenameAbsolute( const char* absolute ){
+       Resource* resource = GlobalReferenceCache().capture( absolute );
+       NodeSmartReference clone( NewMapRoot( path_make_relative( absolute, GlobalFileSystem().findRoot( absolute ) ) ) );
+       resource->setNode( clone.get_pointer() );
+
+       {
+               //ScopeTimer timer("clone subgraph");
+               Node_getTraversable( GlobalSceneGraph().root() )->traverse( CloneAll( clone ) );
+       }
+
+       g_map.m_resource->detach( g_map );
+       GlobalReferenceCache().release( g_map.m_name.c_str() );
+
+       g_map.m_resource = resource;
+
+       g_map.m_name = absolute;
+       Map_UpdateTitle( g_map );
+
+       g_map.m_resource->attach( g_map );
+       // refresh VFS to apply new pak filtering based on mapname
+       // needed for daemon DPK VFS
+       VFS_Refresh();
+}
+
+void Map_Rename( const char* filename ){
+       if ( !string_equal( g_map.m_name.c_str(), filename ) ) {
+               ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+
+               Map_RenameAbsolute( filename );
+
+               SceneChangeNotify();
+       }
+       else
+       {
+               SaveReferences();
+       }
+}
+
+bool Map_Save(){
+       Pointfile_Clear();
+
+       ScopeTimer timer( "map save" );
+       SaveReferences();
+       return true; // assume success..
+}
+
+/*
+   ===========
+   Map_New
+
+   ===========
+ */
+void Map_New(){
+       //globalOutputStream() << "Map_New\n";
+
+       g_map.m_name = "unnamed.map";
+       Map_UpdateTitle( g_map );
+
+       {
+               g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
+//    ASSERT_MESSAGE(g_map.m_resource->getNode() == 0, "bleh");
+               g_map.m_resource->attach( g_map );
+
+               SceneChangeNotify();
+       }
+
+       FocusViews( g_vector3_identity, 0 );
+
+       g_currentMap = &g_map;
+
+       // restart VFS to apply new pak filtering based on mapname
+       // needed for daemon DPK VFS
+       VFS_Restart();
+}
+
+extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 &region_mins, const Vector3 &region_maxs );
+
+void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_mins, const Vector3& region_maxs ){
+       /*!
+          \todo we need to make sure that the player start IS inside the region and bail out if it's not
+          the compiler will refuse to compile a map with a player_start somewhere in empty space..
+          for now, let's just print an error
+        */
+
+       Vector3 vOrig( Camera_getOrigin( *g_pParentWnd->GetCamWnd() ) );
+
+       for ( int i = 0 ; i < 3 ; i++ )
+       {
+               if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
+                       globalErrorStream() << "Camera is NOT in the region, it's likely that the region won't compile correctly\n";
+                       break;
+               }
+       }
+
+       // write the info_playerstart
+       char sTmp[1024];
+       sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
+       Node_getEntity( *startpoint )->setKeyValue( "origin", sTmp );
+       sprintf( sTmp, "%d", (int)Camera_getAngles( *g_pParentWnd->GetCamWnd() )[CAMERA_YAW] );
+       Node_getEntity( *startpoint )->setKeyValue( "angle", sTmp );
+}
+
+/*
+   ===========================================================
+
+   REGION
+
+   ===========================================================
+ */
+bool region_active;
+Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
+Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
+
+scene::Node* region_sides[6];
+scene::Node* region_startpoint = 0;
+
+/*
+   ===========
+   AddRegionBrushes
+   a regioned map will have temp walls put up at the region boundary
+   \todo TODO TTimo old implementation of region brushes
+   we still add them straight in the worldspawn and take them out after the map is saved
+   with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
+   ===========
+ */
+void AddRegionBrushes( void ){
+       int i;
+
+       for ( i = 0; i < 6; i++ )
+       {
+               region_sides[i] = &GlobalBrushCreator().createBrush();
+               Node_getTraversable( Map_FindOrInsertWorldspawn( g_map ) )->insert( NodeSmartReference( *region_sides[i] ) );
+       }
+
+       region_startpoint = &GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( "info_player_start", false ) );
+
+       ConstructRegionBrushes( region_sides, region_mins, region_maxs );
+       ConstructRegionStartpoint( region_startpoint, region_mins, region_maxs );
+
+       Node_getTraversable( GlobalSceneGraph().root() )->insert( NodeSmartReference( *region_startpoint ) );
+}
+
+void RemoveRegionBrushes( void ){
+       for ( std::size_t i = 0; i < 6; i++ )
+       {
+               Node_getTraversable( *Map_GetWorldspawn( g_map ) )->erase( *region_sides[i] );
+       }
+       Node_getTraversable( GlobalSceneGraph().root() )->erase( *region_startpoint );
+}
+
+inline void exclude_node( scene::Node& node, bool exclude ){
+       exclude
+       ? node.enable( scene::Node::eExcluded )
+       : node.disable( scene::Node::eExcluded );
+}
+
+class ExcludeAllWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeAllWalker( bool exclude )
+       : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       exclude_node( path.top(), m_exclude );
+
+       return true;
+}
+};
+
+void Scene_Exclude_All( bool exclude ){
+       GlobalSceneGraph().traverse( ExcludeAllWalker( exclude ) );
+}
+
+bool Instance_isSelected( const scene::Instance& instance ){
+       const Selectable* selectable = Instance_getSelectable( instance );
+       return selectable != 0 && selectable->isSelected();
+}
+
+class ExcludeSelectedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeSelectedWalker( bool exclude )
+       : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
+       return true;
+}
+};
+
+void Scene_Exclude_Selected( bool exclude ){
+       GlobalSceneGraph().traverse( ExcludeSelectedWalker( exclude ) );
+}
+
+class ExcludeRegionedWalker : public scene::Graph::Walker
+{
+bool m_exclude;
+public:
+ExcludeRegionedWalker( bool exclude )
+       : m_exclude( exclude ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       exclude_node(
+               path.top(),
+               !(
+                       (
+                               aabb_intersects_aabb(
+                                       instance.worldAABB(),
+                                       aabb_for_minmax( region_mins, region_maxs )
+                                       ) != 0
+                       ) ^ m_exclude
+                       )
+               );
+
+       return true;
+}
+};
+
+void Scene_Exclude_Region( bool exclude ){
+       GlobalSceneGraph().traverse( ExcludeRegionedWalker( exclude ) );
+}
+
+/*
+   ===========
+   Map_RegionOff
+
+   Other filtering options may still be on
+   ===========
+ */
+void Map_RegionOff(){
+       region_active = false;
+
+       region_maxs[0] = g_MaxWorldCoord - 64;
+       region_mins[0] = g_MinWorldCoord + 64;
+       region_maxs[1] = g_MaxWorldCoord - 64;
+       region_mins[1] = g_MinWorldCoord + 64;
+       region_maxs[2] = g_MaxWorldCoord - 64;
+       region_mins[2] = g_MinWorldCoord + 64;
+
+       Scene_Exclude_All( false );
+}
+
+void Map_ApplyRegion( void ){
+       region_active = true;
+
+       Scene_Exclude_Region( false );
+}
+
+
+/*
+   ========================
+   Map_RegionSelectedBrushes
+   ========================
+ */
+void Map_RegionSelectedBrushes( void ){
+       Map_RegionOff();
+
+       if ( GlobalSelectionSystem().countSelected() != 0
+                && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
+               region_active = true;
+               Select_GetBounds( region_mins, region_maxs );
+
+               Scene_Exclude_Selected( false );
+
+               GlobalSelectionSystem().setSelectedAll( false );
+       }
+}
+
+
+/*
+   ===========
+   Map_RegionXY
    ===========
  */
-void Map_New( void ){
-       Sys_Printf( "Map_New\n" );
-       Map_Free();
+void Map_RegionXY( float x_min, float y_min, float x_max, float y_max ){
+       Map_RegionOff();
+
+       region_mins[0] = x_min;
+       region_maxs[0] = x_max;
+       region_mins[1] = y_min;
+       region_maxs[1] = y_max;
+       region_mins[2] = g_MinWorldCoord + 64;
+       region_maxs[2] = g_MaxWorldCoord - 64;
+
+       Map_ApplyRegion();
+}
+
+void Map_RegionBounds( const AABB& bounds ){
+       Map_RegionOff();
 
-       strcpy( currentmap, "unnamed.map" );
-       Sys_SetTitle( currentmap );
+       region_mins = vector3_subtracted( bounds.origin, bounds.extents );
+       region_maxs = vector3_added( bounds.origin, bounds.extents );
 
-       world_entity = (entity_s*)qmalloc( sizeof( *world_entity ) );
-       world_entity->brushes.onext =
-               world_entity->brushes.oprev = &world_entity->brushes;
-       SetKeyValue( world_entity, "classname", "worldspawn" );
-       world_entity->eclass = Eclass_ForName( "worldspawn", true );
+       deleteSelection();
+
+       Map_ApplyRegion();
+}
+
+/*
+   ===========
+   Map_RegionBrush
+   ===========
+ */
+void Map_RegionBrush( void ){
+       if ( GlobalSelectionSystem().countSelected() != 0 ) {
+               scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
+               Map_RegionBounds( instance.worldAABB() );
+       }
+}
 
-       g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] = 0;
-       g_pParentWnd->GetCamWnd()->Camera()->angles[PITCH] = 0;
-       VectorCopy( vec3_origin, g_pParentWnd->GetCamWnd()->Camera()->origin );
-       g_pParentWnd->GetCamWnd()->Camera()->origin[2] = 48;
-       VectorCopy( vec3_origin, g_pParentWnd->GetXYWnd()->GetOrigin() );
+//
+//================
+//Map_ImportFile
+//================
+//
+bool Map_ImportFile( const char* filename ){
+       ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
 
-       Map_RestoreBetween();
+       g_strLastFolder = g_path_get_dirname( filename );
 
-       Group_Init();
+       bool success = false;
 
-       Sys_UpdateWindows( W_ALL );
-       modified = false;
-}
+       if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
+               goto tryDecompile;
+       }
 
-/*
-   ===========================================================
+       {
+               const MapFormat* format = NULL;
+               const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), path_get_extension( filename ) );
+               if ( string_not_empty( moduleName ) ) {
+                       format = ReferenceAPI_getMapModules().findModule( moduleName );
+               }
 
-   REGION
+               if ( format ) {
+                       format->wrongFormat = false;
+               }
+               Resource* resource = GlobalReferenceCache().capture( filename );
+               resource->refresh(); // avoid loading old version if map has changed on disk since last import
+               if ( !resource->load() ) {
+                       GlobalReferenceCache().release( filename );
+                       goto tryDecompile;
+               }
+               if ( format ) {
+                       if ( format->wrongFormat ) {
+                               GlobalReferenceCache().release( filename );
+                               goto tryDecompile;
+                       }
+               }
+               NodeSmartReference clone( NewMapRoot( "" ) );
+               Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+               Map_gatherNamespaced( clone );
+               Map_mergeClonedNames();
+               MergeMap( clone );
+               success = true;
+               GlobalReferenceCache().release( filename );
+       }
 
-   ===========================================================
- */
-qboolean region_active;
-vec3_t region_mins = {g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord};
-vec3_t region_maxs = {g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord};
+       SceneChangeNotify();
+
+       return success;
+
+tryDecompile:
+
+       const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
+       int n = string_length( path_get_extension( filename ) );
+       if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
+               StringBuffer output;
+               output.push_string( AppPath_get() );
+               output.push_string( "q3map2." );
+               output.push_string( RADIANT_EXECUTABLE );
+               output.push_string( " -v -game " );
+               output.push_string( ( type && *type ) ? type : "quake3" );
+               output.push_string( " -fs_basepath \"" );
+               output.push_string( EnginePath_get() );
+               output.push_string( "\" -fs_homepath \"" );
+               output.push_string( g_qeglobals.m_userEnginePath.c_str() );
+               output.push_string( "\" -fs_game " );
+               output.push_string( gamename_get() );
+               output.push_string( " -convert -format " );
+               output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
+               if ( extension_equal( path_get_extension( filename ), "map" ) ) {
+                       output.push_string( " -readmap " );
+               }
+               output.push_string( " \"" );
+               output.push_string( filename );
+               output.push_string( "\"" );
+
+               // run
+               Q_Exec( NULL, output.c_str(), NULL, false, true );
+
+               // rebuild filename as "filenamewithoutext_converted.map"
+               output.clear();
+               output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
+               output.push_string( "_converted.map" );
+               filename = output.c_str();
+
+               // open
+               Resource* resource = GlobalReferenceCache().capture( filename );
+               resource->refresh(); // avoid loading old version if map has changed on disk since last import
+               if ( !resource->load() ) {
+                       GlobalReferenceCache().release( filename );
+                       goto tryDecompile;
+               }
+               NodeSmartReference clone( NewMapRoot( "" ) );
+               Node_getTraversable( *resource->getNode() )->traverse( CloneAll( clone ) );
+               Map_gatherNamespaced( clone );
+               Map_mergeClonedNames();
+               MergeMap( clone );
+               success = true;
+               GlobalReferenceCache().release( filename );
+       }
 
-brush_t *region_sides[6];
+       SceneChangeNotify();
+       return success;
+}
 
 /*
    ===========
-   AddRegionBrushes
-   a regioned map will have temp walls put up at the region boundary
-   \todo TODO TTimo old implementation of region brushes
-   we still add them straight in the worldspawn and take them out after the map is saved
-   with the new implementation we should be able to append them in a temporary manner to the data we pass to the map module
+   Map_SaveFile
    ===========
  */
-void AddRegionBrushes( void ){
-       vec3_t mins, maxs;
-       int i;
-       texdef_t td;
-
-       if ( !region_active ) {
-#ifdef _DEBUG
-               Sys_FPrintf( SYS_WRN, "Unexpected AddRegionBrushes call.\n" );
-#endif
-               return;
+bool Map_SaveFile( const char* filename ){
+       ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
+       bool success = MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
+       if ( success ) {
+               // refresh VFS to apply new pak filtering based on mapname
+               // needed for daemon DPK VFS
+               VFS_Refresh();
        }
+       return success;
+}
 
-       memset( &td, 0, sizeof( td ) );
-       td.SetName( SHADER_NOT_FOUND );
+//
+//===========
+//Map_SaveSelected
+//===========
+//
+// Saves selected world brushes and whole entities with partial/full selections
+//
+bool Map_SaveSelected( const char* filename ){
+       return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
+}
 
-       // set mins
-       VectorSet( mins, region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
 
-       // vary maxs
-       for ( i = 0; i < 3; i++ )
-       {
-               VectorSet( maxs, region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
-               maxs[i] = region_mins[i];
-               region_sides[i] = Brush_Create( mins, maxs, &td );
+class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
+{
+scene::Node& m_parent;
+public:
+ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if ( path.top().get_pointer() != &m_parent
+                && Node_isPrimitive( path.top() ) ) {
+               Selectable* selectable = Instance_getSelectable( instance );
+               if ( selectable != 0
+                        && selectable->isSelected()
+                        && path.size() > 1 ) {
+                       return false;
+               }
+       }
+       return true;
+}
+void post( const scene::Path& path, scene::Instance& instance ) const {
+       if ( path.top().get_pointer() != &m_parent
+                && Node_isPrimitive( path.top() ) ) {
+               Selectable* selectable = Instance_getSelectable( instance );
+               if ( selectable != 0
+                        && selectable->isSelected()
+                        && path.size() > 1 ) {
+                       scene::Node& parent = path.parent();
+                       if ( &parent != &m_parent ) {
+                               NodeSmartReference node( path.top().get() );
+                               Node_getTraversable( parent )->erase( node );
+                               Node_getTraversable( m_parent )->insert( node );
+                       }
+               }
        }
+}
+};
 
-       // set maxs
-       VectorSet( maxs, region_maxs[0] + 32, region_maxs[1] + 32, region_maxs[2] + 32 );
+void Scene_parentSelectedBrushesToEntity( scene::Graph& graph, scene::Node& parent ){
+       graph.traverse( ParentSelectedBrushesToEntityWalker( parent ) );
+}
 
-       // vary mins
-       for ( i = 0; i < 3; i++ )
-       {
-               VectorSet( mins, region_mins[0] - 32, region_mins[1] - 32, region_mins[2] - 32 );
-               mins[i] = region_maxs[i];
-               region_sides[i + 3] = Brush_Create( mins, maxs, &td );
+class CountSelectedBrushes : public scene::Graph::Walker
+{
+std::size_t& m_count;
+mutable std::size_t m_depth;
+public:
+CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
+       m_count = 0;
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if ( ++m_depth != 1 && path.top().get().isRoot() ) {
+               return false;
+       }
+       Selectable* selectable = Instance_getSelectable( instance );
+       if ( selectable != 0
+                && selectable->isSelected()
+                && Node_isPrimitive( path.top() ) ) {
+               ++m_count;
        }
+       return true;
+}
+void post( const scene::Path& path, scene::Instance& instance ) const {
+       --m_depth;
+}
+};
 
+std::size_t Scene_countSelectedBrushes( scene::Graph& graph ){
+       std::size_t count;
+       graph.traverse( CountSelectedBrushes( count ) );
+       return count;
+}
 
-       // this is a safe check, but it should not really happen anymore
-       vec3_t vOrig;
-       VectorSet( vOrig,
-                          (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],
-                          (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1],
-                          (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2] );
+enum ENodeType
+{
+       eNodeUnknown,
+       eNodeMap,
+       eNodeEntity,
+       eNodePrimitive,
+};
+
+const char* nodetype_get_name( ENodeType type ){
+       if ( type == eNodeMap ) {
+               return "map";
+       }
+       if ( type == eNodeEntity ) {
+               return "entity";
+       }
+       if ( type == eNodePrimitive ) {
+               return "primitive";
+       }
+       return "unknown";
+}
 
-       for ( i = 0 ; i < 3 ; i++ )
-       {
-               if ( vOrig[i] > region_maxs[i] || vOrig[i] < region_mins[i] ) {
-                       Sys_FPrintf( SYS_ERR, "Camera is NOT in the region, it's likely that the region won't compile correctly\n" );
-               }
+ENodeType node_get_nodetype( scene::Node& node ){
+       if ( Node_isEntity( node ) ) {
+               return eNodeEntity;
+       }
+       if ( Node_isPrimitive( node ) ) {
+               return eNodePrimitive;
        }
+       return eNodeUnknown;
+}
 
-       // write the info_playerstart
-       region_startpoint = Entity_Alloc();
-       SetKeyValue( region_startpoint, "classname", "info_player_start" );
-       region_startpoint->eclass = Eclass_ForName( "info_player_start", false );
-       char sTmp[1024];
-       sprintf( sTmp, "%d %d %d", (int)vOrig[0], (int)vOrig[1], (int)vOrig[2] );
-       SetKeyValue( region_startpoint, "origin", sTmp );
-       sprintf( sTmp, "%d", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] );
-       SetKeyValue( region_startpoint, "angle", sTmp );
-       // empty array of children
-       region_startpoint->pData = new CPtrArray;
+bool contains_entity( scene::Node& node ){
+       return Node_getTraversable( node ) != 0 && !Node_isBrush( node ) && !Node_isPatch( node ) && !Node_isEntity( node );
 }
 
-void RemoveRegionBrushes( void ){
-       int i;
+bool contains_primitive( scene::Node& node ){
+       return Node_isEntity( node ) && Node_getTraversable( node ) != 0 && Node_getEntity( node )->isContainer();
+}
 
-       if ( !region_active ) {
-               return;
+ENodeType node_get_contains( scene::Node& node ){
+       if ( contains_entity( node ) ) {
+               return eNodeEntity;
        }
-       for ( i = 0 ; i < 6 ; i++ )
-               Brush_Free( region_sides[i] );
-
-       CleanFilter( region_startpoint );
-       Entity_Free( region_startpoint );
+       if ( contains_primitive( node ) ) {
+               return eNodePrimitive;
+       }
+       return eNodeUnknown;
 }
 
-qboolean Map_IsBrushFiltered( brush_t *b ){
-       int i;
+void Path_parent( const scene::Path& parent, const scene::Path& child ){
+       ENodeType contains = node_get_contains( parent.top() );
+       ENodeType type = node_get_nodetype( child.top() );
 
-       for ( i = 0 ; i < 3 ; i++ )
+       if ( contains != eNodeUnknown && contains == type ) {
+               NodeSmartReference node( child.top().get() );
+               Path_deleteTop( child );
+               Node_getTraversable( parent.top() )->insert( node );
+               SceneChangeNotify();
+       }
+       else
        {
-               if ( b->mins[i] > region_maxs[i] ) {
-                       return true;
-               }
-               if ( b->maxs[i] < region_mins[i] ) {
-                       return true;
-               }
+               globalErrorStream() << "failed - " << nodetype_get_name( type ) << " cannot be parented to " << nodetype_get_name( contains ) << " container.\n";
        }
-       return false;
 }
 
-/*
-   ===========
-   Map_RegionOff
+void Scene_parentSelected(){
+       UndoableCommand undo( "parentSelected" );
 
-   Other filtering options may still be on
-   ===========
- */
-void Map_RegionOff( void ){
-       brush_t *b, *next;
-       int i;
+       if ( GlobalSelectionSystem().countSelected() > 1 ) {
+               class ParentSelectedBrushesToEntityWalker : public SelectionSystem::Visitor
+               {
+               const scene::Path& m_parent;
+public:
+               ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
+               }
+               void visit( scene::Instance& instance ) const {
+                       if ( &m_parent != &instance.path() ) {
+                               Path_parent( m_parent, instance.path() );
+                       }
+               }
+               };
 
-       region_active = false;
-       for ( i = 0 ; i < 3 ; i++ )
+               ParentSelectedBrushesToEntityWalker visitor( GlobalSelectionSystem().ultimateSelected().path() );
+               GlobalSelectionSystem().foreachSelected( visitor );
+       }
+       else
        {
-               region_maxs[i] = g_MaxWorldCoord - 64;
-               region_mins[i] = g_MinWorldCoord + 64;
+               globalOutputStream() << "failed - did not find two selected nodes.\n";
        }
+}
 
-       for ( b = filtered_brushes.next ; b != &filtered_brushes ; b = next )
-       {
-               next = b->next;
-               if ( Map_IsBrushFiltered( b ) ) {
-                       continue;       // still filtered
-               }
-               Brush_RemoveFromList( b );
-               if ( active_brushes.next == NULL || active_brushes.prev == NULL ) {
-                       active_brushes.next = &active_brushes;
-                       active_brushes.prev = &active_brushes;
-               }
-               Brush_AddToList( b, &active_brushes );
-               b->bFiltered = FilterBrush( b );
+
+
+void NewMap(){
+       if ( ConfirmModified( "New Map" ) ) {
+               Map_RegionOff();
+               Map_Free();
+               Map_New();
        }
-       Sys_UpdateWindows( W_ALL );
 }
 
-void Map_ApplyRegion( void ){
-       brush_t *b, *next;
+CopiedString g_mapsPath;
 
-       region_active = true;
-       for ( b = active_brushes.next ; b != &active_brushes ; b = next )
-       {
-               next = b->next;
-               if ( !Map_IsBrushFiltered( b ) ) {
-                       continue;       // still filtered
+const char* getMapsPath(){
+       return g_mapsPath.c_str();
+}
+
+const char* getLastFolderPath(){
+       if (g_strLastFolder.empty()) {
+               GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) );
+               if (g_strLastFolder.empty()) {
+                       g_strLastFolder = g_qeglobals.m_userGamePath;
                }
-               Brush_RemoveFromList( b );
-               Brush_AddToList( b, &filtered_brushes );
        }
+       return g_strLastFolder.c_str();
+}
 
-       Sys_UpdateWindows( W_ALL );
+const char* map_open( const char* title ){
+       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
 }
 
+const char* map_import( const char* title ){
+       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
+}
 
-/*
-   ========================
-   Map_RegionSelectedBrushes
-   ========================
- */
-void Map_RegionSelectedBrushes( void ){
-       Map_RegionOff();
+const char* map_save( const char* title ){
+       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
+}
 
-       if ( selected_brushes.next == &selected_brushes ) { // nothing selected
-               Sys_Printf( "Tried to region with no selection...\n" );
+void OpenMap(){
+       if ( !ConfirmModified( "Open Map" ) ) {
                return;
        }
-       region_active = true;
-       Select_GetBounds( region_mins, region_maxs );
 
-#ifdef _DEBUG
-       if ( filtered_brushes.next != &filtered_brushes ) {
-               Sys_Printf( "WARNING: filtered_brushes list may not be empty in Map_RegionSelectedBrushes\n" );
-       }
-#endif
+       const char* filename = map_open( "Open Map" );
 
-       if ( active_brushes.next == &active_brushes ) {
-               // just have an empty filtered_brushes list
-               // this happens if you set region after selecting all the brushes in your map (some weird people do that, ask MrE!)
-               filtered_brushes.next = filtered_brushes.prev = &filtered_brushes;
+       if ( filename != NULL ) {
+               MRU_AddFile( filename );
+               Map_RegionOff();
+               Map_Free();
+               Map_LoadFile( filename );
        }
-       else
-       {
-               // move the entire active_brushes list to filtered_brushes
-               filtered_brushes.next = active_brushes.next;
-               filtered_brushes.prev = active_brushes.prev;
-               filtered_brushes.next->prev = &filtered_brushes;
-               filtered_brushes.prev->next = &filtered_brushes;
-       }
-
-       // move the entire selected_brushes list to active_brushes
-       active_brushes.next = selected_brushes.next;
-       active_brushes.prev = selected_brushes.prev;
-       active_brushes.next->prev = &active_brushes;
-       active_brushes.prev->next = &active_brushes;
-
-       // deselect patches
-       for ( brush_t *b = active_brushes.next; b != &active_brushes; b = b->next )
-               if ( b->patchBrush ) {
-                       b->pPatch->bSelected = false;
-               }
+}
 
-       // clear selected_brushes
-       selected_brushes.next = selected_brushes.prev = &selected_brushes;
+void ImportMap(){
+       const char* filename = map_import( "Import Map" );
 
-       Sys_UpdateWindows( W_ALL );
+       if ( filename != NULL ) {
+               UndoableCommand undo( "mapImport" );
+               Map_ImportFile( filename );
+       }
 }
 
+bool Map_SaveAs(){
+       const char* filename = map_save( "Save Map" );
 
-/*
-   ===========
-   Map_RegionXY
-   ===========
- */
-void Map_RegionXY( void ){
-       Map_RegionOff();
+       if ( filename != NULL ) {
+               g_strLastFolder = g_path_get_dirname( filename );
+               MRU_AddFile( filename );
+               Map_Rename( filename );
+               return Map_Save();
+       }
+       return false;
+}
 
-       region_mins[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();
-       region_maxs[0] = g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5 * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale();
-       region_mins[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();
-       region_maxs[1] = g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5 * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale();
-       region_mins[2] = g_MinWorldCoord + 64;
-       region_maxs[2] = g_MaxWorldCoord - 64;
-       Map_ApplyRegion();
+void SaveMapAs(){
+       Map_SaveAs();
 }
 
-/*
-   ===========
-   Map_RegionTallBrush
-   ===========
- */
-void Map_RegionTallBrush( void ){
-       brush_t *b;
+void SaveMap(){
+       if ( Map_Unnamed( g_map ) ) {
+               SaveMapAs();
+       }
+       else if ( Map_Modified( g_map ) ) {
+               Map_Save();
+       }
+}
 
-       if ( !QE_SingleBrush() ) {
-               return;
+void ExportMap(){
+       const char* filename = map_save( "Export Selection" );
+
+       if ( filename != NULL ) {
+               g_strLastFolder = g_path_get_dirname( filename );
+               Map_SaveSelected( filename );
        }
+}
 
-       b = selected_brushes.next;
+void SaveRegion(){
+       const char* filename = map_save( "Export Region" );
 
-       Map_RegionOff();
+       if ( filename != NULL ) {
+               g_strLastFolder = g_path_get_dirname( filename );
+               Map_SaveRegion( filename );
+       }
+}
 
-       VectorCopy( b->mins, region_mins );
-       VectorCopy( b->maxs, region_maxs );
-       region_mins[2] = g_MinWorldCoord + 64;
-       region_maxs[2] = g_MaxWorldCoord - 64;
 
-       Undo_Start( "delete" );
-       Undo_AddBrushList( &selected_brushes );
-       Undo_AddEntity( b->owner );
-       Select_Delete();
-       Undo_EndBrushList( &selected_brushes );
-       Undo_End();
+void RegionOff(){
+       Map_RegionOff();
+       SceneChangeNotify();
+}
 
-       Map_ApplyRegion();
+void RegionXY(){
+       Map_RegionXY(
+               g_pParentWnd->GetXYWnd()->GetOrigin()[0] - 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
+               g_pParentWnd->GetXYWnd()->GetOrigin()[1] - 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale(),
+               g_pParentWnd->GetXYWnd()->GetOrigin()[0] + 0.5f * g_pParentWnd->GetXYWnd()->Width() / g_pParentWnd->GetXYWnd()->Scale(),
+               g_pParentWnd->GetXYWnd()->GetOrigin()[1] + 0.5f * g_pParentWnd->GetXYWnd()->Height() / g_pParentWnd->GetXYWnd()->Scale()
+               );
+       SceneChangeNotify();
 }
 
-/*
-   ===========
-   Map_RegionBrush
-   ===========
- */
-void Map_RegionBrush( void ){
-       brush_t *b;
+void RegionBrush(){
+       Map_RegionBrush();
+       SceneChangeNotify();
+}
 
-       if ( !QE_SingleBrush() ) {
-               return;
-       }
+void RegionSelected(){
+       Map_RegionSelectedBrushes();
+       SceneChangeNotify();
+}
 
-       b = selected_brushes.next;
 
-       Map_RegionOff();
 
-       VectorCopy( b->mins, region_mins );
-       VectorCopy( b->maxs, region_maxs );
 
-       Undo_Start( "delete" );
-       Undo_AddBrushList( &selected_brushes );
-       Undo_AddEntity( b->owner );
-       Select_Delete();
-       Undo_EndBrushList( &selected_brushes );
-       Undo_End();
 
-       Map_ApplyRegion();
+class BrushFindByIndexWalker : public scene::Traversable::Walker
+{
+mutable std::size_t m_index;
+scene::Path& m_path;
+public:
+BrushFindByIndexWalker( std::size_t index, scene::Path& path )
+       : m_index( index ), m_path( path ){
+}
+bool pre( scene::Node& node ) const {
+       if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
+               m_path.push( makeReference( node ) );
+       }
+       return false;
+}
+};
+
+class EntityFindByIndexWalker : public scene::Traversable::Walker
+{
+mutable std::size_t m_index;
+scene::Path& m_path;
+public:
+EntityFindByIndexWalker( std::size_t index, scene::Path& path )
+       : m_index( index ), m_path( path ){
 }
+bool pre( scene::Node& node ) const {
+       if ( Node_isEntity( node ) && m_index-- == 0 ) {
+               m_path.push( makeReference( node ) );
+       }
+       return false;
+}
+};
 
-GList *find_string( GList *glist, const char *buf ){
-       while ( glist )
+void Scene_FindEntityBrush( std::size_t entity, std::size_t brush, scene::Path& path ){
+       path.push( makeReference( GlobalSceneGraph().root() ) );
        {
-               if ( strcmp( (char *)glist->data, buf ) == 0 ) {
-                       break; // this name is in our list already
+               Node_getTraversable( path.top() )->traverse( EntityFindByIndexWalker( entity, path ) );
+       }
+       if ( path.size() == 2 ) {
+               scene::Traversable* traversable = Node_getTraversable( path.top() );
+               if ( traversable != 0 ) {
+                       traversable->traverse( BrushFindByIndexWalker( brush, path ) );
                }
-               glist = glist->next;
        }
-       return glist;
 }
 
-void Map_ImportBuffer( char *buf ){
-       Select_Deselect();
+inline bool Node_hasChildren( scene::Node& node ){
+       scene::Traversable* traversable = Node_getTraversable( node );
+       return traversable != 0 && !traversable->empty();
+}
 
-       Undo_Start( "import buffer" );
+void SelectBrush( int entitynum, int brushnum ){
+       scene::Path path;
+       Scene_FindEntityBrush( entitynum, brushnum, path );
+       if ( path.size() == 3 || ( path.size() == 2 && !Node_hasChildren( path.top() ) ) ) {
+               scene::Instance* instance = GlobalSceneGraph().find( path );
+               ASSERT_MESSAGE( instance != 0, "SelectBrush: path not found in scenegraph" );
+               Selectable* selectable = Instance_getSelectable( *instance );
+               ASSERT_MESSAGE( selectable != 0, "SelectBrush: path not selectable" );
+               selectable->setSelected( true );
+               g_pParentWnd->GetXYWnd()->PositionView( instance->worldAABB().origin );
+       }
+}
 
-       MemStream stream;
 
-       stream.Write( buf, strlen( buf ) );
-       Map_Import( &stream, "xmap" );
-       stream.Close();
+class BrushFindIndexWalker : public scene::Graph::Walker
+{
+mutable const scene::Node* m_node;
+std::size_t& m_count;
+public:
+BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
+       : m_node( &node ), m_count( count ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if ( Node_isPrimitive( path.top() ) ) {
+               if ( m_node == path.top().get_pointer() ) {
+                       m_node = 0;
+               }
+               if ( m_node ) {
+                       ++m_count;
+               }
+       }
+       return true;
+}
+};
+
+class EntityFindIndexWalker : public scene::Graph::Walker
+{
+mutable const scene::Node* m_node;
+std::size_t& m_count;
+public:
+EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
+       : m_node( &node ), m_count( count ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       if ( Node_isEntity( path.top() ) ) {
+               if ( m_node == path.top().get_pointer() ) {
+                       m_node = 0;
+               }
+               if ( m_node ) {
+                       ++m_count;
+               }
+       }
+       return true;
+}
+};
 
-       Sys_UpdateWindows( W_ALL );
-       Sys_MarkMapModified();
+static void GetSelectionIndex( int *ent, int *brush ){
+       std::size_t count_brush = 0;
+       std::size_t count_entity = 0;
+       if ( GlobalSelectionSystem().countSelected() != 0 ) {
+               const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
 
-       Undo_End();
+               GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
+               GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
+       }
+       *brush = int(count_brush);
+       *ent = int(count_entity);
 }
 
+void DoFind(){
+       ModalDialog dialog;
+       GtkEntry* entity;
+       GtkEntry* brush;
 
-//
-//================
-//Map_ImportFile
-//================
-//
-void Map_ImportFile( const char *filename ){
-       FileStream file;
-       Sys_BeginWait();
+       GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
 
-       Sys_Printf( "Importing map from %s\n",filename );
+       GtkAccelGroup* accel = gtk_accel_group_new();
+       gtk_window_add_accel_group( window, accel );
 
-       const char* type = strrchr( filename,'.' );
-       if ( type != NULL ) {
-               type++;
-       }
-       /*!\todo Resolve "r" problem in scriptlib" */
-       if ( file.Open( filename, "rb" ) ) {
-               Map_Import( &file, type, true );
-       }
-       else{
-               Sys_FPrintf( SYS_ERR, "ERROR: couldn't open %s for read\n", filename );
+       {
+               GtkVBox* vbox = create_dialog_vbox( 4, 4 );
+               gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
+               {
+                       GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
+                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
+                       {
+                               GtkWidget* label = gtk_label_new( "Entity number" );
+                               gtk_widget_show( label );
+                               gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
+                                                                 (GtkAttachOptions) ( 0 ),
+                                                                 (GtkAttachOptions) ( 0 ), 0, 0 );
+                       }
+                       {
+                               GtkWidget* label = gtk_label_new( "Brush number" );
+                               gtk_widget_show( label );
+                               gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
+                                                                 (GtkAttachOptions) ( 0 ),
+                                                                 (GtkAttachOptions) ( 0 ), 0, 0 );
+                       }
+                       {
+                               GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
+                               gtk_widget_show( GTK_WIDGET( entry ) );
+                               gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 0, 1,
+                                                                 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+                                                                 (GtkAttachOptions) ( 0 ), 0, 0 );
+                               gtk_widget_grab_focus( GTK_WIDGET( entry ) );
+                               entity = entry;
+                       }
+                       {
+                               GtkEntry* entry = GTK_ENTRY( gtk_entry_new() );
+                               gtk_widget_show( GTK_WIDGET( entry ) );
+                               gtk_table_attach( table, GTK_WIDGET( entry ), 1, 2, 1, 2,
+                                                                 (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+                                                                 (GtkAttachOptions) ( 0 ), 0, 0 );
+
+                               brush = entry;
+                       }
+               }
+               {
+                       GtkHBox* hbox = create_dialog_hbox( 4 );
+                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
+                       {
+                               GtkButton* button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
+                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
+                               widget_make_default( GTK_WIDGET( button ) );
+                               gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0 );
+                       }
+                       {
+                               GtkButton* button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
+                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
+                               gtk_widget_add_accelerator( GTK_WIDGET( button ), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
+                       }
+               }
        }
 
-       file.Close();
+       // Initialize dialog
+       char buf[16];
+       int ent, br;
+
+       GetSelectionIndex( &ent, &br );
+       sprintf( buf, "%i", ent );
+       gtk_entry_set_text( entity, buf );
+       sprintf( buf, "%i", br );
+       gtk_entry_set_text( brush, buf );
+
+       if ( modal_dialog_show( window, dialog ) == eIDOK ) {
+               const char *entstr = gtk_entry_get_text( entity );
+               const char *brushstr = gtk_entry_get_text( brush );
+               SelectBrush( atoi( entstr ), atoi( brushstr ) );
+       }
 
-       Sys_UpdateWindows( W_ALL );
-       modified = true;
-       Sys_EndWait();
+       gtk_widget_destroy( GTK_WIDGET( window ) );
 }
 
-//
-//===========
-//Map_SaveSelected
-//===========
-//
-// Saves selected world brushes and whole entities with partial/full selections
-//
-void Map_SaveSelected( const char* filename ){
-       FileStream file;
+void Map_constructPreferences( PreferencesPage& page ){
+       page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
+}
 
-       Sys_Printf( "Saving selection to %s\n",filename );
 
-       const char* type = strrchr( filename,'.' );
-       if ( type != NULL ) {
-               type++;
-       }
-       if ( file.Open( filename, "w" ) ) {
-               Map_Export( &file, type, false, true );
+class MapEntityClasses : public ModuleObserver
+{
+std::size_t m_unrealised;
+public:
+MapEntityClasses() : m_unrealised( 1 ){
+}
+void realise(){
+       if ( --m_unrealised == 0 ) {
+               if ( g_map.m_resource != 0 ) {
+                       ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
+                       g_map.m_resource->realise();
+               }
        }
-       else{
-               Sys_FPrintf( SYS_ERR, "ERROR: failed to open %s for write\n", filename );
+}
+void unrealise(){
+       if ( ++m_unrealised == 1 ) {
+               if ( g_map.m_resource != 0 ) {
+                       g_map.m_resource->flush();
+                       g_map.m_resource->unrealise();
+               }
        }
+}
+};
 
-       file.Close();
+MapEntityClasses g_MapEntityClasses;
 
-}
 
-//
-//===========
-//Map_SaveSelected
-//===========
-//
-// Saves selected world brushes and whole entities with partial/full selections
-//
-void Map_SaveSelected( MemStream* pMemFile, MemStream* pPatchFile ){
-       Map_Export( pMemFile, "xmap", false, true );
-
-       /*
-          // write world entity first
-          Entity_WriteSelected(world_entity, pMemFile);
-
-          // then write all other ents
-          count = 1;
-          for (e=entities.next ; e != &entities ; e=next)
-          {
-              MemFile_fprintf(pMemFile, "// entity %i\n", count);
-              count++;
-              Entity_WriteSelected(e, pMemFile);
-              next = e->next;
-          }
-
-          //if (pPatchFile)
-          //  Patch_WriteFile(pPatchFile);
-        */
+class MapModuleObserver : public ModuleObserver
+{
+std::size_t m_unrealised;
+public:
+MapModuleObserver() : m_unrealised( 1 ){
+}
+void realise(){
+       if ( --m_unrealised == 0 ) {
+               ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
+               StringOutputStream buffer( 256 );
+               buffer << g_qeglobals.m_userGamePath.c_str() << "maps/";
+               Q_mkdir( buffer.c_str() );
+               g_mapsPath = buffer.c_str();
+       }
+}
+void unrealise(){
+       if ( ++m_unrealised == 1 ) {
+               g_mapsPath = "";
+       }
 }
+};
+
+MapModuleObserver g_MapModuleObserver;
 
+CopiedString g_strLastMap;
+bool g_bLoadLastMap = false;
 
-void MemFile_fprintf( MemStream* pMemFile, const char* pText, ... ){
-       char Buffer[4096];
-       va_list args;
-       va_start( args,pText );
-       vsprintf( Buffer, pText, args );
-       pMemFile->Write( Buffer, strlen( Buffer ) );
+void Map_Construct(){
+       GlobalCommands_insert( "RegionOff", FreeCaller<RegionOff>() );
+       GlobalCommands_insert( "RegionSetXY", FreeCaller<RegionXY>() );
+       GlobalCommands_insert( "RegionSetBrush", FreeCaller<RegionBrush>() );
+       GlobalCommands_insert( "RegionSetSelection", FreeCaller<RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
+
+       GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
+
+       PreferencesDialog_addSettingsPreferences( FreeCaller1<PreferencesPage&, Map_constructPreferences>() );
+
+       GlobalEntityClassManager().attach( g_MapEntityClasses );
+       Radiant_attachHomePathsObserver( g_MapModuleObserver );
 }
 
-/*!
-   ==============
-   Region_SpawnPoint
-   push the region spawn point
-   \todo FIXME TTimo this was in the #1 MAP module implementation (in the core)
-   not sure it has any use anymore, should prolly drop it
-   ==============
- */
-void Region_SpawnPoint( FILE *f ){
-       // write the info_player_start, we use the camera position
-       fprintf( f, "{\n" );
-       fprintf( f, "\"classname\" \"info_player_start\"\n" );
-       fprintf( f, "\"origin\" \"%i %i %i\"\n",
-                        (int)g_pParentWnd->GetCamWnd()->Camera()->origin[0],
-                        (int)g_pParentWnd->GetCamWnd()->Camera()->origin[1],
-                        (int)g_pParentWnd->GetCamWnd()->Camera()->origin[2] );
-       fprintf( f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamWnd()->Camera()->angles[YAW] );
-       fprintf( f, "}\n" );
+void Map_Destroy(){
+       Radiant_detachHomePathsObserver( g_MapModuleObserver );
+       GlobalEntityClassManager().detach( g_MapEntityClasses );
 }