]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/map.cpp
netradiant: strip 16-bit png to 8-bit, fix #153
[xonotic/netradiant.git] / radiant / map.cpp
index fae78de3692376068e22060520195a0cdf1a3ad5..c783e035f869f6e63123ada368328c54ca4b6e6c 100644 (file)
 
 #include "map.h"
 
+#include <gtk/gtk.h>
+
 #include "debugging/debugging.h"
 
 #include "imap.h"
+
 MapModules& ReferenceAPI_getMapModules();
+
 #include "iselection.h"
 #include "iundo.h"
 #include "ibrush.h"
@@ -35,21 +39,15 @@ MapModules& ReferenceAPI_getMapModules();
 #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 <gdk/gdkkeysyms.h>
+#include "uilib/uilib.h"
 
 #include "scenelib.h"
 #include "transformlib.h"
@@ -61,6 +59,7 @@ MapModules& ReferenceAPI_getMapModules();
 #include "cmdlib.h"
 #include "stream/textfilestream.h"
 #include "os/path.h"
+#include "os/file.h"
 #include "uniquenames.h"
 #include "modulesystem/singletonmodule.h"
 #include "modulesystem/moduleregistry.h"
@@ -80,12 +79,16 @@ MapModules& ReferenceAPI_getMapModules();
 #include "xywindow.h"
 #include "mainframe.h"
 #include "preferences.h"
+#include "preferencesystem.h"
 #include "referencecache.h"
 #include "mru.h"
 #include "commands.h"
 #include "autosave.h"
 #include "brushmodule.h"
 #include "brush.h"
+#include "patch.h"
+
+bool g_writeMapComments = true;
 
 class NameObserver
 {
@@ -98,6 +101,7 @@ void construct(){
                m_names.insert( name_read( c_str() ) );
        }
 }
+
 void destroy(){
        if ( !empty() ) {
                //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
@@ -106,6 +110,7 @@ void destroy(){
 }
 
 NameObserver& operator=( const NameObserver& other );
+
 public:
 NameObserver( UniqueNames& names ) : m_names( names ){
        construct();
@@ -113,21 +118,26 @@ NameObserver( UniqueNames& names ) : m_names( names ){
 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;
+
+typedef MemberCaller<NameObserver, void(const char*), &NameObserver::nameChanged> NameChangedCaller;
 };
 
 class BasicNamespace : public Namespace
@@ -139,12 +149,14 @@ 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" );
@@ -199,11 +211,13 @@ class NamespaceAPI
 Namespace* m_namespace;
 public:
 typedef Namespace Type;
+
 STRING_CONSTANT( Name, "*" );
 
 NamespaceAPI(){
        m_namespace = &g_defaultNamespace;
 }
+
 Namespace* getTable(){
        return m_namespace;
 }
@@ -261,6 +275,7 @@ public:
 WorldNode()
        : m_node( 0 ){
 }
+
 void set( scene::Node* node ){
        if ( m_node != 0 ) {
                m_node->DecRef();
@@ -270,6 +285,7 @@ void set( scene::Node* node ){
                m_node->IncRef();
        }
 }
+
 scene::Node* get() const {
        return m_node;
 }
@@ -277,7 +293,9 @@ scene::Node* get() const {
 
 class Map;
 void Map_SetValid( Map& map, bool valid );
+
 void Map_UpdateTitle( const Map& map );
+
 void Map_SetWorldspawn( Map& map, scene::Node* node );
 
 
@@ -289,6 +307,7 @@ Resource* m_resource;
 bool m_valid;
 
 bool m_modified;
+
 void ( *m_modified_changed )( const Map& );
 
 Signal0 m_mapValidCallbacks;
@@ -319,6 +338,7 @@ void realise(){
                Map_SetValid( g_map, true );
        }
 }
+
 void unrealise(){
        if ( m_resource != 0 ) {
                Map_SetValid( g_map, false );
@@ -386,7 +406,6 @@ void Map_UpdateTitle( const Map& map ){
 }
 
 
-
 scene::Node* Map_GetWorldspawn( const Map& map ){
        return map.m_world_node.get();
 }
@@ -402,6 +421,7 @@ float g_MaxWorldCoord = 64 * 1024;
 float g_MinWorldCoord = -64 * 1024;
 
 void AddRegionBrushes( void );
+
 void RemoveRegionBrushes( void );
 
 
@@ -432,6 +452,7 @@ 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() );
@@ -541,6 +562,7 @@ 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 );
@@ -584,12 +606,14 @@ 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();
 }
@@ -602,6 +626,7 @@ 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 );
@@ -631,6 +656,7 @@ bool pre( scene::Node& node ) const {
        }
        return false;
 }
+
 void post( scene::Node& node ) const {
        m_path.pop();
 }
@@ -645,6 +671,7 @@ public:
 TypeCasts(){
        NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
 }
+
 NodeTypeCastTable& get(){
        return m_casts;
 }
@@ -662,9 +689,11 @@ scene::Traversable& get( NullType<scene::Traversable>){
 
 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
 }
+
 void release(){
        delete this;
 }
+
 scene::Node& node(){
        return m_node;
 }
@@ -674,6 +703,7 @@ scene::Node& node(){
 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() );
@@ -702,6 +732,7 @@ public:
 CloneAll( scene::Node& root )
        : m_path( makeReference( root ) ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( node.isRoot() ) {
                return false;
@@ -712,6 +743,7 @@ bool pre( scene::Node& node ) const {
 
        return true;
 }
+
 void post( scene::Node& node ) const {
        if ( node.isRoot() ) {
                return;
@@ -733,6 +765,50 @@ scene::Node& Node_Clone( scene::Node& node ){
        return clone;
 }
 
+bool Node_instanceSelected( scene::Node& node );
+
+class CloneAllSelected : public scene::Traversable::Walker
+{
+mutable scene::Path m_path;
+public:
+CloneAllSelected( scene::Node& root )
+       : m_path( makeReference( root ) ){
+}
+bool pre( scene::Node& node ) const {
+       if ( node.isRoot() ) {
+               return false;
+       }
+
+       if( Node_instanceSelected( node ) ){
+               m_path.push( makeReference( node_clone( node ) ) );
+               m_path.top().get().IncRef();
+       }
+
+       return true;
+}
+void post( scene::Node& node ) const {
+       if ( node.isRoot() ) {
+               return;
+       }
+
+       if( Node_instanceSelected( node ) ){
+               Node_getTraversable( m_path.parent() )->insert( m_path.top() );
+
+               m_path.top().get().DecRef();
+               m_path.pop();
+       }
+}
+};
+
+scene::Node& Node_Clone_Selected( scene::Node& node ){
+       scene::Node& clone = node_clone( node );
+       scene::Traversable* traversable = Node_getTraversable( node );
+       if ( traversable != 0 ) {
+               traversable->traverse( CloneAllSelected( clone ) );
+       }
+       return clone;
+}
+
 
 typedef std::map<CopiedString, std::size_t> EntityBreakdown;
 
@@ -743,14 +819,17 @@ 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()];
                }
-               else{ ++m_entitymap[eclass.name()]; }
        }
        return true;
 }
@@ -760,111 +839,196 @@ void Scene_EntityBreakdown( EntityBreakdown& entitymap ){
        GlobalSceneGraph().traverse( EntityBreakdownWalker( entitymap ) );
 }
 
+class CountStuffWalker : public scene::Graph::Walker
+{
+int& m_patches;
+int& m_ents_ingame;
+int& m_groupents;
+int& m_groupents_ingame;
+public:
+CountStuffWalker( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame )
+       : m_patches( patches ), m_ents_ingame( ents_ingame ), m_groupents( groupents ), m_groupents_ingame( groupents_ingame ){
+}
+bool pre( const scene::Path& path, scene::Instance& instance ) const {
+       Patch* patch = Node_getPatch( path.top() );
+       if( patch != 0 ){
+               ++m_patches;
+       }
+       Entity* entity = Node_getEntity( path.top() );
+       if ( entity != 0 ){
+               if( entity->isContainer() ){
+                       ++m_groupents;
+                       if( !string_equal_nocase( "func_group", entity->getKeyValue( "classname" ) ) &&
+                               !string_equal_nocase( "_decal", entity->getKeyValue( "classname" ) ) ){
+                               ++m_groupents_ingame;
+                               ++m_ents_ingame;
+                       }
+                       return true;
+               }
+               if( !string_equal_nocase_n( "light", entity->getKeyValue( "classname" ), 5 ) &&
+                       !string_equal_nocase( "misc_model", entity->getKeyValue( "classname" ) ) ){
+                       ++m_ents_ingame;
+               }
+       }
+       return true;
+}
+};
+
+void Scene_CountStuff( int& patches, int& ents_ingame, int& groupents, int& groupents_ingame ){
+       GlobalSceneGraph().traverse( CountStuffWalker( patches, ents_ingame, groupents, groupents_ingame ) );
+}
 
 WindowPosition g_posMapInfoWnd( c_default_window_pos );
 
 void DoMapInfo(){
        ModalDialog dialog;
-       GtkEntry* brushes_entry;
-       GtkEntry* entities_entry;
-       GtkListStore* EntityBreakdownWalker;
+       ui::Widget w_brushes{ui::null};
+       ui::Widget w_patches{ui::null};
+       ui::Widget w_ents{ui::null};
+       ui::Widget w_ents_ingame{ui::null};
+       ui::Widget w_groupents{ui::null};
+       ui::Widget w_groupents_ingame{ui::null};
+
+       ui::ListStore EntityBreakdownWalker{ui::null};
 
-       GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Map Info", G_CALLBACK( dialog_delete_callback ), &dialog );
+       ui::Window window = MainFrame_getWindow().create_dialog_window("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 ) );
+               auto vbox = create_dialog_vbox( 4, 4 );
+               window.add(vbox);
 
                {
-                       GtkHBox* hbox = create_dialog_hbox( 4 );
-                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), FALSE, TRUE, 0 );
+                       auto hbox = create_dialog_hbox( 4 );
+                       vbox.pack_start( hbox, FALSE, FALSE, 0 );
 
                        {
-                               GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
-                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
+                               auto table = create_dialog_table( 3, 4, 4, 4 );
+                               hbox.pack_start( 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;
+                                       auto label = ui::Label( "Total Brushes:" );
+                                       label.show();
+                    table.attach(label, {0, 1, 0, 1}, {GTK_FILL, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
                                }
                                {
-                                       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;
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                    table.attach(label, {1, 2, 0, 1}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0});
+                                       w_brushes = label;
                                }
                                {
-                                       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 );
+                                       auto label = ui::Label( "Total Patches" );
+                                       label.show();
+                    table.attach(label, {2, 3, 0, 1}, {GTK_FILL, 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 );
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                    table.attach(label, {3, 4, 0, 1}, {GTK_FILL, 0}, {3, 0});
                                        gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
+                                       w_patches = label;
+                               }
+                               {
+                                       auto label = ui::Label( "Total Entities:" );
+                                       label.show();
+                                       table.attach(label, {0, 1, 1, 2}, {GTK_FILL, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                               }
+                               {
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                                       table.attach(label, {1, 2, 1, 2}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                                       w_ents = label;
+                               }
+                               {
+                                       auto label = ui::Label( "Ingame Entities:" );
+                                       label.show();
+                                       table.attach(label, {2, 3, 1, 2}, {GTK_FILL, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                               }
+                               {
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                                       table.attach(label, {3, 4, 1, 2}, {GTK_FILL | GTK_EXPAND, 0 }, {3, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                                       w_ents_ingame = label;
+                               }
+                               {
+                                       auto label = ui::Label( "Group Entities:" );
+                                       label.show();
+                                       table.attach(label, {0, 1, 2, 3}, {GTK_FILL, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
                                }
+                               {
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                                       table.attach(label, {1, 2, 2, 3}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                                       w_groupents = label;
+                               }
+                               {
+                                       auto label = ui::Label( "Ingame Group Entities:" );
+                                       label.show();
+                                       table.attach(label, {2, 3, 2, 3}, {GTK_FILL, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                               }
+                               {
+                                       auto label = ui::Label( "" );
+                                       label.show();
+                                       table.attach(label, {3, 4, 2, 3}, {GTK_FILL | GTK_EXPAND, 0}, {3, 0});
+                                       gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
+                                       w_groupents_ingame = label;
+                               }
+
                        }
                        {
-                               GtkVBox* vbox2 = create_dialog_vbox( 4 );
-                               gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( vbox2 ), FALSE, FALSE, 0 );
+                               auto vbox2 = create_dialog_vbox( 4 );
+                               hbox.pack_start( 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 );
+                                       auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_ok ), &dialog );
+                                       vbox2.pack_start( 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 );
+                       ui::Widget label = ui::Label( "*** Entity breakdown ***" );
+                       label.show();
+                       vbox.pack_start( 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 );
+                       auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
+                       vbox.pack_start( scr, TRUE, TRUE, 0 );
 
                        {
-                               GtkListStore* store = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
+                               auto store = ui::ListStore::from(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_UINT ));
 
-                               GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
-                               gtk_tree_view_set_headers_clickable( GTK_TREE_VIEW( view ), TRUE );
+                               auto view = ui::TreeView(ui::TreeModel::from(store._handle));
+                               gtk_tree_view_set_headers_clickable(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 );
+                                       auto renderer = ui::CellRendererText(ui::New);
+                                       auto column = ui::TreeViewColumn( "Entity", renderer, {{"text", 0}} );
+                                       gtk_tree_view_append_column(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 );
+                                       auto renderer = ui::CellRendererText(ui::New);
+                                       auto column = ui::TreeViewColumn( "Count", renderer, {{"text", 1}} );
+                                       gtk_tree_view_append_column(view, column );
                                        gtk_tree_view_column_set_sort_column_id( column, 1 );
                                }
 
-                               gtk_widget_show( view );
+                               view.show();
 
-                               gtk_container_add( GTK_CONTAINER( scr ), view );
+                               scr.add(view);
 
                                EntityBreakdownWalker = store;
                        }
@@ -879,28 +1043,52 @@ void DoMapInfo(){
 
                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 );
+                       EntityBreakdownWalker.append(0, (*i).first.c_str(), 1, Unsigned( ( *i ).second ));
                }
        }
 
-       g_object_unref( G_OBJECT( EntityBreakdownWalker ) );
+       EntityBreakdownWalker.unref();
+
+       int n_patches = 0;
+       int n_ents_ingame = 0;
+       int n_groupents = 0;
+       int n_groupents_ingame = 0;
+       Scene_CountStuff( n_patches, n_ents_ingame, n_groupents, n_groupents_ingame );
+       //globalOutputStream() << n_patches << n_ents_ingame << n_groupents << n_groupents_ingame << "\n";
+
+       char *markup;
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span>  ", Unsigned( g_brushCount.get() ) );
+       gtk_label_set_markup( GTK_LABEL( w_brushes ), markup );
+       g_free( markup );
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_patches );
+       gtk_label_set_markup( GTK_LABEL( w_patches ), markup );
+       g_free( markup );
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%u</b></span>  ", Unsigned( g_entityCount.get() ) );
+       gtk_label_set_markup( GTK_LABEL( w_ents ), markup );
+       g_free( markup );
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_ents_ingame );
+       gtk_label_set_markup( GTK_LABEL( w_ents_ingame ), markup );
+       g_free( markup );
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_groupents );
+       gtk_label_set_markup( GTK_LABEL( w_groupents ), markup );
+       g_free( markup );
+
+       markup = g_markup_printf_escaped( "<span style=\"italic\"><b>%i</b></span>  ", n_groupents_ingame );
+       gtk_label_set_markup( GTK_LABEL( w_groupents_ingame ), markup );
+       g_free( markup );
 
-       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 ) );
+    window.destroy();
 }
 
 
@@ -914,12 +1102,15 @@ 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_strLastMapFolder = "";
+
 /*
    ================
    Map_LoadFile
@@ -927,9 +1118,20 @@ ScopeTimer( const char* message )
  */
 
 void Map_LoadFile( const char *filename ){
+       g_map.m_name = filename;
+
+       // refresh VFS to apply new pak filtering based on mapname
+       // needed for daemon DPK VFS
+       VFS_Refresh();
+
        globalOutputStream() << "Loading map from " << filename << "\n";
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
 
+       MRU_AddFile( filename );
+       g_strLastMapFolder = g_path_get_dirname( filename );
+
+       bool switch_format = false;
+
        {
                ScopeTimer timer( "map load" );
 
@@ -945,8 +1147,8 @@ void Map_LoadFile( const char *filename ){
                                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;
@@ -956,6 +1158,7 @@ void Map_LoadFile( const char *filename ){
                                if ( !format->wrongFormat ) {
                                        break;
                                }
+                               switch_format = !switch_format;
                        }
                }
 
@@ -976,6 +1179,8 @@ void Map_LoadFile( const char *filename ){
        Map_StartPosition();
 
        g_currentMap = &g_map;
+
+       Brush_switchFormat( switch_format );
 }
 
 class Excluder
@@ -993,6 +1198,7 @@ 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;
@@ -1004,6 +1210,7 @@ bool pre( scene::Node& node ) const {
        }
        return true;
 }
+
 void post( scene::Node& node ) const {
        if ( m_skip ) {
                m_skip = false;
@@ -1022,6 +1229,7 @@ public:
 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
        m_selected = false;
 }
+
 void visit( scene::Instance& instance ) const {
        Selectable* selectable = Instance_getSelectable( instance );
        if ( selectable != 0
@@ -1083,10 +1291,12 @@ 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 )
@@ -1103,6 +1313,7 @@ bool pre( scene::Node& node ) const {
                return false;
        }
 }
+
 void post( scene::Node& node ) const {
        if ( m_skip ) {
                m_skip = false;
@@ -1129,7 +1340,7 @@ void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker&
 }
 
 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
-       format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
+       format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out, g_writeMapComments );
 }
 
 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
@@ -1184,6 +1395,9 @@ void Map_RenameAbsolute( const char* 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 ){
@@ -1231,6 +1445,10 @@ void Map_New(){
        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 );
@@ -1267,7 +1485,17 @@ void ConstructRegionStartpoint( scene::Node* startpoint, const Vector3& region_m
 
    ===========================================================
  */
-bool region_active;
+bool region_active = false;
+
+ConstReferenceCaller<bool, void(const Callback<void(bool)> &), PropertyImpl<bool>::Export> g_region_caller( region_active );
+
+ToggleItem g_region_item( g_region_caller );
+
+/*void Map_ToggleRegion(){
+       region_active = !region_active;
+       g_region_item.update();
+}*/
+
 Vector3 region_mins( g_MinWorldCoord, g_MinWorldCoord, g_MinWorldCoord );
 Vector3 region_maxs( g_MaxWorldCoord, g_MaxWorldCoord, g_MaxWorldCoord );
 
@@ -1321,6 +1549,7 @@ public:
 ExcludeAllWalker( bool exclude )
        : m_exclude( exclude ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        exclude_node( path.top(), m_exclude );
 
@@ -1344,6 +1573,7 @@ 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;
@@ -1361,6 +1591,7 @@ public:
 ExcludeRegionedWalker( bool exclude )
        : m_exclude( exclude ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        exclude_node(
                path.top(),
@@ -1391,6 +1622,7 @@ void Scene_Exclude_Region( bool exclude ){
  */
 void Map_RegionOff(){
        region_active = false;
+       g_region_item.update();
 
        region_maxs[0] = g_MaxWorldCoord - 64;
        region_mins[0] = g_MinWorldCoord + 64;
@@ -1404,6 +1636,7 @@ void Map_RegionOff(){
 
 void Map_ApplyRegion( void ){
        region_active = true;
+       g_region_item.update();
 
        Scene_Exclude_Region( false );
 }
@@ -1420,6 +1653,7 @@ void Map_RegionSelectedBrushes( void ){
        if ( GlobalSelectionSystem().countSelected() != 0
                 && GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
                region_active = true;
+               g_region_item.update();
                Select_GetBounds( region_mins, region_maxs );
 
                Scene_Exclude_Selected( false );
@@ -1478,6 +1712,8 @@ void Map_RegionBrush( void ){
 bool Map_ImportFile( const char* filename ){
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
 
+       g_strLastMapFolder = g_path_get_dirname( filename );
+
        bool success = false;
 
        if ( extension_equal( path_get_extension( filename ), "bsp" ) ) {
@@ -1521,37 +1757,58 @@ bool Map_ImportFile( const char* filename ){
 
 tryDecompile:
 
-       const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
+       const char *type = GlobalRadiant().getGameDescriptionKeyValue( "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" );
+               std::string output;
+               output += AppPath_get();
+               output += "q3map2";
+               output += GDEF_OS_EXE_EXT;
+
+               output += " -v -game ";
+               output += ( type && *type ) ? type : "quake3";
+               output += " -fs_basepath \"";
+               output += EnginePath_get();
+               output += "\" -fs_homepath \"";
+               output += g_qeglobals.m_userEnginePath.c_str();
+               output += "\"";
+
+               // extra pakpaths
+               for ( int i = 0; i < g_pakPathCount; i++ ) {
+                       if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) {
+                               output += " -fs_pakpath \"";
+                               output += g_strPakPath[i].c_str();
+                               output += "\"";
+                       }
+               }
+
+               // extra switches
+               if ( g_disableEnginePath ) {
+                       output += " -fs_nobasepath ";
+               }
+
+               if ( g_disableHomePath ) {
+                       output += " -fs_nohomepath ";
+               }
+
+               output += " -fs_game ";
+               output += gamename_get();
+               output += " -convert -format ";
+               output += Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map";
                if ( extension_equal( path_get_extension( filename ), "map" ) ) {
-                       output.push_string( " -readmap " );
+                       output += " -readmap ";
                }
-               output.push_string( " \"" );
-               output.push_string( filename );
-               output.push_string( "\"" );
+               output += " \"";
+               output += filename;
+               output += "\"";
 
                // 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" );
+               output = "";
+               output.append( filename, string_length( filename ) - ( n + 1 ) );
+               output += "_converted.map";
                filename = output.c_str();
 
                // open
@@ -1581,7 +1838,13 @@ tryDecompile:
  */
 bool Map_SaveFile( const char* filename ){
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
-       return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse, filename );
+       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;
 }
 
 //
@@ -1595,40 +1858,53 @@ bool Map_SaveSelected( const char* filename ){
        return MapResource_saveFile( MapFormat_forFile( filename ), GlobalSceneGraph().root(), Map_Traverse_Selected, filename );
 }
 
-
 class ParentSelectedBrushesToEntityWalker : public scene::Graph::Walker
 {
-scene::Node& m_parent;
+       scene::Node& m_parent;
+       mutable bool m_emptyOldParent;
+
 public:
-ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
+ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ), m_emptyOldParent( false ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
-       if ( path.top().get_pointer() != &m_parent
-                && Node_isPrimitive( path.top() ) ) {
+       if ( path.top().get_pointer() != &m_parent && ( Node_isPrimitive( path.top() ) || m_emptyOldParent ) ) {
                Selectable* selectable = Instance_getSelectable( instance );
-               if ( selectable != 0
-                        && selectable->isSelected()
-                        && path.size() > 1 ) {
+               if ( selectable && 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() ) ) {
+       if ( path.top().get_pointer() == &m_parent )
+               return;
+
+       if ( Node_isPrimitive( path.top() ) ){
+               m_emptyOldParent = false;
                Selectable* selectable = Instance_getSelectable( instance );
-               if ( selectable != 0
-                        && selectable->isSelected()
-                        && path.size() > 1 ) {
+
+               if ( selectable && selectable->isSelected() && path.size() > 1 ){
                        scene::Node& parent = path.parent();
-                       if ( &parent != &m_parent ) {
+                       if ( &parent != &m_parent ){
                                NodeSmartReference node( path.top().get() );
-                               Node_getTraversable( parent )->erase( node );
+                               scene::Traversable* traversable_parent = Node_getTraversable( parent );
+                               traversable_parent->erase( node );
                                Node_getTraversable( m_parent )->insert( node );
+                               if ( traversable_parent->empty() )
+                                       m_emptyOldParent = true;
                        }
                }
        }
+       else if ( m_emptyOldParent ){
+               m_emptyOldParent = false;
+               // delete empty entities
+               Entity* entity = Node_getEntity( path.top() );
+               if ( entity != 0 && path.top().get_pointer() != Map_FindWorldspawn( g_map )     && Node_getTraversable( path.top() )->empty() ) {
+                       Path_deleteTop( path );
+               }
+       }
 }
 };
 
@@ -1644,6 +1920,7 @@ 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;
@@ -1656,6 +1933,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const {
        }
        return true;
 }
+
 void post( const scene::Path& path, scene::Instance& instance ) const {
        --m_depth;
 }
@@ -1742,6 +2020,7 @@ void Scene_parentSelected(){
 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() );
@@ -1759,7 +2038,6 @@ public:
 }
 
 
-
 void NewMap(){
        if ( ConfirmModified( "New Map" ) ) {
                Map_RegionOff();
@@ -1774,16 +2052,32 @@ const char* getMapsPath(){
        return g_mapsPath.c_str();
 }
 
+const char* getLastMapFolderPath(){
+       if (g_strLastMapFolder.empty()) {
+               GlobalPreferenceSystem().registerPreference( "LastMapFolder", make_property_string( g_strLastMapFolder ) );
+               if (g_strLastMapFolder.empty()) {
+                       StringOutputStream buffer( 1024 );
+                       buffer << getMapsPath();
+                       if ( !file_readable( buffer.c_str() ) ) {
+                               buffer.clear();
+                               buffer << g_qeglobals.m_userGamePath.c_str() << "/";
+                       }
+                       g_strLastMapFolder = buffer.c_str();
+               }
+       }
+       return g_strLastMapFolder.c_str();
+}
+
 const char* map_open( const char* title ){
-       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), true, false, false );
+       return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), true, false, false );
 }
 
 const char* map_import( const char* title ){
-       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), TRUE, title, getMapsPath(), MapFormat::Name(), false, true, false );
+       return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), false, true, false );
 }
 
 const char* map_save( const char* title ){
-       return file_dialog( GTK_WIDGET( MainFrame_getWindow() ), FALSE, title, getMapsPath(), MapFormat::Name(), false, false, true );
+       return MainFrame_getWindow().file_dialog( FALSE, title, getLastMapFolderPath(), MapFormat::Name(), false, false, true );
 }
 
 void OpenMap(){
@@ -1793,7 +2087,7 @@ void OpenMap(){
 
        const char* filename = map_open( "Open Map" );
 
-       if ( filename != 0 ) {
+       if ( filename != NULL ) {
                MRU_AddFile( filename );
                Map_RegionOff();
                Map_Free();
@@ -1804,7 +2098,7 @@ void OpenMap(){
 void ImportMap(){
        const char* filename = map_import( "Import Map" );
 
-       if ( filename != 0 ) {
+       if ( filename != NULL ) {
                UndoableCommand undo( "mapImport" );
                Map_ImportFile( filename );
        }
@@ -1813,7 +2107,8 @@ void ImportMap(){
 bool Map_SaveAs(){
        const char* filename = map_save( "Save Map" );
 
-       if ( filename != 0 ) {
+       if ( filename != NULL ) {
+               g_strLastMapFolder = g_path_get_dirname( filename );
                MRU_AddFile( filename );
                Map_Rename( filename );
                return Map_Save();
@@ -1831,13 +2126,15 @@ void SaveMap(){
        }
        else if ( Map_Modified( g_map ) ) {
                Map_Save();
+               MRU_AddFile( g_map.m_name.c_str() );    //add on saving, but not opening via cmd line: spoils the list
        }
 }
 
 void ExportMap(){
        const char* filename = map_save( "Export Selection" );
 
-       if ( filename != 0 ) {
+       if ( filename != NULL ) {
+               g_strLastMapFolder = g_path_get_dirname( filename );
                Map_SaveSelected( filename );
        }
 }
@@ -1845,7 +2142,8 @@ void ExportMap(){
 void SaveRegion(){
        const char* filename = map_save( "Export Region" );
 
-       if ( filename != 0 ) {
+       if ( filename != NULL ) {
+               g_strLastMapFolder = g_path_get_dirname( filename );
                Map_SaveRegion( filename );
        }
 }
@@ -1888,6 +2186,7 @@ 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 ) );
@@ -1904,6 +2203,7 @@ 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 ) );
@@ -1944,7 +2244,7 @@ void SelectBrush( int entitynum, int brushnum ){
 }
 
 
-class BrushFindIndexWalker : public scene::Graph::Walker
+class BrushFindIndexWalker : public scene::Traversable::Walker
 {
 mutable const scene::Node* m_node;
 std::size_t& m_count;
@@ -1952,9 +2252,10 @@ 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() ) {
+
+bool pre( scene::Node& node ) const {
+       if ( Node_isPrimitive( node ) ) {
+               if ( m_node == &node ) {
                        m_node = 0;
                }
                if ( m_node ) {
@@ -1965,7 +2266,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const {
 }
 };
 
-class EntityFindIndexWalker : public scene::Graph::Walker
+class EntityFindIndexWalker : public scene::Traversable::Walker
 {
 mutable const scene::Node* m_node;
 std::size_t& m_count;
@@ -1973,9 +2274,10 @@ 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() ) {
+
+bool pre( scene::Node& node ) const {
+       if ( Node_isEntity( node ) ) {
+               if ( m_node == &node ) {
                        m_node = 0;
                }
                if ( m_node ) {
@@ -1992,8 +2294,24 @@ static void GetSelectionIndex( int *ent, int *brush ){
        if ( GlobalSelectionSystem().countSelected() != 0 ) {
                const scene::Path& path = GlobalSelectionSystem().ultimateSelected().path();
 
-               GlobalSceneGraph().traverse( BrushFindIndexWalker( path.top(), count_brush ) );
-               GlobalSceneGraph().traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
+               {
+                       scene::Traversable* traversable = Node_getTraversable( path.parent() );
+                       if ( traversable != 0 && path.size() == 3 ) {
+                               traversable->traverse( BrushFindIndexWalker( path.top(), count_brush ) );
+                       }
+               }
+
+               {
+                       scene::Traversable* traversable = Node_getTraversable( GlobalSceneGraph().root() );
+                       if ( traversable != 0 ) {
+                               if( path.size() == 3 ){
+                                       traversable->traverse( EntityFindIndexWalker( path.parent(), count_entity ) );
+                               }
+                               else if ( path.size() == 2 ){
+                                       traversable->traverse( EntityFindIndexWalker( path.top(), count_entity ) );
+                               }
+                       }
+               }
        }
        *brush = int(count_brush);
        *ent = int(count_entity);
@@ -2001,66 +2319,58 @@ static void GetSelectionIndex( int *ent, int *brush ){
 
 void DoFind(){
        ModalDialog dialog;
-       GtkEntry* entity;
-       GtkEntry* brush;
+       ui::Entry entity{ui::null};
+       ui::Entry brush{ui::null};
 
-       GtkWindow* window = create_dialog_window( MainFrame_getWindow(), "Find Brush", G_CALLBACK( dialog_delete_callback ), &dialog );
+       ui::Window window = MainFrame_getWindow().create_dialog_window("Find Brush", G_CALLBACK(dialog_delete_callback ), &dialog );
 
-       GtkAccelGroup* accel = gtk_accel_group_new();
-       gtk_window_add_accel_group( window, accel );
+       auto accel = ui::AccelGroup(ui::New);
+       window.add_accel_group( accel );
 
        {
-               GtkVBox* vbox = create_dialog_vbox( 4, 4 );
-               gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( vbox ) );
+               auto vbox = create_dialog_vbox( 4, 4 );
+               window.add(vbox);
                {
-                       GtkTable* table = create_dialog_table( 2, 2, 4, 4 );
-                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
+                       auto table = create_dialog_table( 2, 2, 4, 4 );
+                       vbox.pack_start( 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 );
+                               ui::Widget label = ui::Label( "Entity number" );
+                               label.show();
+                (table).attach(label, {0, 1, 0, 1}, {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 );
+                               ui::Widget label = ui::Label( "Brush number" );
+                               label.show();
+                (table).attach(label, {0, 1, 1, 2}, {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 ) );
+                               auto entry = ui::Entry(ui::New);
+                               entry.show();
+                table.attach(entry, {1, 2, 0, 1}, {GTK_EXPAND | GTK_FILL, 0});
+                               gtk_widget_grab_focus( 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 );
+                               auto entry = ui::Entry(ui::New);
+                               entry.show();
+                table.attach(entry, {1, 2, 1, 2}, {GTK_EXPAND | GTK_FILL, 0});
 
                                brush = entry;
                        }
                }
                {
-                       GtkHBox* hbox = create_dialog_hbox( 4 );
-                       gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( hbox ), TRUE, TRUE, 0 );
+                       auto hbox = create_dialog_hbox( 4 );
+                       vbox.pack_start( 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 );
+                               auto button = create_dialog_button( "Find", G_CALLBACK( dialog_button_ok ), &dialog );
+                               hbox.pack_start( button, FALSE, FALSE, 0 );
+                               widget_make_default( button );
+                               gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_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 );
+                               auto button = create_dialog_button( "Close", G_CALLBACK( dialog_button_cancel ), &dialog );
+                               hbox.pack_start( button, FALSE, FALSE, 0 );
+                               gtk_widget_add_accelerator( button , "clicked", accel, GDK_KEY_Escape, (GdkModifierType)0, (GtkAccelFlags)0 );
                        }
                }
        }
@@ -2071,9 +2381,9 @@ void DoFind(){
 
        GetSelectionIndex( &ent, &br );
        sprintf( buf, "%i", ent );
-       gtk_entry_set_text( entity, buf );
+       entity.text(buf);
        sprintf( buf, "%i", br );
-       gtk_entry_set_text( brush, buf );
+       brush.text(buf);
 
        if ( modal_dialog_show( window, dialog ) == eIDOK ) {
                const char *entstr = gtk_entry_get_text( entity );
@@ -2081,11 +2391,12 @@ void DoFind(){
                SelectBrush( atoi( entstr ), atoi( brushstr ) );
        }
 
-       gtk_widget_destroy( GTK_WIDGET( window ) );
+    window.destroy();
 }
 
 void Map_constructPreferences( PreferencesPage& page ){
-       page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
+       page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap );
+       page.appendCheckBox( "", "Add entity and brush number comments on map write", g_writeMapComments );
 }
 
 
@@ -2095,6 +2406,7 @@ std::size_t m_unrealised;
 public:
 MapEntityClasses() : m_unrealised( 1 ){
 }
+
 void realise(){
        if ( --m_unrealised == 0 ) {
                if ( g_map.m_resource != 0 ) {
@@ -2103,6 +2415,7 @@ void realise(){
                }
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
                if ( g_map.m_resource != 0 ) {
@@ -2122,6 +2435,7 @@ 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" );
@@ -2131,6 +2445,7 @@ void realise(){
                g_mapsPath = buffer.c_str();
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
                g_mapsPath = "";
@@ -2140,22 +2455,22 @@ void unrealise(){
 
 MapModuleObserver g_MapModuleObserver;
 
-#include "preferencesystem.h"
-
 CopiedString g_strLastMap;
 bool g_bLoadLastMap = false;
 
 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>() );
+       GlobalCommands_insert( "RegionOff", makeCallbackF(RegionOff) );
+       GlobalCommands_insert( "RegionSetXY", makeCallbackF(RegionXY) );
+       GlobalCommands_insert( "RegionSetBrush", makeCallbackF(RegionBrush) );
+       //GlobalCommands_insert( "RegionSetSelection", makeCallbackF(RegionSelected), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
+       GlobalToggles_insert( "RegionSetSelection", makeCallbackF(RegionSelected), ToggleItem::AddCallbackCaller( g_region_item ), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
+
+       GlobalPreferenceSystem().registerPreference( "LastMap", make_property_string( g_strLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "LoadLastMap", make_property_string( g_bLoadLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "MapInfoDlg", make_property<WindowPosition_String>( g_posMapInfoWnd ) );
+       GlobalPreferenceSystem().registerPreference( "WriteMapComments", make_property_string( g_writeMapComments ) );
+
+       PreferencesDialog_addSettingsPreferences( makeCallbackF(Map_constructPreferences) );
 
        GlobalEntityClassManager().attach( g_MapEntityClasses );
        Radiant_attachHomePathsObserver( g_MapModuleObserver );