]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/mainframe.cpp
Fix GTK_CRITICAL errors
[xonotic/netradiant.git] / radiant / mainframe.cpp
index 6ae00e3d3fabb8620c15c529be2f4647efa68bfe..f39805bfd3bf09f0896abbf9cafad7415afbb59b 100644 (file)
 #include "referencecache.h"
 #include "texwindow.h"
 
+#if GDEF_OS_WINDOWS
+#include <process.h>
+#else
+#include <spawn.h>
+#endif
+
+#ifdef WORKAROUND_WINDOWS_GTK2_GLWIDGET
+/* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
+#define WORKAROUND_GOBJECT_SET_GLWIDGET(window, widget) g_object_set_data( G_OBJECT( window ), "glwidget", G_OBJECT( widget ) )
+#else
+#define WORKAROUND_GOBJECT_SET_GLWIDGET(window, widget)
+#endif
 
 struct layout_globals_t
 {
@@ -138,22 +150,25 @@ void VFS_Init(){
        GlobalFileSystem().initialise();
        g_vfsInitialized = true;
 }
+
 void VFS_Shutdown(){
        if ( !g_vfsInitialized ) return;
        GlobalFileSystem().shutdown();
        g_vfsInitialized = false;
 }
+
 void VFS_Refresh(){
        if ( !g_vfsInitialized ) return;
        GlobalFileSystem().clear();
        QE_InitVFS();
        GlobalFileSystem().refresh();
        g_vfsInitialized = true;
-       // also refresg models
+       // also refresh models
        RefreshReferences();
        // also refresh texture browser
        TextureBrowser_RefreshShaders();
 }
+
 void VFS_Restart(){
        VFS_Shutdown();
        VFS_Init();
@@ -165,6 +180,7 @@ public:
 void realise(){
        VFS_Init();
 }
+
 void unrealise(){
        VFS_Shutdown();
 }
@@ -175,6 +191,7 @@ VFSModuleObserver g_VFSModuleObserver;
 void VFS_Construct(){
        Radiant_attachHomePathsObserver( g_VFSModuleObserver );
 }
+
 void VFS_Destroy(){
        Radiant_detachHomePathsObserver( g_VFSModuleObserver );
 }
@@ -191,6 +208,7 @@ const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D
 typedef HRESULT ( WINAPI qSHGetKnownFolderPath_t )( qREFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath );
 static qSHGetKnownFolderPath_t *qSHGetKnownFolderPath;
 #endif
+
 void HomePaths_Realise(){
        do
        {
@@ -207,9 +225,7 @@ void HomePaths_Realise(){
                        }
                        path.clear();
                        path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
-#endif
-
-#if GDEF_OS_WINDOWS
+#elif GDEF_OS_WINDOWS
                        TCHAR mydocsdir[MAX_PATH + 1];
                        wchar_t *mydocsdirw;
                        HMODULE shfolder = LoadLibrary( "shfolder.dll" );
@@ -246,13 +262,19 @@ void HomePaths_Realise(){
                                        break;
                                }
                        }
-#endif
-
-#if GDEF_OS_POSIX
+#elif GDEF_OS_XDG
                        path.clear();
-                       path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
-                       g_qeglobals.m_userEnginePath = path.c_str();
-                       break;
+                       path << DirectoryCleaned( g_get_user_data_dir() ) << ( prefix + 1 ) << "/";
+                       if ( file_exists( path.c_str() ) && file_is_directory( path.c_str() ) ) {
+                               g_qeglobals.m_userEnginePath = path.c_str();
+                               break;
+                       }
+                       else {
+                               path.clear();
+                               path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
+                               g_qeglobals.m_userEnginePath = path.c_str();
+                               break;
+                       }
 #endif
                }
 
@@ -287,12 +309,14 @@ std::size_t m_unrealised;
 public:
 HomePathsModuleObserver() : m_unrealised( 1 ){
 }
+
 void realise(){
        if ( --m_unrealised == 0 ) {
                HomePaths_Realise();
                g_homePathObservers.realise();
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
                g_homePathObservers.unrealise();
@@ -305,6 +329,7 @@ HomePathsModuleObserver g_HomePathsModuleObserver;
 void HomePaths_Construct(){
        Radiant_attachEnginePathObserver( g_HomePathsModuleObserver );
 }
+
 void HomePaths_Destroy(){
        Radiant_detachEnginePathObserver( g_HomePathsModuleObserver );
 }
@@ -371,15 +396,86 @@ void setEnginePath( const char* path ){
        }
 }
 
+// Pak Path
+
+CopiedString g_strPakPath[g_pakPathCount] = { "", "", "", "", "" };
+ModuleObservers g_pakPathObservers[g_pakPathCount];
+std::size_t g_pakpath_unrealised[g_pakPathCount] = { 1, 1, 1, 1, 1 };
+
+void Radiant_attachPakPathObserver( int num, ModuleObserver& observer ){
+       g_pakPathObservers[num].attach( observer );
+}
+
+void Radiant_detachPakPathObserver( int num, ModuleObserver& observer ){
+       g_pakPathObservers[num].detach( observer );
+}
+
+
+void PakPath_Realise( int num ){
+       if ( --g_pakpath_unrealised[num] == 0 ) {
+               g_pakPathObservers[num].realise();
+       }
+}
+
+const char* PakPath_get( int num ){
+       std::string message = "PakPath_get: pak path " + std::to_string(num) + " not realised";
+       ASSERT_MESSAGE( g_pakpath_unrealised[num] == 0, message.c_str() );
+       return g_strPakPath[num].c_str();
+}
+
+void PakPath_Unrealise( int num ){
+       if ( ++g_pakpath_unrealised[num] == 1 ) {
+               g_pakPathObservers[num].unrealise();
+       }
+}
+
+void setPakPath( int num, const char* path ){
+       if (!g_strcmp0( path, "")) {
+               g_strPakPath[num] = "";
+               return;
+       }
 
-// App Path
+       StringOutputStream buffer( 256 );
+       buffer << DirectoryCleaned( path );
+       if ( !path_equal( buffer.c_str(), g_strPakPath[num].c_str() ) ) {
+               std::string message = "Changing Pak Path " + std::to_string(num);
+               ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", message.c_str() );
+
+               PakPath_Unrealise(num);
+
+               g_strPakPath[num] = buffer.c_str();
 
-CopiedString g_strAppPath;                 ///< holds the full path of the executable
+               PakPath_Realise(num);
+       }
+}
+
+
+// executable file path (full path)
+CopiedString g_strAppFilePath;
+
+// directory paths
+CopiedString g_strAppPath; 
+CopiedString g_strLibPath;
+CopiedString g_strDataPath;
+
+const char* AppFilePath_get(){
+       return g_strAppFilePath.c_str();
+}
 
 const char* AppPath_get(){
        return g_strAppPath.c_str();
 }
 
+const char *LibPath_get()
+{
+    return g_strLibPath.c_str();
+}
+
+const char *DataPath_get()
+{
+    return g_strDataPath.c_str();
+}
+
 /// the path to the local rc-dir
 const char* LocalRcPath_get( void ){
        static CopiedString rc_path;
@@ -394,6 +490,7 @@ const char* LocalRcPath_get( void ){
 /// directory for temp files
 /// NOTE: on *nix this is were we check for .pid
 CopiedString g_strSettingsPath;
+
 const char* SettingsPath_get(){
        return g_strSettingsPath.c_str();
 }
@@ -414,21 +511,98 @@ const char* GameToolsPath_get(){
        return g_strGameToolsPath.c_str();
 }
 
-void EnginePathImport( CopiedString& self, const char* value ){
+struct EnginePath {
+       static void Export(const CopiedString &self, const Callback<void(const char *)> &returnz) {
+               returnz(self.c_str());
+       }
+
+       static void Import(CopiedString &self, const char *value) {
        setEnginePath( value );
 }
-typedef ReferenceCaller<CopiedString, void(const char*), EnginePathImport> EnginePathImportCaller;
+};
+
+struct PakPath0 {
+       static void Export( const CopiedString &self, const Callback<void(const char*)> &returnz ) {
+               returnz( self.c_str() );
+       }
+
+       static void Import( CopiedString &self, const char *value ) {
+               setPakPath( 0, value );
+       }
+};
+
+struct PakPath1 {
+       static void Export( const CopiedString &self, const Callback<void(const char*)> &returnz ) {
+               returnz( self.c_str() );
+       }
+
+       static void Import( CopiedString &self, const char *value ) {
+               setPakPath( 1, value );
+       }
+};
+
+struct PakPath2 {
+       static void Export( const CopiedString &self, const Callback<void(const char*)> &returnz ) {
+               returnz( self.c_str() );
+       }
+
+       static void Import( CopiedString &self, const char *value ) {
+               setPakPath( 2, value );
+       }
+};
+
+struct PakPath3 {
+       static void Export( const CopiedString &self, const Callback<void(const char*)> &returnz ) {
+               returnz( self.c_str() );
+       }
+
+       static void Import( CopiedString &self, const char *value ) {
+               setPakPath( 3, value );
+       }
+};
+
+struct PakPath4 {
+       static void Export( const CopiedString &self, const Callback<void(const char*)> &returnz ) {
+               returnz( self.c_str() );
+       }
+
+       static void Import( CopiedString &self, const char *value ) {
+               setPakPath( 4, value );
+       }
+};
+
+bool g_disableEnginePath = false;
+bool g_disableHomePath = false;
+
+void Paths_constructBasicPreferences(  PreferencesPage& page ) {
+       page.appendPathEntry( "Engine Path", true, make_property<EnginePath>(g_strEnginePath) );
+}
 
 void Paths_constructPreferences( PreferencesPage& page ){
-       page.appendPathEntry( "Engine Path", true,
-                                                 StringImportCallback( EnginePathImportCaller( g_strEnginePath ) ),
-                                                 StringExportCallback( StringExportCaller( g_strEnginePath ) )
-                                                 );
+       Paths_constructBasicPreferences( page );
+
+       page.appendSpacer( 4 );
+       page.appendLabel( "", "Advanced options" );
+       page.appendCheckBox( "", "Do not use Engine Path", g_disableEnginePath );
+       page.appendCheckBox( "", "Do not use Home Path", g_disableHomePath );
+
+       page.appendSpacer( 4 );
+       page.appendLabel( "", "Only a very few games support Pak Paths," );
+       page.appendLabel( "", "if you don't know what it is, leave this blank." );
+
+       const char *label = "Pak Path ";
+       page.appendPathEntry( label, true, make_property<PakPath0>( g_strPakPath[0] ) );
+       page.appendPathEntry( label, true, make_property<PakPath1>( g_strPakPath[1] ) );
+       page.appendPathEntry( label, true, make_property<PakPath2>( g_strPakPath[2] ) );
+       page.appendPathEntry( label, true, make_property<PakPath3>( g_strPakPath[3] ) );
+       page.appendPathEntry( label, true, make_property<PakPath4>( g_strPakPath[4] ) );
 }
+
 void Paths_constructPage( PreferenceGroup& group ){
        PreferencesPage page( group.createPage( "Paths", "Path Settings" ) );
        Paths_constructPreferences( page );
 }
+
 void Paths_registerPreferencesPage(){
        PreferencesDialog_addSettingsPage( makeCallbackF(Paths_constructPage) );
 }
@@ -438,14 +612,14 @@ class PathsDialog : public Dialog
 {
 public:
 ui::Window BuildDialog(){
-       auto frame = create_dialog_frame( "Path settings", ui::Shadow::ETCHED_IN );
+       auto frame = create_dialog_frame( "Path Settings", ui::Shadow::ETCHED_IN );
 
        auto vbox2 = create_dialog_vbox( 0, 4 );
        frame.add(vbox2);
 
        {
-               PreferencesPage preferencesPage( *this, vbox2 );
-               Paths_constructPreferences( preferencesPage );
+               PreferencesPage page( *this, vbox2 );
+               Paths_constructBasicPreferences( page );
        }
 
        return ui::Window(create_simple_modal_dialog_window( "Engine Path Not Found", m_modal, frame ));
@@ -573,11 +747,13 @@ std::size_t m_unrealised;
 public:
 WorldspawnColourEntityClassObserver() : m_unrealised( 1 ){
 }
+
 void realise(){
        if ( --m_unrealised == 0 ) {
                SetWorldspawnColour( g_xywindow_globals.color_brushes );
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
        }
@@ -600,7 +776,7 @@ void Radiant_detachGameToolsPathObserver( ModuleObserver& observer ){
 void Radiant_Initialise(){
        GlobalModuleServer_Initialise();
 
-       Radiant_loadModulesFromRoot( AppPath_get() );
+       Radiant_loadModulesFromRoot( LibPath_get() );
 
        Preferences_Load();
 
@@ -629,7 +805,7 @@ void Radiant_Shutdown(){
 }
 
 void Exit(){
-       if ( ConfirmModified( "Exit Radiant" ) ) {
+       if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
 }
@@ -790,6 +966,53 @@ void ColorScheme_Ydnar(){
        XY_UpdateAllWindows();
 }
 
+/* color scheme to fit the GTK Adwaita Dark theme */
+void ColorScheme_AdwaitaDark()
+{
+       // SI_Colors0
+       // GlobalTextureBrowser().color_textureback
+       TextureBrowser_setBackgroundColour(GlobalTextureBrowser(), Vector3(0.25f, 0.25f, 0.25f));
+
+       // SI_Colors4
+       g_camwindow_globals.color_cameraback = Vector3(0.25f, 0.25f, 0.25f);
+       // SI_Colors12
+       g_camwindow_globals.color_selbrushes3d = Vector3(1.0f, 0.0f, 0.0f);
+       CamWnd_Update(*g_pParentWnd->GetCamWnd());
+
+       // SI_Colors1
+       g_xywindow_globals.color_gridback = Vector3(0.25f, 0.25f, 0.25f);
+       // SI_Colors2
+       g_xywindow_globals.color_gridminor = Vector3(0.21f, 0.23f, 0.23f);
+       // SI_Colors3
+       g_xywindow_globals.color_gridmajor = Vector3(0.14f, 0.15f, 0.15f);
+       // SI_Colors14
+       g_xywindow_globals.color_gridmajor_alt = Vector3(1.0f, 0.0f, 0.0f);
+       // SI_Colors6
+       g_xywindow_globals.color_gridblock = Vector3(1.0f, 1.0f, 1.0f);
+       // SI_Colors7
+       g_xywindow_globals.color_gridtext = Vector3(0.0f, 0.0f, 0.0f);
+       // ??
+       g_xywindow_globals.color_selbrushes = Vector3(1.0f, 0.0f, 0.0f);
+       // ??
+       g_xywindow_globals.color_clipper = Vector3(0.0f, 0.0f, 1.0f);
+       // SI_Colors8
+       g_xywindow_globals.color_brushes = Vector3(0.73f, 0.73f, 0.73f);
+
+       // SI_AxisColors0
+       g_xywindow_globals.AxisColorX = Vector3(1.0f, 0.0f, 0.0f);
+       // SI_AxisColors1
+       g_xywindow_globals.AxisColorY = Vector3(0.0f, 1.0f, 0.0f);
+       // SI_AxisColors2
+       g_xywindow_globals.AxisColorZ = Vector3(0.0f, 0.0f, 1.0f);
+       SetWorldspawnColour(g_xywindow_globals.color_brushes);
+       // ??
+       g_xywindow_globals.color_viewname = Vector3(0.5f, 0.0f, 0.75f);
+       XY_UpdateAllWindows();
+
+       // SI_Colors5
+       // g_entity_globals.color_entity = Vector3(0.0f, 0.0f, 0.0f);
+}
+
 typedef Callback<void(Vector3&)> GetColourCallback;
 typedef Callback<void(const Vector3&)> SetColourCallback;
 
@@ -801,6 +1024,7 @@ public:
 ChooseColour( const GetColourCallback& get, const SetColourCallback& set )
        : m_get( get ), m_set( set ){
 }
+
 void operator()(){
        Vector3 colour;
        m_get( colour );
@@ -810,16 +1034,17 @@ void operator()(){
 };
 
 
-
 void Colour_get( const Vector3& colour, Vector3& other ){
        other = colour;
 }
+
 typedef ConstReferenceCaller<Vector3, void(Vector3&), Colour_get> ColourGetCaller;
 
 void Colour_set( Vector3& colour, const Vector3& other ){
        colour = other;
        SceneChangeNotify();
 }
+
 typedef ReferenceCaller<Vector3, void(const Vector3&), Colour_set> ColourSetCaller;
 
 void BrushColour_set( const Vector3& other ){
@@ -827,6 +1052,7 @@ void BrushColour_set( const Vector3& other ){
        SetWorldspawnColour( g_xywindow_globals.color_brushes );
        SceneChangeNotify();
 }
+
 typedef FreeCaller<void(const Vector3&), BrushColour_set> BrushColourSetCaller;
 
 void ClipperColour_set( const Vector3& other ){
@@ -834,16 +1060,19 @@ void ClipperColour_set( const Vector3& other ){
        Brush_clipperColourChanged();
        SceneChangeNotify();
 }
+
 typedef FreeCaller<void(const Vector3&), ClipperColour_set> ClipperColourSetCaller;
 
 void TextureBrowserColour_get( Vector3& other ){
        other = TextureBrowser_getBackgroundColour( GlobalTextureBrowser() );
 }
+
 typedef FreeCaller<void(Vector3&), TextureBrowserColour_get> TextureBrowserColourGetCaller;
 
 void TextureBrowserColour_set( const Vector3& other ){
        TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), other );
 }
+
 typedef FreeCaller<void(const Vector3&), TextureBrowserColour_set> TextureBrowserColourSetCaller;
 
 
@@ -887,7 +1116,7 @@ ColoursMenu g_ColoursMenu;
 
 ui::MenuItem create_colours_menu(){
        auto colours_menu_item = new_sub_menu_item_with_mnemonic( "Colors" );
-       auto menu_in_menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( colours_menu_item ) ));
+       auto menu_in_menu = ui::Menu::from( gtk_menu_item_get_submenu( colours_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu_in_menu );
        }
@@ -901,6 +1130,7 @@ ui::MenuItem create_colours_menu(){
        create_menu_item_with_mnemonic( menu_3, "Q3Radiant Original", "ColorSchemeQER" );
        create_menu_item_with_mnemonic( menu_3, "Black and Green", "ColorSchemeBlackAndGreen" );
        create_menu_item_with_mnemonic( menu_3, "Maya/Max/Lightwave Emulation", "ColorSchemeYdnar" );
+       create_menu_item_with_mnemonic(menu_3, "Adwaita Dark", "ColorSchemeAdwaitaDark");
 
        menu_separator( menu_in_menu );
 
@@ -962,17 +1192,17 @@ void EntityInspector_ToggleShow(){
 }
 
 
-
 void SetClipMode( bool enable );
+
 void ModeChangeNotify();
 
 typedef void ( *ToolMode )();
+
 ToolMode g_currentToolMode = 0;
 bool g_currentToolModeSupportsComponentEditing = false;
 ToolMode g_defaultToolMode = 0;
 
 
-
 void SelectionSystem_DefaultMode(){
        GlobalSelectionSystem().SetMode( SelectionSystem::ePrimitive );
        GlobalSelectionSystem().SetComponentMode( SelectionSystem::eDefault );
@@ -999,24 +1229,24 @@ template<bool( *BoolFunction ) ( )>
 class BoolFunctionExport
 {
 public:
-static void apply( const BoolImportCallback& importCallback ){
+static void apply( const Callback<void(bool)> & importCallback ){
        importCallback( BoolFunction() );
 }
 };
 
-typedef FreeCaller<void(const BoolImportCallback&), &BoolFunctionExport<EdgeMode>::apply> EdgeModeApplyCaller;
+typedef FreeCaller<void(const Callback<void(bool)> &), &BoolFunctionExport<EdgeMode>::apply> EdgeModeApplyCaller;
 EdgeModeApplyCaller g_edgeMode_button_caller;
-BoolExportCallback g_edgeMode_button_callback( g_edgeMode_button_caller );
+Callback<void(const Callback<void(bool)> &)> g_edgeMode_button_callback( g_edgeMode_button_caller );
 ToggleItem g_edgeMode_button( g_edgeMode_button_callback );
 
-typedef FreeCaller<void(const BoolImportCallback&), &BoolFunctionExport<VertexMode>::apply> VertexModeApplyCaller;
+typedef FreeCaller<void(const Callback<void(bool)> &), &BoolFunctionExport<VertexMode>::apply> VertexModeApplyCaller;
 VertexModeApplyCaller g_vertexMode_button_caller;
-BoolExportCallback g_vertexMode_button_callback( g_vertexMode_button_caller );
+Callback<void(const Callback<void(bool)> &)> g_vertexMode_button_callback( g_vertexMode_button_caller );
 ToggleItem g_vertexMode_button( g_vertexMode_button_callback );
 
-typedef FreeCaller<void(const BoolImportCallback&), &BoolFunctionExport<FaceMode>::apply> FaceModeApplyCaller;
+typedef FreeCaller<void(const Callback<void(bool)> &), &BoolFunctionExport<FaceMode>::apply> FaceModeApplyCaller;
 FaceModeApplyCaller g_faceMode_button_caller;
-BoolExportCallback g_faceMode_button_callback( g_faceMode_button_caller );
+Callback<void(const Callback<void(bool)> &)> g_faceMode_button_callback( g_faceMode_button_caller );
 ToggleItem g_faceMode_button( g_faceMode_button_callback );
 
 void ComponentModeChanged(){
@@ -1113,6 +1343,7 @@ NodeSmartReference worldspawn;
 public:
 CloneSelected( bool d ) : doMakeUnique( d ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.size() == 1 ) {
                return true;
@@ -1134,6 +1365,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const {
 
        return true;
 }
+
 void post( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.size() == 1 ) {
                return;
@@ -1178,6 +1410,7 @@ struct AxisBase
        Vector3 x;
        Vector3 y;
        Vector3 z;
+
        AxisBase( const Vector3& x_, const Vector3& y_, const Vector3& z_ )
                : x( x_ ), y( y_ ), z( z_ ){
        }
@@ -1290,44 +1523,44 @@ void Selection_NudgeRight(){
 }
 
 
-void TranslateToolExport( const BoolImportCallback& importCallback ){
+void TranslateToolExport( const Callback<void(bool)> & importCallback ){
        importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eTranslate );
 }
 
-void RotateToolExport( const BoolImportCallback& importCallback ){
+void RotateToolExport( const Callback<void(bool)> & importCallback ){
        importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eRotate );
 }
 
-void ScaleToolExport( const BoolImportCallback& importCallback ){
+void ScaleToolExport( const Callback<void(bool)> & importCallback ){
        importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eScale );
 }
 
-void DragToolExport( const BoolImportCallback& importCallback ){
+void DragToolExport( const Callback<void(bool)> & importCallback ){
        importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eDrag );
 }
 
-void ClipperToolExport( const BoolImportCallback& importCallback ){
+void ClipperToolExport( const Callback<void(bool)> & importCallback ){
        importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip );
 }
 
-FreeCaller<void(const BoolImportCallback&), TranslateToolExport> g_translatemode_button_caller;
-BoolExportCallback g_translatemode_button_callback( g_translatemode_button_caller );
+FreeCaller<void(const Callback<void(bool)> &), TranslateToolExport> g_translatemode_button_caller;
+Callback<void(const Callback<void(bool)> &)> g_translatemode_button_callback( g_translatemode_button_caller );
 ToggleItem g_translatemode_button( g_translatemode_button_callback );
 
-FreeCaller<void(const BoolImportCallback&), RotateToolExport> g_rotatemode_button_caller;
-BoolExportCallback g_rotatemode_button_callback( g_rotatemode_button_caller );
+FreeCaller<void(const Callback<void(bool)> &), RotateToolExport> g_rotatemode_button_caller;
+Callback<void(const Callback<void(bool)> &)> g_rotatemode_button_callback( g_rotatemode_button_caller );
 ToggleItem g_rotatemode_button( g_rotatemode_button_callback );
 
-FreeCaller<void(const BoolImportCallback&), ScaleToolExport> g_scalemode_button_caller;
-BoolExportCallback g_scalemode_button_callback( g_scalemode_button_caller );
+FreeCaller<void(const Callback<void(bool)> &), ScaleToolExport> g_scalemode_button_caller;
+Callback<void(const Callback<void(bool)> &)> g_scalemode_button_callback( g_scalemode_button_caller );
 ToggleItem g_scalemode_button( g_scalemode_button_callback );
 
-FreeCaller<void(const BoolImportCallback&), DragToolExport> g_dragmode_button_caller;
-BoolExportCallback g_dragmode_button_callback( g_dragmode_button_caller );
+FreeCaller<void(const Callback<void(bool)> &), DragToolExport> g_dragmode_button_caller;
+Callback<void(const Callback<void(bool)> &)> g_dragmode_button_callback( g_dragmode_button_caller );
 ToggleItem g_dragmode_button( g_dragmode_button_callback );
 
-FreeCaller<void(const BoolImportCallback&), ClipperToolExport> g_clipper_button_caller;
-BoolExportCallback g_clipper_button_callback( g_clipper_button_caller );
+FreeCaller<void(const Callback<void(bool)> &), ClipperToolExport> g_clipper_button_caller;
+Callback<void(const Callback<void(bool)> &)> g_clipper_button_callback( g_clipper_button_caller );
 ToggleItem g_clipper_button( g_clipper_button_callback );
 
 void ToolChanged(){
@@ -1514,6 +1747,7 @@ public:
 SnappableSnapToGridSelected( float snap )
        : m_snap( snap ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.top().get().visible() ) {
                Snappable* snappable = Node_getSnappable( path.top() );
@@ -1537,6 +1771,7 @@ public:
 ComponentSnappableSnapToGridSelected( float snap )
        : m_snap( snap ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.top().get().visible() ) {
                ComponentSnappable* componentSnappable = Instance_getComponentSnappable( instance );
@@ -1569,9 +1804,11 @@ void Selection_SnapToGrid(){
 
 
 static gint qe_every_second( gpointer data ){
-       GdkModifierType mask;
+       if (g_pParentWnd == nullptr)
+               return TRUE;
 
-       gdk_window_get_pointer( 0, 0, 0, &mask );
+       GdkModifierType mask;
+       gdk_window_get_pointer( gtk_widget_get_window(g_pParentWnd->m_window), nullptr, nullptr, &mask );
 
        if ( ( mask & ( GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK ) ) == 0 ) {
                QE_CheckAutoSave();
@@ -1683,15 +1920,18 @@ void ScreenUpdates_Disable( const char* message, const char* title ){
                bool isActiveApp = MainFrame_isActiveApp();
 
                g_wait = create_wait_dialog( title, message );
-               gtk_grab_add( g_wait.m_window  );
 
                if ( isActiveApp ) {
                        g_wait.m_window.show();
+                       gtk_grab_add( g_wait.m_window  );
                        ScreenUpdates_process();
                }
        }
        else if ( g_wait.m_window.visible() ) {
                g_wait.m_label.text(message);
+               if ( GTK_IS_WINDOW(g_wait.m_window) ) {
+                       gtk_grab_add(g_wait.m_window);
+               }
                ScreenUpdates_process();
        }
        g_wait_stack.push_back( message );
@@ -1717,7 +1957,6 @@ void ScreenUpdates_Enable(){
 }
 
 
-
 void GlobalCamera_UpdateWindow(){
        if ( g_pParentWnd != 0 ) {
                CamWnd_Update( *g_pParentWnd->GetCamWnd() );
@@ -1770,17 +2009,16 @@ void ClipperChangeNotify(){
 }
 
 
-LatchedInt g_Layout_viewStyle( 0, "Window Layout" );
-LatchedBool g_Layout_enableDetachableMenus( true, "Detachable Menus" );
-LatchedBool g_Layout_enablePatchToolbar( true, "Patch Toolbar" );
-LatchedBool g_Layout_enablePluginToolbar( true, "Plugin Toolbar" );
-
+LatchedValue<int> g_Layout_viewStyle( 0, "Window Layout" );
+LatchedValue<bool> g_Layout_enableDetachableMenus( true, "Detachable Menus" );
+LatchedValue<bool> g_Layout_enablePatchToolbar( true, "Patch Toolbar" );
+LatchedValue<bool> g_Layout_enablePluginToolbar( true, "Plugin Toolbar" );
 
 
 ui::MenuItem create_file_menu(){
        // File menu
        auto file_menu_item = new_sub_menu_item_with_mnemonic( "_File" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( file_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( file_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -1820,7 +2058,7 @@ ui::MenuItem create_file_menu(){
 ui::MenuItem create_edit_menu(){
        // Edit menu
        auto edit_menu_item = new_sub_menu_item_with_mnemonic( "_Edit" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( edit_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( edit_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -1877,7 +2115,7 @@ ui::Widget g_toggle_entitylist_item{ui::null};
 ui::MenuItem create_view_menu( MainFrame::EViewStyle style ){
        // View menu
        auto view_menu_item = new_sub_menu_item_with_mnemonic( "Vie_w" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( view_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( view_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -1991,7 +2229,7 @@ ui::MenuItem create_view_menu( MainFrame::EViewStyle style ){
 ui::MenuItem create_selection_menu(){
        // Selection menu
        auto selection_menu_item = new_sub_menu_item_with_mnemonic( "M_odify" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( selection_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( selection_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2046,7 +2284,7 @@ ui::MenuItem create_selection_menu(){
 ui::MenuItem create_bsp_menu(){
        // BSP menu
        auto bsp_menu_item = new_sub_menu_item_with_mnemonic( "_Build" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( bsp_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( bsp_menu_item ) );
 
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
@@ -2066,7 +2304,7 @@ ui::MenuItem create_bsp_menu(){
 ui::MenuItem create_grid_menu(){
        // Grid menu
        auto grid_menu_item = new_sub_menu_item_with_mnemonic( "_Grid" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( grid_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( grid_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2079,7 +2317,7 @@ ui::MenuItem create_grid_menu(){
 ui::MenuItem create_misc_menu(){
        // Misc menu
        auto misc_menu_item = new_sub_menu_item_with_mnemonic( "M_isc" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( misc_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( misc_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2100,7 +2338,7 @@ ui::MenuItem create_misc_menu(){
 ui::MenuItem create_entity_menu(){
        // Brush menu
        auto entity_menu_item = new_sub_menu_item_with_mnemonic( "E_ntity" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( entity_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( entity_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2113,7 +2351,7 @@ ui::MenuItem create_entity_menu(){
 ui::MenuItem create_brush_menu(){
        // Brush menu
        auto brush_menu_item = new_sub_menu_item_with_mnemonic( "B_rush" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( brush_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( brush_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2126,7 +2364,7 @@ ui::MenuItem create_brush_menu(){
 ui::MenuItem create_patch_menu(){
        // Curve menu
        auto patch_menu_item = new_sub_menu_item_with_mnemonic( "_Curve" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( patch_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( patch_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2139,7 +2377,7 @@ ui::MenuItem create_patch_menu(){
 ui::MenuItem create_help_menu(){
        // Help menu
        auto help_menu_item = new_sub_menu_item_with_mnemonic( "_Help" );
-       auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( help_menu_item ) ));
+       auto menu = ui::Menu::from( gtk_menu_item_get_submenu( help_menu_item ) );
        if ( g_Layout_enableDetachableMenus.m_value ) {
                menu_tearoff( menu );
        }
@@ -2152,13 +2390,13 @@ ui::MenuItem create_help_menu(){
 
        create_menu_item_with_mnemonic( menu, "Bug report", makeCallbackF(OpenBugReportURL) );
        create_menu_item_with_mnemonic( menu, "Shortcuts list", makeCallbackF(DoCommandListDlg) );
-       create_menu_item_with_mnemonic( menu, "_About", makeCallbackF(DoAbout) );
+       create_menu_item_with_mnemonic( menu, "_About...", makeCallbackF(DoAbout) );
 
        return help_menu_item;
 }
 
 ui::MenuBar create_main_menu( MainFrame::EViewStyle style ){
-       auto menu_bar = ui::MenuBar(GTK_MENU_BAR( gtk_menu_bar_new() ));
+       auto menu_bar = ui::MenuBar::from( gtk_menu_bar_new() );
        menu_bar.show();
 
        menu_bar.add(create_file_menu());
@@ -2277,7 +2515,8 @@ void Select_constructToolbar( ui::Toolbar toolbar ){
 void CSG_constructToolbar( ui::Toolbar toolbar ){
        toolbar_append_button( toolbar, "CSG Subtract (SHIFT + U)", "selection_csgsubtract.png", "CSGSubtract" );
        toolbar_append_button( toolbar, "CSG Merge (CTRL + U)", "selection_csgmerge.png", "CSGMerge" );
-       toolbar_append_button( toolbar, "Hollow", "selection_makehollow.png", "CSGHollow" );
+       toolbar_append_button( toolbar, "Make Hollow", "selection_makehollow.png", "CSGMakeHollow" );
+       toolbar_append_button( toolbar, "Make Room", "selection_makeroom.png", "CSGMakeRoom" );
 }
 
 void ComponentModes_constructToolbar( ui::Toolbar toolbar ){
@@ -2305,14 +2544,14 @@ void Manipulators_constructToolbar( ui::Toolbar toolbar ){
 }
 
 ui::Toolbar create_main_toolbar( MainFrame::EViewStyle style ){
-       auto toolbar = ui::Toolbar(GTK_TOOLBAR( gtk_toolbar_new() ));
+       auto toolbar = ui::Toolbar::from( gtk_toolbar_new() );
        gtk_orientable_set_orientation( GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL );
        gtk_toolbar_set_style( toolbar, GTK_TOOLBAR_ICONS );
 
        toolbar.show();
 
        auto space = [&]() {
-               auto btn = ui::ToolItem(gtk_separator_tool_item_new());
+               auto btn = ui::ToolItem::from(gtk_separator_tool_item_new());
                btn.show();
                toolbar.add(btn);
        };
@@ -2467,6 +2706,7 @@ static gboolean notify( ui::Window window, gpointer dummy, MainWindowActive* sel
 
        return FALSE;
 }
+
 public:
 void connect( ui::Window toplevel_window ){
        toplevel_window.connect( "notify::is-active", G_CALLBACK( notify ), this );
@@ -2529,10 +2769,14 @@ MainFrame::~MainFrame(){
 
        for ( std::vector<ui::Widget>::iterator i = g_floating_windows.begin(); i != g_floating_windows.end(); ++i )
        {
+#ifndef WORKAROUND_MACOS_GTK2_DESTROY
                i->destroy();
+#endif
        }
 
+#ifndef WORKAROUND_MACOS_GTK2_DESTROY
        m_window.destroy();
+#endif
 }
 
 void MainFrame::SetActiveXY( XYWnd* p ){
@@ -2653,13 +2897,13 @@ void MainFrame::OnSleep(){
 
 
 ui::Window create_splash(){
-       ui::Window window = ui::Window( ui::window_type::TOP );
-       gtk_window_set_decorated( window, FALSE );
-       gtk_window_set_resizable( window, FALSE );
-       gtk_window_set_modal( window, TRUE );
+       auto window = ui::Window( ui::window_type::TOP );
+       gtk_window_set_decorated(window, false);
+       gtk_window_set_resizable(window, false);
+       gtk_window_set_modal(window, true);
        gtk_window_set_default_size( window, -1, -1 );
        gtk_window_set_position( window, GTK_WIN_POS_CENTER );
-       gtk_container_set_border_width( GTK_CONTAINER( window ), 0 );
+       gtk_container_set_border_width(window, 0);
 
        auto image = new_local_image( "splash.png" );
        image.show();
@@ -2689,7 +2933,7 @@ WindowPositionTracker g_posXZWnd;
 WindowPositionTracker g_posYZWnd;
 
 static gint mainframe_delete( ui::Widget widget, GdkEvent *event, gpointer data ){
-       if ( ConfirmModified( "Exit Radiant" ) ) {
+       if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
 
@@ -2863,6 +3107,8 @@ void MainFrame::Create(){
                        }
                        CamWnd_setParent( *m_pCamWnd, window );
 
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( window, CamWnd_getWidget( *m_pCamWnd ) );
+
                        g_floating_windows.push_back( window );
                }
 
@@ -2882,6 +3128,8 @@ void MainFrame::Create(){
                        }
                        XY_Top_Shown_Construct( window );
 
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXYWnd->GetWidget() );
+
                        g_floating_windows.push_back( window );
                }
 
@@ -2901,6 +3149,8 @@ void MainFrame::Create(){
 
                        XZ_Front_Shown_Construct( window );
 
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXZWnd->GetWidget() );
+
                        g_floating_windows.push_back( window );
                }
 
@@ -2920,12 +3170,16 @@ void MainFrame::Create(){
 
                        YZ_Side_Shown_Construct( window );
 
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pYZWnd->GetWidget() );
+
                        g_floating_windows.push_back( window );
                }
 
                {
                        auto frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
+
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( GroupDialog_getWindow(), TextureBrowser_getGLWidget() );
                }
 
                GroupDialog_show();
@@ -2959,6 +3213,8 @@ void MainFrame::Create(){
                {
             auto frame = create_framed_widget( TextureBrowser_constructWindow( window ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
+
+                       WORKAROUND_GOBJECT_SET_GLWIDGET( window, TextureBrowser_getGLWidget() );
                }
        }
 
@@ -3048,7 +3304,7 @@ void MainFrame::SetStatusText( CopiedString& status_text, const char* pText ){
 }
 
 void Sys_Status( const char* status ){
-       if ( g_pParentWnd != 0 ) {
+       if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetStatusText( g_pParentWnd->m_command_status, status );
        }
 }
@@ -3062,8 +3318,11 @@ int getFarClipDistance(){
 }
 
 float ( *GridStatus_getGridSize )() = GetGridSize;
+
 int ( *GridStatus_getRotateIncrement )() = getRotateIncrement;
+
 int ( *GridStatus_getFarClipDistance )() = getFarClipDistance;
+
 bool ( *GridStatus_getTextureLockEnabled )();
 
 void MainFrame::SetGridStatus(){
@@ -3077,7 +3336,7 @@ void MainFrame::SetGridStatus(){
 }
 
 void GridStatus_onTextureLockEnabledChanged(){
-       if ( g_pParentWnd != 0 ) {
+       if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetGridStatus();
        }
 }
@@ -3126,26 +3385,22 @@ void Layout_constructPreferences( PreferencesPage& page ){
                page.appendRadioIcons(
                        "Window Layout",
                        STRING_ARRAY_RANGE( layouts ),
-                       LatchedIntImportCaller( g_Layout_viewStyle ),
-                       IntExportCaller( g_Layout_viewStyle.m_latched )
+                       make_property( g_Layout_viewStyle )
                        );
        }
        page.appendCheckBox(
                "", "Detachable Menus",
-               LatchedBoolImportCaller( g_Layout_enableDetachableMenus ),
-               BoolExportCaller( g_Layout_enableDetachableMenus.m_latched )
+               make_property( g_Layout_enableDetachableMenus )
                );
        if ( !string_empty( g_pGameDescription->getKeyValue( "no_patch" ) ) ) {
                page.appendCheckBox(
                        "", "Patch Toolbar",
-                       LatchedBoolImportCaller( g_Layout_enablePatchToolbar ),
-                       BoolExportCaller( g_Layout_enablePatchToolbar.m_latched )
+                       make_property( g_Layout_enablePatchToolbar )
                        );
        }
        page.appendCheckBox(
                "", "Plugin Toolbar",
-               LatchedBoolImportCaller( g_Layout_enablePluginToolbar ),
-               BoolExportCaller( g_Layout_enablePluginToolbar.m_latched )
+               make_property( g_Layout_enablePluginToolbar )
                );
 }
 
@@ -3232,6 +3487,7 @@ void MainFrame_Construct(){
        GlobalCommands_insert( "ColorSchemeQER", makeCallbackF(ColorScheme_QER) );
        GlobalCommands_insert( "ColorSchemeBlackAndGreen", makeCallbackF(ColorScheme_Black) );
        GlobalCommands_insert( "ColorSchemeYdnar", makeCallbackF(ColorScheme_Ydnar) );
+       GlobalCommands_insert("ColorSchemeAdwaitaDark", makeCallbackF(ColorScheme_AdwaitaDark));
        GlobalCommands_insert( "ChooseTextureBackgroundColor", makeCallback( g_ColoursMenu.m_textureback ) );
        GlobalCommands_insert( "ChooseGridBackgroundColor", makeCallback( g_ColoursMenu.m_xyback ) );
        GlobalCommands_insert( "ChooseGridMajorColor", makeCallback( g_ColoursMenu.m_gridmajor ) );
@@ -3249,8 +3505,9 @@ void MainFrame_Construct(){
 
 
        GlobalCommands_insert( "CSGSubtract", makeCallbackF(CSG_Subtract), Accelerator( 'U', (GdkModifierType)GDK_SHIFT_MASK ) );
-       GlobalCommands_insert( "CSGMerge", makeCallbackF(CSG_Merge), Accelerator( 'U', (GdkModifierType)GDK_CONTROL_MASK ) );
-       GlobalCommands_insert( "CSGHollow", makeCallbackF(CSG_MakeHollow) );
+       GlobalCommands_insert( "CSGMerge", makeCallbackF(CSG_Merge), Accelerator( 'U', (GdkModifierType) GDK_CONTROL_MASK ) );
+       GlobalCommands_insert( "CSGMakeHollow", makeCallbackF(CSG_MakeHollow) );
+       GlobalCommands_insert( "CSGMakeRoom", makeCallbackF(CSG_MakeRoom) );
 
        Grid_registerCommands();
 
@@ -3283,25 +3540,25 @@ void MainFrame_Construct(){
        typedef FreeCaller<void(const Selectable&), ComponentMode_SelectionChanged> ComponentModeSelectionChangedCaller;
        GlobalSelectionSystem().addSelectionChangeCallback( ComponentModeSelectionChangedCaller() );
 
-       GlobalPreferenceSystem().registerPreference( "DetachableMenus", BoolImportStringCaller( g_Layout_enableDetachableMenus.m_latched ), BoolExportStringCaller( g_Layout_enableDetachableMenus.m_latched ) );
-       GlobalPreferenceSystem().registerPreference( "PatchToolBar", BoolImportStringCaller( g_Layout_enablePatchToolbar.m_latched ), BoolExportStringCaller( g_Layout_enablePatchToolbar.m_latched ) );
-       GlobalPreferenceSystem().registerPreference( "PluginToolBar", BoolImportStringCaller( g_Layout_enablePluginToolbar.m_latched ), BoolExportStringCaller( g_Layout_enablePluginToolbar.m_latched ) );
-       GlobalPreferenceSystem().registerPreference( "QE4StyleWindows", IntImportStringCaller( g_Layout_viewStyle.m_latched ), IntExportStringCaller( g_Layout_viewStyle.m_latched ) );
-       GlobalPreferenceSystem().registerPreference( "XYHeight", IntImportStringCaller( g_layout_globals.nXYHeight ), IntExportStringCaller( g_layout_globals.nXYHeight ) );
-       GlobalPreferenceSystem().registerPreference( "XYWidth", IntImportStringCaller( g_layout_globals.nXYWidth ), IntExportStringCaller( g_layout_globals.nXYWidth ) );
-       GlobalPreferenceSystem().registerPreference( "CamWidth", IntImportStringCaller( g_layout_globals.nCamWidth ), IntExportStringCaller( g_layout_globals.nCamWidth ) );
-       GlobalPreferenceSystem().registerPreference( "CamHeight", IntImportStringCaller( g_layout_globals.nCamHeight ), IntExportStringCaller( g_layout_globals.nCamHeight ) );
-
-       GlobalPreferenceSystem().registerPreference( "State", IntImportStringCaller( g_layout_globals.nState ), IntExportStringCaller( g_layout_globals.nState ) );
-       GlobalPreferenceSystem().registerPreference( "PositionX", IntImportStringCaller( g_layout_globals.m_position.x ), IntExportStringCaller( g_layout_globals.m_position.x ) );
-       GlobalPreferenceSystem().registerPreference( "PositionY", IntImportStringCaller( g_layout_globals.m_position.y ), IntExportStringCaller( g_layout_globals.m_position.y ) );
-       GlobalPreferenceSystem().registerPreference( "Width", IntImportStringCaller( g_layout_globals.m_position.w ), IntExportStringCaller( g_layout_globals.m_position.w ) );
-       GlobalPreferenceSystem().registerPreference( "Height", IntImportStringCaller( g_layout_globals.m_position.h ), IntExportStringCaller( g_layout_globals.m_position.h ) );
-
-       GlobalPreferenceSystem().registerPreference( "CamWnd", WindowPositionTrackerImportStringCaller( g_posCamWnd ), WindowPositionTrackerExportStringCaller( g_posCamWnd ) );
-       GlobalPreferenceSystem().registerPreference( "XYWnd", WindowPositionTrackerImportStringCaller( g_posXYWnd ), WindowPositionTrackerExportStringCaller( g_posXYWnd ) );
-       GlobalPreferenceSystem().registerPreference( "YZWnd", WindowPositionTrackerImportStringCaller( g_posYZWnd ), WindowPositionTrackerExportStringCaller( g_posYZWnd ) );
-       GlobalPreferenceSystem().registerPreference( "XZWnd", WindowPositionTrackerImportStringCaller( g_posXZWnd ), WindowPositionTrackerExportStringCaller( g_posXZWnd ) );
+       GlobalPreferenceSystem().registerPreference( "DetachableMenus", make_property_string( g_Layout_enableDetachableMenus.m_latched ) );
+       GlobalPreferenceSystem().registerPreference( "PatchToolBar", make_property_string( g_Layout_enablePatchToolbar.m_latched ) );
+       GlobalPreferenceSystem().registerPreference( "PluginToolBar", make_property_string( g_Layout_enablePluginToolbar.m_latched ) );
+       GlobalPreferenceSystem().registerPreference( "QE4StyleWindows", make_property_string( g_Layout_viewStyle.m_latched ) );
+       GlobalPreferenceSystem().registerPreference( "XYHeight", make_property_string( g_layout_globals.nXYHeight ) );
+       GlobalPreferenceSystem().registerPreference( "XYWidth", make_property_string( g_layout_globals.nXYWidth ) );
+       GlobalPreferenceSystem().registerPreference( "CamWidth", make_property_string( g_layout_globals.nCamWidth ) );
+       GlobalPreferenceSystem().registerPreference( "CamHeight", make_property_string( g_layout_globals.nCamHeight ) );
+
+       GlobalPreferenceSystem().registerPreference( "State", make_property_string( g_layout_globals.nState ) );
+       GlobalPreferenceSystem().registerPreference( "PositionX", make_property_string( g_layout_globals.m_position.x ) );
+       GlobalPreferenceSystem().registerPreference( "PositionY", make_property_string( g_layout_globals.m_position.y ) );
+       GlobalPreferenceSystem().registerPreference( "Width", make_property_string( g_layout_globals.m_position.w ) );
+       GlobalPreferenceSystem().registerPreference( "Height", make_property_string( g_layout_globals.m_position.h ) );
+
+       GlobalPreferenceSystem().registerPreference( "CamWnd", make_property<WindowPositionTracker_String>(g_posCamWnd) );
+       GlobalPreferenceSystem().registerPreference( "XYWnd", make_property<WindowPositionTracker_String>(g_posXYWnd) );
+       GlobalPreferenceSystem().registerPreference( "YZWnd", make_property<WindowPositionTracker_String>(g_posYZWnd) );
+       GlobalPreferenceSystem().registerPreference( "XZWnd", make_property<WindowPositionTracker_String>(g_posXZWnd) );
 
        {
                const char* ENGINEPATH_ATTRIBUTE =
@@ -3320,7 +3577,15 @@ void MainFrame_Construct(){
                g_strEnginePath = path.c_str();
        }
 
-       GlobalPreferenceSystem().registerPreference( "EnginePath", CopiedStringImportStringCaller( g_strEnginePath ), CopiedStringExportStringCaller( g_strEnginePath ) );
+       GlobalPreferenceSystem().registerPreference( "EnginePath", make_property_string( g_strEnginePath ) );
+
+       GlobalPreferenceSystem().registerPreference( "DisableEnginePath", make_property_string( g_disableEnginePath ) );
+       GlobalPreferenceSystem().registerPreference( "DisableHomePath", make_property_string( g_disableHomePath ) );
+
+       for ( int i = 0; i < g_pakPathCount; i++ ) {
+               std::string label = "PakPath" + std::to_string( i );
+               GlobalPreferenceSystem().registerPreference( label.c_str(), make_property_string( g_strPakPath[i] ) );
+       }
 
        g_Layout_viewStyle.useLatched();
        g_Layout_enableDetachableMenus.useLatched();
@@ -3350,8 +3615,49 @@ void MainFrame_Destroy(){
 
 
 void GLWindow_Construct(){
-       GlobalPreferenceSystem().registerPreference( "MouseButtons", IntImportStringCaller( g_glwindow_globals.m_nMouseType ), IntExportStringCaller( g_glwindow_globals.m_nMouseType ) );
+       GlobalPreferenceSystem().registerPreference( "MouseButtons", make_property_string( g_glwindow_globals.m_nMouseType ) );
 }
 
 void GLWindow_Destroy(){
 }
+
+void Radiant_Restart(){
+       // preferences are expected to be already saved in any way
+       // this is just to be sure and be future proof
+       Preferences_Save();
+
+       // this asks user for saving if map is modified
+       // user can chose to not save, it's ok
+       ConfirmModified( "Restart " RADIANT_NAME );
+
+       int status;
+
+       char *argv[ 3 ];
+       char exe_file[ 256 ];
+       char map_file[ 256 ];
+       bool with_map = false;
+
+       strncpy( exe_file, g_strAppFilePath.c_str(), 256 );
+
+       if ( !Map_Unnamed( g_map ) ) {
+               strncpy( map_file, Map_Name( g_map ), 256 );
+               with_map = true;
+       }
+
+       argv[ 0 ] = exe_file;
+       argv[ 1 ] = with_map ? map_file : NULL;
+       argv[ 2 ] = NULL;
+
+#if GDEF_OS_WINDOWS
+       status = !_spawnvpe( P_NOWAIT, exe_file, argv, environ );
+#else
+       pid_t pid;
+
+       status = posix_spawn( &pid, exe_file, NULL, NULL, argv, environ );
+#endif
+
+       // quit if radiant successfully started
+       if ( status == 0 ) {
+               gtk_main_quit();
+       }
+}