]> de.git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Merge commit '87f4eea939309a5ea1972323e237d23afdf01104' into master-merge
authorThomas Debesse <dev@illwieckz.net>
Tue, 21 Jun 2022 03:21:49 +0000 (05:21 +0200)
committerThomas Debesse <dev@illwieckz.net>
Tue, 21 Jun 2022 03:21:49 +0000 (05:21 +0200)
15 files changed:
1  2 
contrib/bobtoolz/dialogs/dialogs-gtk.cpp
contrib/bobtoolz/funchandlers-GTK.cpp
contrib/brushexport/interface.cpp
contrib/brushexport/plugin.cpp
contrib/brushexport/plugin.h
contrib/gtkgensurf/plugin.cpp
contrib/prtview/ConfigDialog.cpp
contrib/prtview/prtview.cpp
contrib/prtview/prtview.h
radiant/camwindow.cpp
radiant/mainframe.cpp
radiant/mainframe.h
radiant/preferences.cpp
radiant/xywindow.cpp
radiant/xywindow.h

index a5732ff5cb30b00b87a9dd79221bb8905f0680ff,ad4446795e45386b418ad7f697df927d55ccefe4..be0e4add1c7f463d4e9ec3dace5007318cecbbd3
  #include <list>
  #include <gtk/gtk.h>
  #include "gtkutil/pointer.h"
 +#include "gtkutil/dialog.h"
  
  #include "../lists.h"
  #include "../misc.h"
  
+ #include "../bobToolz-GTK.h"
  
  /*--------------------------------
          Callback Functions
@@@ -91,7 -92,7 +93,7 @@@ static void dialog_button_callback( ui:
        *ret = (EMessageBoxReturn)gpointer_to_int( data );
  }
  
 -static gint dialog_delete_callback( ui::Widget widget, GdkEvent* event, gpointer data ){
 +static gint custom_dialog_delete_callback( ui::Widget widget, GdkEvent* event, gpointer data ){
        widget.hide();
        int *loop = (int *) g_object_get_data(G_OBJECT(widget), "loop");
        *loop = 0;
@@@ -211,7 -212,8 +213,8 @@@ EMessageBoxReturn DoMessageBox( const c
        int loop = 1;
  
        auto window = ui::Window( ui::window_type::TOP );
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
+       gtk_window_set_transient_for( GTK_WINDOW( window ), GTK_WINDOW( g_pRadiantWnd ) );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
        gtk_window_set_title( window, lpCaption );
        gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
                ret = eIDNO;
        }
  
-       gtk_window_set_position( window, GTK_WIN_POS_CENTER );
+       gtk_window_set_position( GTK_WINDOW( window ),GTK_WIN_POS_CENTER_ON_PARENT );
        window.show();
        gtk_grab_add( window );
  
@@@ -313,7 -315,7 +316,7 @@@ EMessageBoxReturn DoIntersectBox( Inter
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Intersect" );
@@@ -405,8 -407,9 +408,9 @@@ EMessageBoxReturn DoPolygonBox( Polygon
        int loop = 1;
  
        auto window = ui::Window( ui::window_type::TOP );
+       gtk_window_set_transient_for( GTK_WINDOW( window ), GTK_WINDOW( g_pRadiantWnd ) );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Polygon Builder" );
  
        // ---- /vbox ----
  
-       gtk_window_set_position( window, GTK_WIN_POS_CENTER );
+       gtk_window_set_position( GTK_WINDOW( window ),GTK_WIN_POS_CENTER_ON_PARENT );
+       gtk_window_set_modal( GTK_WINDOW( window ), TRUE );
        window.show();
        gtk_grab_add( window );
  
@@@ -574,7 -578,7 +579,7 @@@ EMessageBoxReturn DoBuildStairsBox( Bui
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Stair Builder" );
@@@ -824,7 -828,7 +829,7 @@@ EMessageBoxReturn DoDoorsBox( DoorRS* r
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Door Builder" );
  //-djbob
  }
  
 -EMessageBoxReturn DoPathPlotterBox( PathPlotterRS* rs ){
 +EMessageBoxReturn DoPathPlotterBox( PathPlotterRS* rs, ui::Window main_window ){
        ui::Widget w{ui::null};
 +      ModalDialog dialog;
  
        EMessageBoxReturn ret;
        int loop = 1;
  
 -      auto window = ui::Window( ui::window_type::TOP );
 +      auto window = main_window.create_dialog_window( "Path Plotter", G_CALLBACK( custom_dialog_delete_callback ), &dialog );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
 -      gtk_window_set_title( window, "Texture Reset" );
        gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
  
        g_object_set_data( G_OBJECT( window ), "loop", &loop );
  
        gtk_widget_realize( window );
  
 -
 -
        auto vbox = ui::VBox( FALSE, 10 );
        window.add(vbox);
        vbox.show();
@@@ -1202,7 -1209,7 +1207,7 @@@ EMessageBoxReturn DoCTFColourChangeBox(
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "CTF Colour Changer" );
        return ret;
  }
  
 -EMessageBoxReturn DoResetTextureBox( ResetTextureRS* rs ){
 +EMessageBoxReturn DoResetTextureBox( ResetTextureRS* rs, ui::Window main_window ){
        Str texSelected;
  
        ui::Widget w{ui::null};
 +      ModalDialog dialog;
  
        EMessageBoxReturn ret;
        int loop = 1;
  
 -      auto window = ui::Window( ui::window_type::TOP );
 +      auto window = main_window.create_dialog_window( "Texture Reset", G_CALLBACK( custom_dialog_delete_callback ), &dialog );
+       gtk_window_set_transient_for( GTK_WINDOW( window ), GTK_WINDOW( g_pRadiantWnd ) );
+       gtk_window_set_modal( GTK_WINDOW( window ), TRUE );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
 -      gtk_window_set_title( window, "Texture Reset" );
        gtk_container_set_border_width( GTK_CONTAINER( window ), 10 );
  
        g_object_set_data( G_OBJECT( window ), "loop", &loop );
  
        // ---- /vbox ----
  
+       gtk_window_set_position( GTK_WINDOW( window ),GTK_WIN_POS_CENTER_ON_PARENT );
        window.show();
        gtk_grab_add( window );
  
@@@ -1567,7 -1578,7 +1575,7 @@@ EMessageBoxReturn DoTrainThingBox( Trai
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Train Thing" );
@@@ -1802,7 -1813,7 +1810,7 @@@ EMessageBoxReturn DoMakeChainBox( MakeC
  
        auto window = ui::Window( ui::window_type::TOP );
  
 -      window.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
 +      window.connect( "delete_event", G_CALLBACK( custom_dialog_delete_callback ), NULL );
        window.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
  
        gtk_window_set_title( window, "Make Chain" );
index decbf8ad49825f1ae49404e33ff484b97100bbb0,4436afbe5b995d32c724aa9196e06476c3554991..b6ef6ff2d3b1a90d134685e3d9f26d08a6c8803d
@@@ -202,7 -202,7 +202,7 @@@ void DoFixBrushes()
        globalOutputStream() << "bobToolz FixBrushes: " << count << " invalid/duplicate planes removed.\n";
  }
  
 -void DoResetTextures(){
 +void DoResetTextures( ui::Window main_window ){
        UndoableCommand undo( "bobToolz.resetTextures" );
        static ResetTextureRS rs;
  
        }
  
        EMessageBoxReturn ret;
 -      if ( ( ret = DoResetTextureBox( &rs ) ) == eIDCANCEL ) {
 +      if ( ( ret = DoResetTextureBox( &rs, main_window ) ) == eIDCANCEL ) {
                return;
        }
  
@@@ -362,10 -362,10 +362,10 @@@ void DoBuildDoors()
        }
  }
  
 -void DoPathPlotter(){
 +void DoPathPlotter( ui::Window main_window ){
        UndoableCommand undo( "bobToolz.pathPlotter" );
        PathPlotterRS rs;
 -      EMessageBoxReturn ret = DoPathPlotterBox( &rs );
 +      EMessageBoxReturn ret = DoPathPlotterBox( &rs, main_window );
        if ( ret == eIDCANCEL ) {
                return;
        }
@@@ -463,7 -463,7 +463,7 @@@ void DoMergePatches()
        merge_info = mrgPatches[0].IsMergable( &mrgPatches[1] );
  
        if ( merge_info.mergable ) {
              globalOutputStream() << merge_info.pos1 << " " <<  merge_info.pos2;
//            globalOutputStream() << merge_info.pos1 << " " <<  merge_info.pos2;
                //Message removed, No tools give feedback on success.
                //globalOutputStream() << "bobToolz MergePatches: Patches Mergable.\n";
                DPatch* newPatch = mrgPatches[0].MergePatches( merge_info, &mrgPatches[0], &mrgPatches[1] );
                }
                else
                {
+                       newPatch->BuildInRadiant( patches[0]->path().parent().get_pointer() );
+                       scene::Instance& parent = *( patches[1]->parent() );
                        Path_deleteTop( patches[0]->path() );
                        Path_deleteTop( patches[1]->path() );
+                       Entity* entity = Node_getEntity( parent.path().top() );
+                       if ( entity != 0
+                               && Node_getTraversable( parent.path().top() )->empty() ) {
+                               Path_deleteTop( parent.path() );
+                       }
  
-                       newPatch->BuildInRadiant();
                        delete newPatch;
                }
        }
@@@ -519,7 -526,7 +526,7 @@@ void DoSplitPatch() 
  
        std::list<DPatch> patchList = patch.Split();
        for ( std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++ ) {
-               ( *patches ).BuildInRadiant();
+               ( *patches ).BuildInRadiant( instance.path().parent().get_pointer() );
        }
  
        Path_deleteTop( instance.path() );
@@@ -549,7 -556,7 +556,7 @@@ void DoSplitPatchCols() 
  
        std::list<DPatch> patchList = patch.SplitCols();
        for ( std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++ ) {
-               ( *patches ).BuildInRadiant();
+               ( *patches ).BuildInRadiant( instance.path().parent().get_pointer() );
        }
  
        Path_deleteTop( instance.path() );
@@@ -579,7 -586,7 +586,7 @@@ void DoSplitPatchRows() 
  
        std::list<DPatch> patchList = patch.SplitRows();
        for ( std::list<DPatch>::iterator patches = patchList.begin(); patches != patchList.end(); patches++ ) {
-               ( *patches ).BuildInRadiant();
+               ( *patches ).BuildInRadiant( instance.path().parent().get_pointer() );
        }
  
        Path_deleteTop( instance.path() );
index 068325ea2bbf88b6a56d00026bfaa5806e162822,ec661495ede3ee4a76e9b0add6a031b9686588bb..ead267890a0326d55f109f274e996afe93cf0bf1
@@@ -4,7 -4,7 +4,8 @@@
  #include "debugging/debugging.h"
  #include "callbacks.h"
  #include "support.h"
 +#include "gtkutil/dialog.h"
+ #include "plugin.h"
  
  #define GLADE_HOOKUP_OBJECT( component,widget,name ) \
        g_object_set_data_full( G_OBJECT( component ), name, \
        g_object_set_data( G_OBJECT( component ), name, (void *) widget )
  
  // created by glade
 -ui::Widget create_w_plugplug2( void ){
 +ui::Widget create_w_plugplug2( ui::Window main_window ){
        GSList *r_collapse_group = NULL;
 +      ModalDialog dialog;
  
 -      auto w_plugplug2 = ui::Window( ui::window_type::TOP );
 +      auto w_plugplug2 = main_window.create_dialog_window( "BrushExport", G_CALLBACK( dialog_delete_callback ), &dialog );
        gtk_widget_set_name( w_plugplug2, "w_plugplug2" );
 -      gtk_window_set_title( w_plugplug2, "BrushExport-Plugin 3.0 by namespace" );
+       gtk_window_set_position( GTK_WINDOW( w_plugplug2 ), GTK_WIN_POS_CENTER_ON_PARENT );
+       gtk_window_set_transient_for( GTK_WINDOW( w_plugplug2 ), GTK_WINDOW( g_pRadiantWnd ) );
        gtk_window_set_destroy_with_parent( w_plugplug2, TRUE );
  
        auto vbox1 = ui::VBox( FALSE, 0 );
  ui::Widget g_brushexp_window{ui::null};
  
  // spawn plugin window (and make sure it got destroyed first or never created)
 -void CreateWindow( void ){
 +void CreateWindow( ui::Window main_window ){
        ASSERT_TRUE( !g_brushexp_window );
  
 -      ui::Widget wnd = create_w_plugplug2();
 +      ui::Widget wnd = create_w_plugplug2( main_window );
  
        // column & renderer
      auto col = ui::TreeViewColumn::from(gtk_tree_view_column_new());
index 3bef9d5a713636003c2590cbdb82c578bbc67e9d,77c54c7e1f1b7afc661e692615582c4d32a10ff0..9b40fd69327bd8665f80c3442ca526382debfa9c
  
  #include "typesystem.h"
  
 -void CreateWindow( void );
 +#define CMD_ABOUT "About..."
 +
 +void CreateWindow( ui::Window main_window );
  void DestroyWindow( void );
  bool IsWindowOpen( void );
  
+ ui::Widget g_pRadiantWnd{ui::null};
  namespace BrushExport
  {
  ui::Window g_mainwnd{ui::null};
  
  const char* init( void* hApp, void* pMainWidget ){
        g_mainwnd = ui::Window::from(pMainWidget);
+       g_pRadiantWnd = ui::Window::from(pMainWidget);
        ASSERT_TRUE( g_mainwnd );
        return "";
  }
@@@ -62,34 -63,24 +65,34 @@@ const char* getName()
        return "Brush export Plugin";
  }
  const char* getCommandList(){
 -      return "Export selected as Wavefront Object;About";
 +      return CMD_ABOUT ";-;Export selected as Wavefront Object";
  }
  const char* getCommandTitleList(){
        return "";
  }
  
  void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush ){
 -      if ( string_equal( command, "About" ) ) {
 -              GlobalRadiant().m_pfnMessageBox( g_mainwnd, "Brushexport plugin v 2.0 by namespace (www.codecreator.net)\n"
 -                                                                                                                                "Enjoy!\n\nSend feedback to spam@codecreator.net", "About me...",
 -                                                                               eMB_OK,
 -                                                                               eMB_ICONDEFAULT );
 +      if ( string_equal( command, CMD_ABOUT ) ) {
 +              const char *label_text =
 +                      PLUGIN_NAME " " PLUGIN_VERSION " for "
 +                      RADIANT_NAME " " RADIANT_VERSION "\n\n"
 +                      "Written by namespace <spam@codecreator.net>\n\n"
 +//                    20200404 dead link
 +//                    "http://www.codecreator.net"
 +                      "Built against "
 +                      RADIANT_NAME " " RADIANT_VERSION_STRING "\n"
 +                      __DATE__;
 +
 +              GlobalRadiant().m_pfnMessageBox( g_mainwnd, label_text,
 +                                                                              "About " PLUGIN_NAME,
 +                                                                              eMB_OK,
 +                                                                              eMB_ICONDEFAULT );
        }
        else if ( string_equal( command, "Export selected as Wavefront Object" ) ) {
                if ( IsWindowOpen() ) {
                        DestroyWindow();
                }
 -              CreateWindow();
 +              CreateWindow( g_mainwnd );
        }
  }
  }
@@@ -113,7 -104,7 +116,7 @@@ class BrushExportModule : public TypeSy
  _QERPluginTable m_plugin;
  public:
  typedef _QERPluginTable Type;
 -STRING_CONSTANT( Name, "brushexport2" );
 +STRING_CONSTANT( Name, PLUGIN_NAME );
  
  BrushExportModule(){
        m_plugin.m_pfnQERPlug_Init = &BrushExport::init;
index a2dc6cb63edc734f2af1377da84862ecde7d85f5,b0f4b05861e6c89eb7f11c5bd8702baff9afa0e9..5808cd98d8578a1ad9977dc5302905c1d69e5896
  #if !defined( INCLUDED_BRUSH_EXPORT_H )
  #define INCLUDED_BRUSH_EXPORT_H
  
+ #include <uilib/uilib.h>
 +#define PLUGIN_NAME "BrushExport"
 +#define PLUGIN_VERSION "2.0"
 +
+ extern ui::Widget g_pRadiantWnd;
  #endif
index 3af80d63a98292025e6dd70178190234ab4e8ded,928bd1458f6bf68b47a75946c8f4c2024e455501..6875476bb13c9983e9bd7ddcb9b5eadda9363870
@@@ -17,6 -17,8 +17,8 @@@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
+ #include <uilib/uilib.h>
  #include "gensurf.h"
  
  // Global plugin FuncTable
@@@ -32,7 -34,7 +34,7 @@@ bool g_bInitDone
  #include "iplugin.h"
  
  const char* QERPlug_Init( void* hApp, void* pMainWidget ){
-       g_pRadiantWnd = (GtkWidget*)pMainWidget;
+       g_pRadiantWnd = ui::Window::from(pMainWidget);
  
        return "GenSurf for Q3Radiant";
  }
@@@ -197,6 -199,8 +199,6 @@@ bool GenSurfSynapseClient::RequestAPI( 
        return false;
  }
  
 -#include "version.h"
 -
  const char* GenSurfSynapseClient::GetInfo(){
        return "GtkGenSurf - built " __DATE__ " " RADIANT_VERSION;
  }
index 688a66b18f20223bf611af831e12dee113208642,3b3e80c05b8d1b4b52041ce984f1df9411ebf6f4..d030b55ee327bfcb05ad012c8c777c2b4cd6f1be
@@@ -21,7 -21,6 +21,7 @@@
  #include <stdio.h>
  #include <gtk/gtk.h>
  #include <uilib/uilib.h>
 +#include "gtkutil/dialog.h"
  #include "gtkutil/pointer.h"
  
  #include "iscenegraph.h"
@@@ -29,8 -28,6 +29,6 @@@
  #include "prtview.h"
  #include "portals.h"
  
- ui::Window config_dialog{ui::null};
  static void dialog_button_callback( ui::Widget widget, gpointer data ){
        int *loop, *ret;
  
@@@ -42,7 -39,7 +40,7 @@@
        *ret = gpointer_to_int( data );
  }
  
 -static gint dialog_delete_callback( ui::Widget widget, GdkEvent* event, gpointer data ){
 +static gint custom_dialog_delete_callback( ui::Widget widget, GdkEvent* event, gpointer data ){
        widget.hide();
        int *loop = (int *) g_object_get_data(G_OBJECT(widget), "loop");
        *loop = 0;
@@@ -61,7 -58,10 +59,9 @@@ static int DoColor( PackedColour *c )
        clr.green = (guint16) (GetBValue(*c) * (65535 / 255));
  
        auto dlg = ui::Widget::from(gtk_color_selection_dialog_new( "Choose Color" ));
-       gtk_window_set_transient_for( GTK_WINDOW( dlg ), config_dialog );
 -
+       gtk_window_set_transient_for( GTK_WINDOW( dlg ), GTK_WINDOW( g_pRadiantWnd ) );
+       gtk_window_set_position( GTK_WINDOW( dlg ),GTK_WIN_POS_CENTER_ON_PARENT );
+       gtk_window_set_modal( GTK_WINDOW( dlg ), TRUE );
        gtk_color_selection_set_current_color( GTK_COLOR_SELECTION( gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(dlg)) ), &clr );
        dlg.connect( "delete_event", G_CALLBACK( dialog_delete_callback ), NULL );
        dlg.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
@@@ -237,16 -237,22 +237,18 @@@ static void OnClip(ui::Widget widget, g
        SceneChangeNotify();
  }
  
 -void DoConfigDialog(){
 +void DoConfigDialog( ui::Window main_window ){
        int loop = 1, ret = IDCANCEL;
 +      ModalDialog dialog;
  
-       auto dlg = main_window.create_dialog_window( "Portal Viewer Configuration", G_CALLBACK( custom_dialog_delete_callback ), &dialog );
-       
-       dlg.connect( "destroy", G_CALLBACK( gtk_widget_destroy ), NULL );
+       auto dlg = ui::Window( ui::window_type::TOP );
 -
 -      gtk_window_set_transient_for( GTK_WINDOW( dlg ), GTK_WINDOW( g_pRadiantWnd ) );
 -      gtk_window_set_position( GTK_WINDOW( dlg ),GTK_WIN_POS_CENTER_ON_PARENT );
 -      gtk_window_set_modal( GTK_WINDOW( dlg ), TRUE );
 -
+       gtk_window_set_title( dlg, "Portal Viewer Configuration" );
+       dlg.connect( "delete_event",
+                                               G_CALLBACK( dialog_delete_callback ), NULL );
+       dlg.connect( "destroy",
+                                               G_CALLBACK( gtk_widget_destroy ), NULL );
        g_object_set_data( G_OBJECT( dlg ), "loop", &loop );
        g_object_set_data( G_OBJECT( dlg ), "ret", &ret );
-       config_dialog = dlg;
  
        auto vbox = ui::VBox( FALSE, 5 );
        vbox.show();
index 14aeec1325bb80feec981cda000f8170d6c21bb0,fe6bf41a8ba47798c57c137c5a5c6aa1d22daa2d..b99d2086f66b3a37cce1e7be0320ddd1b942f598
@@@ -22,6 -22,8 +22,6 @@@
  #include <stdio.h>
  #include <stdlib.h>
  
 -#include <uilib/uilib.h>
 -
  #include "profile/profile.h"
  
  #include "qerplugin.h"
  #include "stream/stringstream.h"
  
  #include "portals.h"
 -#include "AboutDialog.h"
  #include "ConfigDialog.h"
  #include "LoadPortalFileDialog.h"
  
  #define Q3R_CMD_SPLITTER "-"
 -#define Q3R_CMD_ABOUT "About Portal Viewer"
 +#define Q3R_CMD_ABOUT "About..."
  #define Q3R_CMD_LOAD "Load .prt file"
  #define Q3R_CMD_RELEASE "Unload .prt file"
  #define Q3R_CMD_SHOW_3D "Toggle portals (3D)"
  #define Q3R_CMD_SHOW_2D "Toggle portals (2D)"
 -#define Q3R_CMD_OPTIONS "Configure Portal Viewer"
 +#define Q3R_CMD_OPTIONS "Configure..."
  
  CopiedString INIfn;
  
@@@ -65,7 -68,6 +65,7 @@@ const char *TRANS_3D = "Transparency"
  const char *CLIP_RANGE = "ClipRange";
  const char *CLIP = "Clip";
  
 +static ui::Window main_window{ui::null};
  
  void PrtView_construct(){
        StringOutputStream tmp( 64 );
@@@ -185,6 -187,9 +185,6 @@@ void INISetInt( const char *key, int va
        save_var( INIfn.c_str(), CONFIG_SECTION, key, s );
  }
  
 -
 -// plugin name
 -static const char *PLUGIN_NAME = "Portal Viewer";
  // commands in the menu
  static const char *PLUGIN_COMMANDS =
        Q3R_CMD_ABOUT ";"
        Q3R_CMD_LOAD;
  
  
+ ui::Widget g_pRadiantWnd{ui::null};
  
  const char* QERPlug_Init( void *hApp, void* pMainWidget ){
 -      return "Portal Viewer for Q3Radiant";
+       g_pRadiantWnd = ui::Window::from(pMainWidget);
 +      main_window = ui::Window::from(pMainWidget);
 +      ASSERT_TRUE( main_window );
 +
 +      return PLUGIN_NAME " for " RADIANT_NAME;
  }
  
  const char* QERPlug_GetName(){
@@@ -224,19 -228,7 +226,19 @@@ void QERPlug_Dispatch( const char* p, f
        globalOutputStream() << MSG_PREFIX "Command \"" << p << "\"\n";
  
        if ( !strcmp( p,Q3R_CMD_ABOUT ) ) {
 -              DoAboutDlg();
 +              const char *label_text =
 +                              PLUGIN_NAME " " PLUGIN_VERSION " for "
 +                              RADIANT_NAME " " RADIANT_VERSION "\n\n"
 +                              "Gtk port by Leonardo Zide <leo@lokigames.com>\n"
 +                              "Written by Geoffrey DeWan <gdewan@prairienet.org>\n\n"
 +                              "Built against "
 +                              RADIANT_NAME " " RADIANT_VERSION_STRING "\n"
 +                              __DATE__;
 +
 +              GlobalRadiant().m_pfnMessageBox( main_window, label_text,
 +                                                                              "About " PLUGIN_NAME,
 +                                                                              eMB_OK,
 +                                                                              eMB_ICONDEFAULT );
        }
        else if ( !strcmp( p,Q3R_CMD_LOAD ) ) {
                if ( DoLoadPortalFileDialog() == IDOK ) {
                }
        }
        else if ( !strcmp( p,Q3R_CMD_OPTIONS ) ) {
 -              DoConfigDialog();
 +              DoConfigDialog( main_window );
                SaveConfig();
  
                SceneChangeNotify();
@@@ -306,7 -298,7 +308,7 @@@ class PrtViewPluginModul
  _QERPluginTable m_plugin;
  public:
  typedef _QERPluginTable Type;
 -STRING_CONSTANT( Name, "prtview" );
 +STRING_CONSTANT( Name, PLUGIN_NAME );
  
  PrtViewPluginModule(){
        m_plugin.m_pfnQERPlug_Init = QERPlug_Init;
index e6942cc99ea3811b0ad7b6bd94c407c9b7092f68,96b77b5dcaf43412b75b654035a91c95c65994eb..0028210252742d41e6665b182525ab2dd36fd7db
  #if !defined( INCLUDED_PRTVIEW_H )
  #define INCLUDED_PRTVIEW_H
  
 -#define MSG_PREFIX "Portal Viewer plugin: "
+ #include <uilib/uilib.h>
 +#define PLUGIN_NAME "Portal Viewer"
 +#define PLUGIN_VERSION "1.0"
 +
 +#define MSG_PREFIX PLUGIN_NAME " plugin: "
  
  void InitInstance();
  void SaveConfig();
@@@ -31,6 -30,8 +33,8 @@@
  int INIGetInt( const char *key, int def );
  void INISetInt( const char *key, int val, const char *comment = 0 );
  
+ extern ui::Widget g_pRadiantWnd;
  const int IDOK                = 1;
  const int IDCANCEL            = 2;
  
diff --combined radiant/camwindow.cpp
index 33316edc4c49ac685559164903a4441be62bfe72,eafc1da093d41d8bf1044178492d1baefc792c74..97a30887531c586bdc7d36b4bb868c24d361fc8e
@@@ -76,7 -76,6 +76,7 @@@ void CameraMovedNotify()
  
  struct camwindow_globals_private_t
  {
 +      int m_nFOV;
        int m_nMoveSpeed;
        bool m_bCamLinkSpeed;
        int m_nAngleSpeed;
@@@ -87,7 -86,6 +87,7 @@@
        int m_nStrafeMode;
  
        camwindow_globals_private_t() :
 +              m_nFOV( 110 ),
                m_nMoveSpeed( 100 ),
                m_bCamLinkSpeed( true ),
                m_nAngleSpeed( 3 ),
@@@ -153,6 -151,8 +153,6 @@@ struct camera_
        guint m_keymove_handler;
  
  
 -      float fieldOfView;
 -
        DeferredMotionDelta m_mouseMove;
  
        static void motionDelta( int x, int y, void* data ){
                origin( 0, 0, 0 ),
                angles( 0, 0, 0 ),
                color( 0, 0, 0 ),
 +              projection( g_matrix4_identity ),
 +              modelview( g_matrix4_identity ),
                movementflags( 0 ),
 +              m_keycontrol_timer(),
                m_keymove_handler( 0 ),
 -              fieldOfView( 110.0f ),
                m_mouseMove( motionDelta, this ),
                m_view( view ),
                m_update( update ){
@@@ -204,7 -202,7 +204,7 @@@ float Camera_getFarClipPlane( camera_t
  
  void Camera_updateProjection( camera_t& camera ){
        float farClip = Camera_getFarClipPlane( camera );
 -      camera.projection = projection_for_camera( farClip / 4096.0f, farClip, camera.fieldOfView, camera.width, camera.height );
 +      camera.projection = projection_for_camera( farClip / 4096.0f, farClip, (float)g_camwindow_globals_private.m_nFOV, camera.width, camera.height );
  
        camera.m_view->Construct( camera.projection, camera.modelview, camera.width, camera.height );
  }
@@@ -529,9 -527,6 +529,9 @@@ typedef ReferenceCaller<camera_t, void(
  typedef ReferenceCaller<camera_t, void(), &Camera_MoveDown_KeyUp> FreeMoveCameraMoveDownKeyUpCaller;
  
  
 +const float MIN_FOV = 60;
 +const float MAX_FOV = 179;
 +const float FOV_STEP = 10;
  const float SPEED_MOVE = 32;
  const float SPEED_TURN = 22.5;
  const float MIN_CAM_SPEED = 10;
@@@ -711,7 -706,7 +711,7 @@@ static void releaseStates()
  
  camera_t& getCamera(){
        return m_Camera;
- };
+ }
  
  void BenchMark();
  void Cam_ChangeFloor( bool up );
@@@ -1302,7 -1297,7 +1302,7 @@@ void CamWnd::Cam_PositionDrag()
                CamWnd_Update( camwnd );
                CameraMovedNotify();
  
 -              Sys_SetCursorPos( m_parent, m_PositionDragCursorX, m_PositionDragCursorY );
 +              Sys_SetCursorPos( m_gl_widget, m_PositionDragCursorX, m_PositionDragCursorY );
        }
  }
  #endif
@@@ -1327,9 -1322,7 +1327,9 @@@ void CamWnd::EnableFreeMove()
  
        gtk_window_set_focus( m_parent, m_gl_widget );
        m_freemove_handle_focusout = m_gl_widget.connect( "focus_out_event", G_CALLBACK( camwindow_freemove_focusout ), this );
 -      m_freezePointer.freeze_pointer( m_parent, m_gl_widget, Camera_motionDelta, &m_Camera );
 +      /* We chose to replace m_parent by m_gl_widget but NetRadiantCustom does:
 +      m_freezePointer.freeze_pointer( m_parent, m_gl_widget, Camera_motionDelta, &m_Camera ); */
 +      m_freezePointer.freeze_pointer( m_gl_widget, Camera_motionDelta, &m_Camera );
  
        CamWnd_Update( *this );
  }
@@@ -1344,8 -1337,7 +1344,8 @@@ void CamWnd::DisableFreeMove()
        CamWnd_Remove_Handlers_FreeMove( *this );
        CamWnd_Add_Handlers_Move( *this );
  
 -      m_freezePointer.unfreeze_pointer( m_parent, true );
 +      m_freezePointer.unfreeze_pointer( m_gl_widget, true );
 +
        g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_freemove_handle_focusout );
  
        CamWnd_Update( *this );
@@@ -1399,11 -1391,9 +1399,11 @@@ void PopState()
        m_state_stack.pop_back();
  }
  void Highlight( EHighlightMode mode, bool bEnable = true ){
 -      ( bEnable )
 -      ? m_state_stack.back().m_highlight |= mode
 -                                                                                : m_state_stack.back().m_highlight &= ~mode;
 +      if ( bEnable ) {
 +              m_state_stack.back().m_highlight |= mode;
 +      } else {
 +              m_state_stack.back().m_highlight &= ~mode;
 +      }
  }
  void setLights( const LightList& lights ){
        m_state_stack.back().m_lights = &lights;
@@@ -1642,7 -1632,7 +1642,7 @@@ void CamWnd::BenchMark()
                Vector3 angles;
                angles[CAMERA_ROLL] = 0;
                angles[CAMERA_PITCH] = 0;
 -              angles[CAMERA_YAW] = static_cast<float>( i * ( 360.0 / 100.0 ) );
 +              angles[CAMERA_YAW] = i * 360.0f / 100.0f;
                Camera_setAngles( *this, angles );
        }
        double dEnd = Sys_DoubleTime();
@@@ -1654,10 -1644,54 +1654,54 @@@ void GlobalCamera_ResetAngles()
        CamWnd& camwnd = *g_camwnd;
        Vector3 angles;
        angles[CAMERA_ROLL] = angles[CAMERA_PITCH] = 0;
 -      angles[CAMERA_YAW] = static_cast<float>( 22.5 * floor( ( Camera_getAngles( camwnd )[CAMERA_YAW] + 11 ) / 22.5 ) );
 +      angles[CAMERA_YAW] = 22.5f * floorf( ( Camera_getAngles( camwnd )[CAMERA_YAW] + 11 ) / 22.5f );
        Camera_setAngles( camwnd, angles );
  }
  
+ #include "select.h"
+ void GlobalCamera_FocusOnSelected(){
+       CamWnd& camwnd = *g_camwnd;
+       Vector3 angles( Camera_getAngles( camwnd ) );
+       Vector3 radangles( degrees_to_radians( angles[0] ), degrees_to_radians( angles[1] ), degrees_to_radians( angles[2] ) );
+       Vector3 viewvector;
+       viewvector[0] = cos( radangles[1] ) * cos( radangles[0] );
+       viewvector[1] = sin( radangles[1] ) * cos( radangles[0] );
+       viewvector[2] = sin( radangles[0] );
+       Vector3 camorigin( Camera_getOrigin( camwnd ) );
+       AABB aabb( aabb_for_minmax( Select_getWorkZone().d_work_min, Select_getWorkZone().d_work_max ) );
+       View& view = *( camwnd.getCamera().m_view );
+       Plane3 frustumPlanes[4];
+       frustumPlanes[0] = plane3_translated( view.getFrustum().left, camorigin - aabb.origin );
+       frustumPlanes[1] = plane3_translated( view.getFrustum().right, camorigin - aabb.origin );
+       frustumPlanes[2] = plane3_translated( view.getFrustum().top, camorigin - aabb.origin );
+       frustumPlanes[3] = plane3_translated( view.getFrustum().bottom, camorigin - aabb.origin );
+       float offset = 64.0f;
+       Vector3 corners[8];
+       aabb_corners( aabb, corners );
+       for ( size_t i = 0; i < 4; ++i ){
+               for ( size_t j = 0; j < 8; ++j ){
+                       Ray ray( aabb.origin, -viewvector );
+                       //Plane3 newplane( frustumPlanes[i].normal(), vector3_dot( frustumPlanes[i].normal(), corners[j] - frustumPlanes[i].normal() * 16.0f ) );
+                       Plane3 newplane( frustumPlanes[i].normal(), vector3_dot( frustumPlanes[i].normal(), corners[j] ) );
+                       float d = vector3_dot( ray.direction, newplane.normal() );
+                       if( d != 0 ){
+                               float s = vector3_dot( newplane.normal() * newplane.dist() - ray.origin, newplane.normal() ) / d;
+                               offset = std::max( offset, s );
+                       }
+               }
+       }
+       Camera_setOrigin( camwnd, aabb.origin - viewvector * offset );
+ }
  void Camera_ChangeFloorUp(){
        CamWnd& camwnd = *g_camwnd;
        camwnd.Cam_ChangeFloor( true );
@@@ -1682,8 -1716,8 +1726,8 @@@ void Camera_CubeIn()
  void Camera_CubeOut(){
        CamWnd& camwnd = *g_camwnd;
        g_camwindow_globals.m_nCubicScale++;
 -      if ( g_camwindow_globals.m_nCubicScale > 23 ) {
 -              g_camwindow_globals.m_nCubicScale = 23;
 +      if ( g_camwindow_globals.m_nCubicScale > 46 ) {
 +              g_camwindow_globals.m_nCubicScale = 46;
        }
        Camera_updateProjection( camwnd.getCamera() );
        CamWnd_Update( camwnd );
@@@ -1731,8 -1765,6 +1775,8 @@@ void CamWnd_registerShortcuts()
                command_connect_accelerator( "TogglePreview" );
        }
  
 +      command_connect_accelerator( "FOVInc" );
 +      command_connect_accelerator( "FOVDec" );
        command_connect_accelerator( "CameraSpeedInc" );
        command_connect_accelerator( "CameraSpeedDec" );
  }
@@@ -1850,7 -1882,6 +1894,7 @@@ struct RenderMode 
  };
  
  void Camera_constructPreferences( PreferencesPage& page ){
 +      page.appendSlider( "FOV", g_camwindow_globals_private.m_nFOV, TRUE, 0, 0, 100, MIN_FOV, MAX_FOV, 1, 10 );
        page.appendSlider( "Movement Speed", g_camwindow_globals_private.m_nMoveSpeed, TRUE, 0, 0, 100, MIN_CAM_SPEED, MAX_CAM_SPEED, 1, 10 );
        page.appendCheckBox( "", "Link strafe speed to movement speed", g_camwindow_globals_private.m_bCamLinkSpeed );
        page.appendSlider( "Rotation Speed", g_camwindow_globals_private.m_nAngleSpeed, TRUE, 0, 0, 3, 1, 180, 1, 10 );
@@@ -1904,31 -1935,6 +1948,31 @@@ void Camera_registerPreferencesPage()
  #include "stringio.h"
  #include "dialog.h"
  
 +void FOV_increase(){
 +      CamWnd& camwnd = *g_camwnd;
 +      if ( g_camwindow_globals_private.m_nFOV <= ( MAX_FOV - FOV_STEP - 10 ) ) {
 +              g_camwindow_globals_private.m_nFOV += FOV_STEP;
 +      }
 +      else {
 +              g_camwindow_globals_private.m_nFOV = MAX_FOV - 10;
 +      }
 +      Camera_updateProjection( camwnd.getCamera() );
 +      CamWnd_Update( camwnd );
 +}
 +
 +void FOV_decrease(){
 +      CamWnd& camwnd = *g_camwnd;
 +      if ( g_camwindow_globals_private.m_nFOV >= ( MIN_FOV + FOV_STEP ) ) {
 +              g_camwindow_globals_private.m_nFOV -= FOV_STEP;
 +      }
 +      else {
 +              g_camwindow_globals_private.m_nFOV = MIN_FOV;
 +      }
 +      Camera_updateProjection( camwnd.getCamera() );
 +      CamWnd_Update( camwnd );
 +}
 +
 +
  void CameraSpeed_increase(){
        if ( g_camwindow_globals_private.m_nMoveSpeed <= ( MAX_CAM_SPEED - CAM_SPEED_STEP - 10 ) ) {
                g_camwindow_globals_private.m_nMoveSpeed += CAM_SPEED_STEP;
@@@ -1950,6 -1956,7 +1994,7 @@@ void CameraSpeed_decrease()
  /// \brief Initialisation for things that have the same lifespan as this module.
  void CamWnd_Construct(){
        GlobalCommands_insert( "CenterView", makeCallbackF(GlobalCamera_ResetAngles), Accelerator( GDK_KEY_End ) );
+       GlobalCommands_insert( "CameraFocusOnSelected", makeCallbackF( GlobalCamera_FocusOnSelected ), Accelerator( GDK_Tab ) );
  
        GlobalToggles_insert( "ToggleCubicClip", makeCallbackF(Camera_ToggleFarClip), ToggleItem::AddCallbackCaller( g_getfarclip_item ), Accelerator( '\\', (GdkModifierType)GDK_CONTROL_MASK ) );
        GlobalCommands_insert( "CubicClipZoomIn", makeCallbackF(Camera_CubeIn), Accelerator( '[', (GdkModifierType)GDK_CONTROL_MASK ) );
                GlobalCommands_insert( "TogglePreview", makeCallbackF(CamWnd_TogglePreview), Accelerator( GDK_KEY_F3 ) );
        }
  
 +      GlobalCommands_insert( "FOVInc", makeCallbackF(FOV_increase), Accelerator( GDK_KEY_KP_Multiply, (GdkModifierType)GDK_SHIFT_MASK ) );
 +      GlobalCommands_insert( "FOVDec", makeCallbackF(FOV_decrease), Accelerator( GDK_KEY_KP_Divide, (GdkModifierType)GDK_SHIFT_MASK ) );
 +
        GlobalCommands_insert( "CameraSpeedInc", makeCallbackF(CameraSpeed_increase), Accelerator( GDK_KEY_KP_Add, (GdkModifierType)GDK_SHIFT_MASK ) );
        GlobalCommands_insert( "CameraSpeedDec", makeCallbackF(CameraSpeed_decrease), Accelerator( GDK_KEY_KP_Subtract, (GdkModifierType)GDK_SHIFT_MASK ) );
  
        GlobalToggles_insert( "ShowStats", makeCallbackF(ShowStatsToggle), ToggleItem::AddCallbackCaller( g_show_stats ) );
  
        GlobalPreferenceSystem().registerPreference( "ShowStats", make_property_string( g_camwindow_globals_private.m_showStats ) );
 +      GlobalPreferenceSystem().registerPreference( "FOV", make_property_string( g_camwindow_globals_private.m_nFOV ) );
        GlobalPreferenceSystem().registerPreference( "MoveSpeed", make_property_string( g_camwindow_globals_private.m_nMoveSpeed ) );
        GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", make_property_string( g_camwindow_globals_private.m_bCamLinkSpeed ) );
        GlobalPreferenceSystem().registerPreference( "AngleSpeed", make_property_string( g_camwindow_globals_private.m_nAngleSpeed ) );
diff --combined radiant/mainframe.cpp
index f1263dfd031efb40a9aa489ad0942b8d0e578f30,01399dfb8896f13486277d7876e2d4374b0c5182..6aa3e9f7bde6c5ad370bbd686a560b989daec6ec
  #include "texwindow.h"
  #include "filterbar.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
 +
  #define GARUX_DISABLE_GTKTHEME
  #ifndef GARUX_DISABLE_GTKTHEME
  #include "gtktheme.h"
  #endif
  
 -
  struct layout_globals_t
  {
        WindowPosition m_position;
  };
  
  layout_globals_t g_layout_globals;
- glwindow_globals_t g_glwindow_globals;
//glwindow_globals_t g_glwindow_globals;
  
  
  // VFS
@@@ -173,8 -161,6 +173,8 @@@ void VFS_Refresh()
        RefreshReferences();
        // also refresh texture browser
        TextureBrowser_RefreshShaders();
 +      // also show textures (all or common)
 +      TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
  }
  
  void VFS_Restart(){
@@@ -233,7 -219,9 +233,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" );
                                        break;
                                }
                        }
 -#endif
 -
 -#if GDEF_OS_POSIX
 +#elif GDEF_OS_XDG
 +                      path.clear();
 +                      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
                }
  
@@@ -458,32 -440,14 +458,32 @@@ void setPakPath( int num, const char* p
  }
  
  
 -// App Path
 +// executable file path (full path)
 +CopiedString g_strAppFilePath;
 +
 +// directory paths
 +CopiedString g_strAppPath; 
 +CopiedString g_strLibPath;
 +CopiedString g_strDataPath;
  
 -CopiedString g_strAppPath;                 ///< holds the full path of the executable
 +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;
@@@ -582,28 -546,39 +582,28 @@@ struct PakPath4 
  bool g_disableEnginePath = false;
  bool g_disableHomePath = false;
  
 -void Paths_constructPreferences( PreferencesPage& page ){
 +void Paths_constructBasicPreferences(  PreferencesPage& page ) {
        page.appendPathEntry( "Engine Path", true, make_property<EnginePath>(g_strEnginePath) );
 +}
  
 -      page.appendCheckBox(
 -              "", "Do not use Engine Path",
 -              g_disableEnginePath
 -                                                );
 +void Paths_constructPreferences( PreferencesPage& page ){
 +      Paths_constructBasicPreferences( page );
  
 -      page.appendCheckBox(
 -              "", "Do not use Home Path",
 -              g_disableHomePath
 -              );
 +      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 );
  
 -      for ( int i = 0; i < g_pakPathCount; i++ ) {
 -              std::string label = "Pak Path " + std::to_string(i);
 -              switch (i) {
 -                      case 0:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath0>( g_strPakPath[i] ) );
 -                      break;
 -                      case 1:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath1>( g_strPakPath[i] ) );
 -                      break;
 -                      case 2:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath2>( g_strPakPath[i] ) );
 -                      break;
 -                      case 3:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath3>( g_strPakPath[i] ) );
 -                      break;
 -                      case 4:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath4>( g_strPakPath[i] ) );
 -                      break;
 -}
 -      }
 +      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 ){
@@@ -620,14 -595,14 +620,14 @@@ class PathsDialog : public Dialo
  {
  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 ));
@@@ -786,7 -761,7 +786,7 @@@ void Radiant_detachGameToolsPathObserve
  void Radiant_Initialise(){
        GlobalModuleServer_Initialise();
  
 -      Radiant_loadModulesFromRoot( AppPath_get() );
 +      Radiant_loadModulesFromRoot( LibPath_get() );
  
        Preferences_Load();
  
@@@ -815,7 -790,7 +815,7 @@@ void Radiant_Shutdown()
  }
  
  void Exit(){
 -      if ( ConfirmModified( "Exit Radiant" ) ) {
 +      if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
  }
@@@ -974,53 -949,6 +974,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;
  
@@@ -1134,7 -1062,6 +1134,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");
  
  #ifndef GARUX_DISABLE_GTKTHEME
        create_menu_item_with_mnemonic( menu_in_menu, "GTK Theme...", "gtkThemeDlg" );
@@@ -1831,11 -1758,9 +1831,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();
@@@ -1947,18 -1872,15 +1947,18 @@@ void ScreenUpdates_Disable( const char
                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 );
@@@ -2163,6 -2085,7 +2163,7 @@@ ui::MenuItem create_view_menu( MainFram
                if ( g_Layout_enableDetachableMenus.m_value ) {
                        menu_tearoff( camera_menu );
                }
+               create_menu_item_with_mnemonic( camera_menu, "Focus on Selected", "CameraFocusOnSelected" );
                create_menu_item_with_mnemonic( camera_menu, "_Center", "CenterView" );
                create_menu_item_with_mnemonic( camera_menu, "_Up Floor", "UpFloor" );
                create_menu_item_with_mnemonic( camera_menu, "_Down Floor", "DownFloor" );
                create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane In", "CubicClipZoomIn" );
                create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane Out", "CubicClipZoomOut" );
                menu_separator( camera_menu );
 +              create_menu_item_with_mnemonic( camera_menu, "Decrease FOV", "FOVDec" );
 +              create_menu_item_with_mnemonic( camera_menu, "Increase FOV", "FOVInc" );
 +              menu_separator( camera_menu );
                create_menu_item_with_mnemonic( camera_menu, "Next leak spot", "NextLeakSpot" );
                create_menu_item_with_mnemonic( camera_menu, "Previous leak spot", "PrevLeakSpot" );
                //cameramodel is not implemented in instances, thus useless
@@@ -2289,6 -2209,9 +2290,9 @@@ ui::MenuItem create_selection_menu()
                create_menu_item_with_mnemonic( menu_in_menu, "Nudge Right", "SelectNudgeRight" );
                create_menu_item_with_mnemonic( menu_in_menu, "Nudge Up", "SelectNudgeUp" );
                create_menu_item_with_mnemonic( menu_in_menu, "Nudge Down", "SelectNudgeDown" );
+               menu_separator( menu_in_menu );
+               create_menu_item_with_mnemonic( menu_in_menu, "Nudge +Z", "MoveSelectionUP" );
+               create_menu_item_with_mnemonic( menu_in_menu, "Nudge -Z", "MoveSelectionDOWN" );
        }
        {
                auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Rotate" );
@@@ -2432,8 -2355,8 +2436,8 @@@ ui::MenuItem create_help_menu()
        create_game_help_menu( 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, "Shortcuts", makeCallbackF(DoCommandListDlg) );
 -      create_menu_item_with_mnemonic( menu, "_About", makeCallbackF(DoAbout) );
 +      create_menu_item_with_mnemonic( menu, "_About...", makeCallbackF(DoAbout) );
  
        return help_menu_item;
  }
@@@ -2496,8 -2419,8 +2500,8 @@@ void TexdefNudge_registerShortcuts()
  }
  
  void SelectNudge_registerShortcuts(){
-       command_connect_accelerator( "MoveSelectionDOWN" );
-       command_connect_accelerator( "MoveSelectionUP" );
+       //command_connect_accelerator( "MoveSelectionDOWN" );
+       //command_connect_accelerator( "MoveSelectionUP" );
        //command_connect_accelerator("SelectNudgeLeft");
        //command_connect_accelerator("SelectNudgeRight");
        //command_connect_accelerator("SelectNudgeUp");
@@@ -2832,14 -2755,10 +2836,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 ){
@@@ -2996,16 -2915,13 +3000,16 @@@ 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();
        }
  
        return TRUE;
  }
  
 +PanedState g_single_hpaned = { 0.75f, -1, };
 +PanedState g_single_vpaned = { 0.75f, -1, };
 +
  void MainFrame::Create(){
        ui::Window window = ui::Window( ui::window_type::TOP );
  
  
        window.show();
  
 -      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft ) {
 +      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft )
 +      {
                {
                        ui::Widget hsplit = ui::HPaned(ui::New);
                        m_hSplit = hsplit;
                        }
                }
        }
 -      else if ( CurrentStyle() == eFloating ) {
 +      else if ( CurrentStyle() == eFloating )
 +      {
                {
                        ui::Window window = ui::Window(create_persistent_floating_window( "Camera", m_window ));
                        global_accel_connect_window( window );
                                window.add(frame);
                        }
                        CamWnd_setParent( *m_pCamWnd, window );
 -#define GARUX_GTK_WORKAROUND
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", CamWnd_getWidget( *m_pCamWnd ) );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, CamWnd_getWidget( *m_pCamWnd ) );
  
                        g_floating_windows.push_back( window );
                }
                                window.add(frame);
                        }
                        XY_Top_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pXYWnd->GetWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXYWnd->GetWidget() );
  
                        g_floating_windows.push_back( window );
                }
                        }
  
                        XZ_Front_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pXZWnd->GetWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXZWnd->GetWidget() );
  
                        g_floating_windows.push_back( window );
                }
                        }
  
                        YZ_Side_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pYZWnd->GetWidget() );
 -#endif
 +
 +                      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() );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( GroupDialog_getWindow() ), "glwidget", TextureBrowser_getGLWidget() );
 -#endif
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( GroupDialog_getWindow(), TextureBrowser_getGLWidget() );
                }
  
                // FIXME: find a way to do it with newer syntax
  
                GroupDialog_show();
        }
 -      else // 4 way
 +      else if ( CurrentStyle() == eSplit )
        {
                m_pCamWnd = NewCamWnd();
                GlobalCamera_setCamWnd( *m_pCamWnd );
                {
              auto frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( GroupDialog_getWindow() ), "glwidget", TextureBrowser_getGLWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, TextureBrowser_getGLWidget() );
                }
        }
 +      else // single window
 +      {
 +              m_pCamWnd = NewCamWnd();
 +              GlobalCamera_setCamWnd( *m_pCamWnd );
 +              CamWnd_setParent( *m_pCamWnd, window );
 +
 +              ui::Widget camera = CamWnd_getWidget( *m_pCamWnd );
 +
 +              m_pYZWnd = new XYWnd();
 +              m_pYZWnd->SetViewType( YZ );
 +
 +              ui::Widget yz = m_pYZWnd->GetWidget();
 +
 +              m_pXYWnd = new XYWnd();
 +              m_pXYWnd->SetViewType( XY );
 +
 +              ui::Widget xy = m_pXYWnd->GetWidget();
 +
 +              m_pXZWnd = new XYWnd();
 +              m_pXZWnd->SetViewType( XZ );
 +
 +              ui::Widget xz = m_pXZWnd->GetWidget();
 +
 +              ui::Widget hsplit = ui::HPaned(ui::New);
 +              vbox.pack_start( hsplit, TRUE, TRUE, 0 );
 +              hsplit.show();
 +
 +              /* Before merging NetRadiantCustom:
 +              ui::Widget split = create_split_views( camera, yz, xy, xz ); */
 +              m_hSplit = create_split_views( camera, yz, xy, xz, m_vSplit, m_vSplit2 );
 +
 +              ui::Widget vsplit = ui::VPaned(ui::New);
 +              vsplit.show();
 +
 +              // textures
 +              ui::Widget texture_window = create_framed_widget( TextureBrowser_constructWindow( window ) );
 +
 +              // console
 +              ui::Widget console_window = create_framed_widget( Console_constructWindow( window ) );
 +
 +              /* Before merging NetRadiantCustom:
 +              gtk_paned_add1( GTK_PANED( hsplit ), m_hSplit );
 +              gtk_paned_add2( GTK_PANED( hsplit ), vsplit );
 +
 +              gtk_paned_add1( GTK_PANED( vsplit ), texture_window  );
 +              gtk_paned_add2( GTK_PANED( vsplit ), console_window  );
 +              */
 +
 +              gtk_paned_pack1( GTK_PANED( hsplit ), m_hSplit, TRUE, TRUE );
 +              gtk_paned_pack2( GTK_PANED( hsplit ), vsplit, TRUE, TRUE);
 +
 +              gtk_paned_pack1( GTK_PANED( vsplit ), texture_window, TRUE, TRUE );
 +              gtk_paned_pack2( GTK_PANED( vsplit ), console_window, TRUE, TRUE );
 +
 +              hsplit.connect( "size_allocate", G_CALLBACK( hpaned_allocate ), &g_single_hpaned );
 +              hsplit.connect( "notify::position", G_CALLBACK( paned_position ), &g_single_hpaned );
 +
 +              vsplit.connect( "size_allocate", G_CALLBACK( vpaned_allocate ), &g_single_vpaned );
 +              vsplit.connect( "notify::position", G_CALLBACK( paned_position ), &g_single_vpaned );
 +      }
  
        EntityList_constructWindow( window );
        PreferencesDialog_constructWindow( window );
@@@ -3459,7 -3327,7 +3463,7 @@@ void MainFrame::SetStatusText( CopiedSt
  }
  
  void Sys_Status( const char* status ){
 -      if ( g_pParentWnd != 0 ) {
 +      if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetStatusText( g_pParentWnd->m_command_status, status );
        }
  }
@@@ -3491,7 -3359,7 +3495,7 @@@ void MainFrame::SetGridStatus()
  }
  
  void GridStatus_onTextureLockEnabledChanged(){
 -      if ( g_pParentWnd != 0 ) {
 +      if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetGridStatus();
        }
  }
@@@ -3536,7 -3404,7 +3540,7 @@@ void GlobalGL_sharedContextDestroyed()
  
  void Layout_constructPreferences( PreferencesPage& page ){
        {
 -              const char* layouts[] = { "window1.png", "window2.png", "window3.png", "window4.png" };
 +              const char* layouts[] = { "window1.png", "window2.png", "window3.png", "window4.png", "window5.png" };
                page.appendRadioIcons(
                        "Window Layout",
                        STRING_ARRAY_RANGE( layouts ),
@@@ -3663,9 -3531,9 +3667,9 @@@ void Maximize_View()
                g_maximizeview.toggle();
  }
  
 -
  #include "preferencesystem.h"
  #include "stringio.h"
 +#include "transformpath/transformpath.h"
  
  void MainFrame_Construct(){
        GlobalCommands_insert( "OpenManual", makeCallbackF(OpenHelpURL), Accelerator( GDK_KEY_F1 ) );
        GlobalCommands_insert( "ToggleEntityInspector", makeCallbackF(EntityInspector_ToggleShow), Accelerator( 'N' ) );
        GlobalCommands_insert( "EntityList", makeCallbackF(EntityList_toggleShown), Accelerator( 'L' ) );
  
- //    GlobalCommands_insert( "ShowHidden", FreeCaller<Select_ShowAllHidden>(), Accelerator( 'H', (GdkModifierType)GDK_SHIFT_MASK ) );
- //    GlobalCommands_insert( "HideSelected", FreeCaller<HideSelected>(), Accelerator( 'H' ) );
+ //    GlobalCommands_insert( "ShowHidden", makeCallbackF( Select_ShowAllHidden ), Accelerator( 'H', (GdkModifierType)GDK_SHIFT_MASK ) );
+ //    GlobalCommands_insert( "HideSelected", makeCallbackF( HideSelected ), Accelerator( 'H' ) );
  
        Select_registerCommands();
  
        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 ) );
  #error "unknown platform"
  #endif
                ;
 +
                StringOutputStream path( 256 );
                path << DirectoryCleaned( g_pGameDescription->getRequiredKeyValue( ENGINEPATH_ATTRIBUTE ) );
 -              g_strEnginePath = path.c_str();
 +
 +              g_strEnginePath = transformPath( path.c_str() ).c_str();
                GlobalPreferenceSystem().registerPreference( "EnginePath", make_property_string( g_strEnginePath ) );
        }
  
        g_entityCount.setCountChangedCallback( makeCallbackF(QE_entityCountChanged) );
        GlobalEntityCreator().setCounter( &g_entityCount );
  
 -      GLWidget_sharedContextCreated = GlobalGL_sharedContextCreated;
 -      GLWidget_sharedContextDestroyed = GlobalGL_sharedContextDestroyed;
 +      glwidget_set_shared_context_constructors( GlobalGL_sharedContextCreated, GlobalGL_sharedContextDestroyed);
  
        GlobalEntityClassManager().attach( g_WorldspawnColourEntityClassObserver );
  }
@@@ -3878,66 -3744,8 +3882,66 @@@ void MainFrame_Destroy()
  
  
  void GLWindow_Construct(){
      GlobalPreferenceSystem().registerPreference( "MouseButtons", make_property_string( g_glwindow_globals.m_nMouseType ) );
//    GlobalPreferenceSystem().registerPreference( "MouseButtons", make_property_string( g_glwindow_globals.m_nMouseType ) );
  }
  
  void GLWindow_Destroy(){
  }
 +
 +/* HACK: If ui::main is not called yet,
 +gtk_main_quit will not quit, so tell main
 +to not call ui::main. This happens when a
 +map is loaded from command line and require
 +a restart because of wrong format.
 +Delete this when the code to not have to
 +restart to load another format is merged. */
 +extern bool g_dontStart;
 +
 +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();
 +              /* HACK: If ui::main is not called yet,
 +              gtk_main_quit will not quit, so tell main
 +              to not call ui::main. This happens when a
 +              map is loaded from command line and require
 +              a restart because of wrong format.
 +              Delete this when the code to not have to
 +              restart to load another format is merged. */
 +              g_dontStart = true;
 +      }
 +}
diff --combined radiant/mainframe.h
index e48337904fcdd8b594938fd4119d251d68fa8713,296319cc44f7645513ce4ec23f5298a2d2e7ad15..c5a53efd445510fdeafe6d578dfd84001eb3e16c
@@@ -54,7 -54,6 +54,7 @@@ enum EViewStyl
        eFloating = 1,
        eSplit = 2,
        eRegularLeft = 3,
 +      eSingle = 4,
  };
  
  MainFrame();
@@@ -146,7 -145,7 +146,7 @@@ bool FloatingGroupDialog()
  extern MainFrame* g_pParentWnd;
  
  ui::Window MainFrame_getWindow();
+ /*
  enum EMouseButtonMode
  {
        ETwoButton = 0,
@@@ -161,11 -160,11 +161,11 @@@ struct glwindow_globals_
                m_nMouseType( EThreeButton ){
        }
  };
+ */
  void GLWindow_Construct();
  void GLWindow_Destroy();
  
- extern glwindow_globals_t g_glwindow_globals;
//extern glwindow_globals_t g_glwindow_globals;
  template<typename Value>
  class LatchedValue;
  extern LatchedValue<bool> g_Layout_enableDetachableMenus;
@@@ -216,15 -215,8 +216,15 @@@ const int g_pakPathCount = 5
  extern CopiedString g_strPakPath[g_pakPathCount];
  const char* PakPath_get( int num );
  
 +extern CopiedString g_strAppFilePath;
  extern CopiedString g_strAppPath;
 +extern CopiedString g_strLibPath;
 +extern CopiedString g_strDataPath;
 +
 +const char* AppFilePath_get();
  const char* AppPath_get();
 +const char *LibPath_get();
 +const char *DataPath_get();
  
  extern CopiedString g_strSettingsPath;
  const char* SettingsPath_get();
@@@ -276,8 -268,6 +276,8 @@@ void Radiant_detachHomePathsObserver( M
  void MainFrame_Construct();
  void MainFrame_Destroy();
  
 +extern char **environ;
 +void Radiant_Restart();
  
  extern float ( *GridStatus_getGridSize )();
  extern int ( *GridStatus_getRotateIncrement )();
diff --combined radiant/preferences.cpp
index a689f71c63f11f2b9497ca2e605f05a7b6a6dc9b,3735e31e4a829d5427b07a1524e2ecbf96549ab8..8d16946159e90bc8fb5162d9be58c0a210f555b5
@@@ -70,11 -70,11 +70,11 @@@ void Interface_constructPreferences( Pr
  }
  
  void Mouse_constructPreferences( PreferencesPage& page ){
      {
              const char* buttons[] = { "2 button", "3 button", };
              page.appendRadio( "Mouse Type",  g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
      }
      page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
//    {
//            const char* buttons[] = { "2 button", "3 button", };
//            page.appendRadio( "Mouse Type",  g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
//    }
//    page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
        page.appendCheckBox( "", "Improved mousewheel zoom", g_xywindow_globals.m_bImprovedWheelZoom );
  }
  void Mouse_constructPage( PreferenceGroup& group ){
@@@ -122,7 -122,7 +122,7 @@@ CGameDescription::CGameDescription( xml
  
        {
                StringOutputStream path( 256 );
 -              path << AppPath_get() << gameFile.c_str() << "/";
 +              path << DataPath_get() << "gamepacks/" << gameFile.c_str() << "/";
                mGameToolsPath = path.c_str();
        }
  
@@@ -205,10 -205,13 +205,10 @@@ bool Preferences_Save( PreferenceDictio
  }
  
  bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
 -      Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
 -      *( tmpName.end() - 4 ) = 'T';
 -      *( tmpName.end() - 3 ) = 'M';
 -      *( tmpName.end() - 2 ) = 'P';
 -      *( tmpName.end() - 1 ) = '\0';
 +      std::string tmpName( filename );
 +      tmpName += "TMP";
  
 -      return Preferences_Save( preferences, tmpName.data() )
 +      return Preferences_Save( preferences, tmpName.c_str() )
                   && ( !file_exists( filename ) || file_remove( filename ) )
                   && file_move( tmpName.data(), filename );
  }
@@@ -221,7 -224,7 +221,7 @@@ struct LogConsole 
  
        static void Import(bool value) {
                g_Console_enableLogging = value;
 -              Sys_LogFile(g_Console_enableLogging);
 +              Sys_EnableLogFile(g_Console_enableLogging);
        }
  };
  
  void RegisterGlobalPreferences( PreferenceSystem& preferences ){
        preferences.registerPreference( "gamefile", make_property_string( g_GamesDialog.m_sGameFile ) );
        preferences.registerPreference( "gamePrompt", make_property_string( g_GamesDialog.m_bGamePrompt ) );
 +      preferences.registerPreference( "skipGamePromptOnce", make_property_string( g_GamesDialog.m_bSkipGamePromptOnce ) );
        preferences.registerPreference( "log console", make_property_string<LogConsole>() );
  }
  
@@@ -280,18 -282,7 +280,18 @@@ void CGameDialog::GameFileImport( int v
        {
                ++iGame;
        }
 +
 +      if ( ( *iGame )->mGameFile != m_sGameFile ) {
        m_sGameFile = ( *iGame )->mGameFile;
 +
 +              // do not trigger radiant restart when switching game on startup using Global Preferences dialog
 +              if ( !onStartup ) {
 +                      PreferencesDialog_restartRequired( "Selected Game" );
 +              }
 +      }
 +
 +      // onStartup can only be true once, when Global Preferences are displayed at startup
 +      onStartup = false;
  }
  
  void CGameDialog::GameFileExport( const Callback<void(int)> & importCallback ) const {
@@@ -349,33 -340,9 +349,33 @@@ ui::Window CGameDialog::BuildDialog()
        return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
  }
  
 +static void StringReplace( std::string& input, const std::string& first, const std::string& second )
 +{
 +      size_t found = 0;
 +      while ( ( found = input.find(first, found) ) != std::string::npos )
 +      {
 +              input.replace( found, first.length(), second );
 +      }
 +}
 +
 +// FIXME, for some unknown reason it sorts “Quake 3” after “Quake 4”.
 +static bool CompareGameName( CGameDescription *first, CGameDescription *second )
 +{
 +      std::string string1( first->getRequiredKeyValue( "name" ) );
 +      std::string string2( second->getRequiredKeyValue( "name" ) );
 +
 +      // HACK: Replace some roman numerals.
 +      StringReplace( string1, " III", " 3" );
 +      StringReplace( string2, " III", " 3" );
 +      StringReplace( string1, " II", " 2" );
 +      StringReplace( string2, " II", " 2" );
 +
 +      return string1 < string2;
 +}
 +
  void CGameDialog::ScanForGames(){
        StringOutputStream strGamesPath( 256 );
 -      strGamesPath << AppPath_get() << "games/";
 +      strGamesPath << DataPath_get() << "gamepacks/games/";
        const char *path = strGamesPath.c_str();
  
        globalOutputStream() << "Scanning for game description files: " << path << '\n';
                } else {
                        globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
                }
 +
 +              mGames.sort(CompareGameName);
        });
  }
  
@@@ -435,12 -400,9 +435,12 @@@ void CGameDialog::Reset()
  }
  
  void CGameDialog::Init(){
 +      bool gamePrompt = false;
 +
        InitGlobalPrefPath();
        LoadPrefs();
        ScanForGames();
 +
        if ( mGames.empty() ) {
                Error( "Didn't find any valid game file descriptions, aborting\n" );
        }
  
        CGameDescription* currentGameDescription = 0;
  
 -      if ( !m_bGamePrompt ) {
 +      // m_bSkipGamePromptOnce is used to not prompt for game on restart, only on fresh startup
 +      if ( m_bGamePrompt && !m_bSkipGamePromptOnce ) {
 +              gamePrompt = true;
 +      }
 +
 +      m_bSkipGamePromptOnce = false;
 +      g_GamesDialog.SavePrefs();
 +
 +      if ( !gamePrompt ) {
                // search by .game name
                std::list<CGameDescription *>::iterator iGame;
                for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
                        }
                }
        }
 -      if ( m_bGamePrompt || !currentGameDescription ) {
 +
 +      if ( gamePrompt || !currentGameDescription ) {
 +              onStartup = true;
                Create();
                DoGameDialog();
                // use m_nComboSelect to identify the game to run as and set the globals
                currentGameDescription = GameDescriptionForComboItem();
                ASSERT_NOTNULL( currentGameDescription );
        }
 +      else {
 +              onStartup = false;
 +      }
 +
        g_pGameDescription = currentGameDescription;
  
        g_pGameDescription->Dump();
@@@ -528,8 -476,8 +528,8 @@@ CGameDialog g_GamesDialog
  
  static void OnButtonClean( ui::Widget widget, gpointer data ){
        // make sure this is what the user wants
 -      if ( ui::alert( g_Preferences.GetWidget(), "This will close Radiant and clean the corresponding registry entries.\n"
 -                                                                                                                                "Next time you start Radiant it will be good as new. Do you wish to continue?",
 +      if ( ui::alert( g_Preferences.GetWidget(), "This will close " RADIANT_NAME " and clean the corresponding registry entries.\n"
 +                                                                                                                                "Next time you start " RADIANT_NAME " it will be good as new. Do you wish to continue?",
                                                 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
                PrefsDlg *dlg = (PrefsDlg*)data;
                dlg->EndModal( eIDCANCEL );
@@@ -729,10 -677,7 +729,10 @@@ ui::Window PrefsDlg::BuildDialog()
        PreferencesDialog_addInterfacePreferences( makeCallbackF(Interface_constructPreferences) );
        Mouse_registerPreferencesPage();
  
 -      ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
 +      ui::Window dialog = ui::Window(create_floating_window( RADIANT_NAME " Preferences", m_parent ));
 +
 +      gtk_window_set_transient_for( dialog, m_parent );
 +      gtk_window_set_position( dialog, GTK_WIN_POS_CENTER_ON_PARENT );
  
        {
                auto mainvbox = ui::VBox( FALSE, 5 );
@@@ -933,7 -878,6 +933,7 @@@ void Preferences_Save()
                return;
        }
  
 +      // save global preferences
        g_GamesDialog.SavePrefs();
  
        globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
@@@ -961,39 -905,22 +961,39 @@@ void PreferencesDialog_restartRequired
        g_restart_required.push_back( staticName );
  }
  
 -void PreferencesDialog_showDialog(){
 -      //if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
 -      if ( g_Preferences.DoModal() == eIDOK ) {
 -              if ( !g_restart_required.empty() ) {
 -                      StringOutputStream message( 256 );
 -                      message << "Preference changes require a restart:\n";
 -                      for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
 -                      {
 -                              message << ( *i ) << '\n';
 -                      }
 -                      ui::alert( MainFrame_getWindow(), message.c_str() );
 -                      g_restart_required.clear();
 +bool PreferencesDialog_isRestartRequired(){
 +      return !g_restart_required.empty();
 +}
 +
 +void PreferencesDialog_restartIfRequired(){
 +      if ( !g_restart_required.empty() ) {
 +              StringOutputStream message( 256 );
 +              message << "Preference changes require a restart:\n\n";
 +
 +              for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
 +              {
 +                      message << ( *i ) << '\n';
 +              }
 +
 +              message << "\nRestart now?";
 +
 +              auto ret = ui::alert( MainFrame_getWindow(), message.c_str(), "Restart " RADIANT_NAME "?", ui::alert_type::YESNO, ui::alert_icon::Question );
 +
 +              g_restart_required.clear();
 +
 +              if ( ret == ui::alert_response::YES ) {
 +                      g_GamesDialog.m_bSkipGamePromptOnce = true;
 +                      Radiant_Restart();
                }
        }
  }
  
 +void PreferencesDialog_showDialog(){
 +      if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
 +              PreferencesDialog_restartIfRequired();
 +      }
 +}
 +
  struct GameName {
        static void Export(const Callback<void(const char *)> &returnz) {
                returnz(gamename_get());
diff --combined radiant/xywindow.cpp
index 63ef5973b107e3eb25f175ef1775795d639086c6,f1d3888a74c1eb9ba8c275b6a8cab361a2b564c6..5024d68f69246e5652d6ee7272db05eef3166cee
@@@ -58,7 -58,6 +58,7 @@@
  #include "gtkutil/widget.h"
  #include "gtkutil/glwidget.h"
  #include "gtkutil/filechooser.h"
 +#include "gtkutil/cursor.h"
  #include "gtkmisc.h"
  #include "select.h"
  #include "csg.h"
@@@ -354,7 -353,7 +354,7 @@@ struct xywindow_globals_private_
        bool show_blocks;
        int blockSize;
  
      bool m_bCamXYUpdate;
//    bool m_bCamXYUpdate;
        bool m_bChaseMouse;
        bool m_bSizePaint;
  
  
                show_blocks( false ),
  
              m_bCamXYUpdate( true ),
//            m_bCamXYUpdate( true ),
                m_bChaseMouse( true ),
                m_bSizePaint( true ),
  
@@@ -556,19 -555,6 +556,19 @@@ void XYWnd::ZoomInWithMouse( int pointx
        }
  }
  
 +void XYWnd::Redraw() {
 +      if ( glwidget_make_current( m_gl_widget ) != FALSE ) {
 +              if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) {
 +                      GlobalOpenGL_debugAssertNoErrors();
 +                      XY_Draw();
 +                      GlobalOpenGL_debugAssertNoErrors();
 +
 +                      m_XORRectangle.set( rectangle_t() );
 +              }
 +              glwidget_swap_buffers( m_gl_widget );
 +      }
 +}
 +
  VIEWTYPE GlobalXYWnd_getCurrentViewType(){
        ASSERT_NOTNULL( g_pParentWnd );
        ASSERT_NOTNULL( g_pParentWnd->ActiveXY() );
@@@ -823,15 -809,25 +823,15 @@@ gboolean xywnd_size_allocate( ui::Widge
  }
  
  gboolean xywnd_expose( ui::Widget widget, GdkEventExpose* event, XYWnd* xywnd ){
 -      if ( glwidget_make_current( xywnd->GetWidget() ) != FALSE ) {
 -              if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) {
 -                      GlobalOpenGL_debugAssertNoErrors();
 -                      xywnd->XY_Draw();
 -                      GlobalOpenGL_debugAssertNoErrors();
 -
 -                      xywnd->m_XORRectangle.set( rectangle_t() );
 -              }
 -              glwidget_swap_buffers( xywnd->GetWidget() );
 -      }
 +      xywnd->Redraw();
        return FALSE;
  }
  
 -
  void XYWnd_CameraMoved( XYWnd& xywnd ){
      if ( g_xywindow_globals_private.m_bCamXYUpdate ) {
//    if ( g_xywindow_globals_private.m_bCamXYUpdate ) {
                //XYWnd_Update( xywnd );
                xywnd.UpdateCameraIcon();
      }
//    }
  }
  
  XYWnd::XYWnd() :
        m_window_observer( NewWindowObserver() ),
        m_XORRectangle( m_gl_widget ),
        m_chasemouse_handler( 0 ){
 +
        m_bActive = false;
        m_buttonstate = 0;
  
  
        Map_addValidCallback( g_map, DeferredDrawOnMapValidChangedCaller( m_deferredDraw ) );
  
 -      updateProjection();
 -      updateModelview();
 +      // This reconstruct=false argument is used to avoid a circular dependency
 +      // between modelview and projection initialization and a valgrind complaint
 +      updateProjection( false );
 +      updateModelview( false );
 +      m_view.Construct( m_projection, m_modelview, m_nWidth, m_nHeight );
  
        AddSceneChangeCallback( ReferenceCaller<XYWnd, void(), &XYWnd_Update>( *this ) );
        AddCameraMovedCallback( ReferenceCaller<XYWnd, void(), &XYWnd_CameraMoved>( *this ) );
@@@ -999,11 -991,14 +999,11 @@@ void XYWnd::Clipper_Crosshair_OnMouseMo
        Vector3 mousePosition;
        XY_ToPoint( x, y, mousePosition );
        if ( ClipMode() && GlobalClipPoints_Find( mousePosition, (VIEWTYPE)m_viewType, m_fScale ) != 0 ) {
 -              GdkCursor *cursor;
 -              cursor = gdk_cursor_new( GDK_CROSSHAIR );
 -              gdk_window_set_cursor( gtk_widget_get_window(m_gl_widget), cursor );
 -              gdk_cursor_unref( cursor );
 +              set_cursor ( m_gl_widget, GDK_CROSSHAIR );
        }
        else
        {
 -              gdk_window_set_cursor( gtk_widget_get_window(m_gl_widget), 0 );
 +              default_cursor( m_gl_widget );
        }
  }
  
@@@ -1020,7 -1015,8 +1020,8 @@@ void XYWnd::SetCustomPivotOrigin( int p
  }
  
  unsigned int MoveCamera_buttons(){
-       return RAD_CONTROL | ( g_glwindow_globals.m_nMouseType == ETwoButton ? RAD_RBUTTON : RAD_MBUTTON );
+ //    return RAD_CONTROL | ( g_glwindow_globals.m_nMouseType == ETwoButton ? RAD_RBUTTON : RAD_MBUTTON );
+       return RAD_CONTROL | RAD_MBUTTON;
  }
  
  void XYWnd_PositionCamera( XYWnd* xywnd, int x, int y, CamWnd& camwnd ){
  }
  
  unsigned int OrientCamera_buttons(){
      if ( g_glwindow_globals.m_nMouseType == ETwoButton ) {
              return RAD_RBUTTON | RAD_SHIFT | RAD_CONTROL;
      }
//    if ( g_glwindow_globals.m_nMouseType == ETwoButton ) {
//            return RAD_RBUTTON | RAD_SHIFT | RAD_CONTROL;
//    }
        return RAD_MBUTTON;
  }
  
  void XYWnd_OrientCamera( XYWnd* xywnd, int x, int y, CamWnd& camwnd ){
+       //globalOutputStream() << Camera_getAngles( camwnd ) << "  b4\n";
        Vector3 point = g_vector3_identity;
        xywnd->XY_ToPoint( x, y, point );
-       xywnd->XY_SnapToGrid( point );
+       //xywnd->XY_SnapToGrid( point );
        vector3_subtract( point, Camera_getOrigin( camwnd ) );
  
        int n1 = ( xywnd->GetViewType() == XY ) ? 1 : 2;
        if ( point[n1] || point[n2] ) {
                Vector3 angles( Camera_getAngles( camwnd ) );
                angles[nAngle] = static_cast<float>( radians_to_degrees( atan2( point[n1], point[n2] ) ) );
+               if( angles[CAMERA_YAW] < 0 )
+                       angles[CAMERA_YAW] = angles[CAMERA_YAW] + 360;
+               if ( nAngle == CAMERA_PITCH ){
+                       if( fabs( angles[CAMERA_PITCH] ) > 90 ){
+                               angles[CAMERA_PITCH] = ( angles[CAMERA_PITCH] > 0 ) ? ( -angles[CAMERA_PITCH] + 180 ) : ( -angles[CAMERA_PITCH] - 180 );
+                               if( xywnd->GetViewType() == YZ ){
+                                       if( angles[CAMERA_YAW] < 180 ){
+                                               angles[CAMERA_YAW] = 360 - angles[CAMERA_YAW];
+                                       }
+                               }
+                               else if( angles[CAMERA_YAW] < 90 || angles[CAMERA_YAW] > 270 ){
+                                       angles[CAMERA_YAW] = 180 - angles[CAMERA_YAW];
+                               }
+                       }
+                       else{
+                               if( xywnd->GetViewType() == YZ ){
+                                       if( angles[CAMERA_YAW] > 180 ){
+                                               angles[CAMERA_YAW] = 360 - angles[CAMERA_YAW];
+                                       }
+                               }
+                               else if( angles[CAMERA_YAW] > 90 && angles[CAMERA_YAW] < 270 ){
+                                       angles[CAMERA_YAW] = 180 - angles[CAMERA_YAW];
+                               }
+                       }
+               }
                Camera_setAngles( camwnd, angles );
        }
+       //globalOutputStream() << Camera_getAngles( camwnd ) << "\n";
  }
  
  unsigned int SetCustomPivotOrigin_buttons(){
@@@ -1136,8 -1159,8 +1164,8 @@@ void entitycreate_activated( ui::Widge
                g_pParentWnd->ActiveXY()->OnEntityCreate( entity_name );
        }
        else {
 -              GlobalRadiant().m_pfnMessageBox( MainFrame_getWindow(), "There's already a worldspawn in your map!"
 -                                                                                                                                                        "",
 +              GlobalRadiant().m_pfnMessageBox( MainFrame_getWindow(),
 +                      "There's already a worldspawn in your map!",
                                                                                 "Info",
                                                                                 eMB_OK,
                                                                                 eMB_ICONDEFAULT );
@@@ -1217,9 -1240,9 +1245,9 @@@ void addItem( const char* name, const c
  };
  
  void XYWnd::OnContextMenu(){
      if ( g_xywindow_globals.m_bRightClick == false ) {
              return;
      }
//    if ( g_xywindow_globals.m_bRightClick == false ) {
//            return;
//    }
  
        if ( !m_mnuDrop ) { // first time, load it up
                auto menu = m_mnuDrop = ui::Menu(ui::New);
@@@ -1252,17 -1275,13 +1280,17 @@@ void XYWnd::Move_Begin()
                Move_End();
        }
        m_move_started = true;
 -      g_xywnd_freezePointer.freeze_pointer( m_parent  ? m_parent : MainFrame_getWindow(), m_gl_widget, XYWnd_moveDelta, this );
 +      /* NetRadiantCustom did this instead:
 +      g_xywnd_freezePointer.freeze_pointer( m_parent  ? m_parent : MainFrame_getWindow(), m_gl_widget, XYWnd_moveDelta, this ); */
 +      g_xywnd_freezePointer.freeze_pointer( m_gl_widget, XYWnd_moveDelta, this );
        m_move_focusOut = m_gl_widget.connect( "focus_out_event", G_CALLBACK( XYWnd_Move_focusOut ), this );
  }
  
  void XYWnd::Move_End(){
        m_move_started = false;
 -      g_xywnd_freezePointer.unfreeze_pointer( m_parent ? m_parent : MainFrame_getWindow(), false );
 +      /* NetRadiant did this instead:
 +      g_xywnd_freezePointer.unfreeze_pointer( m_parent ? m_parent : MainFrame_getWindow(), false ); */
 +      g_xywnd_freezePointer.unfreeze_pointer( m_gl_widget, false );
        g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_move_focusOut );
  }
  
@@@ -1301,9 -1320,7 +1329,9 @@@ void XYWnd::Zoom_Begin()
        }
        m_zoom_started = true;
        g_dragZoom = 0;
 -      g_xywnd_freezePointer.freeze_pointer( m_parent ? m_parent : MainFrame_getWindow(), m_gl_widget, XYWnd_zoomDelta, this );
 +      /* NetRadiantCustom did this instead:
 +      g_xywnd_freezePointer.freeze_pointer( m_parent ? m_parent : MainFrame_getWindow(), m_gl_widget, XYWnd_zoomDelta, this ); */
 +      g_xywnd_freezePointer.freeze_pointer( m_parent ? m_parent : MainFrame_getWindow(), XYWnd_zoomDelta, this );
        m_zoom_focusOut = m_gl_widget.connect( "focus_out_event", G_CALLBACK( XYWnd_Zoom_focusOut ), this );
  }
  
@@@ -1421,12 -1438,12 +1449,12 @@@ void XYWnd::XY_MouseMoved( int x, int y
        }
  
        // control mbutton = move camera
-       else if ( getButtonState() == MoveCamera_buttons() ) {
+       else if ( buttons == MoveCamera_buttons() ) {
                XYWnd_PositionCamera( this, x, y, *g_pParentWnd->GetCamWnd() );
        }
  
        // mbutton = angle camera
-       else if ( getButtonState() == OrientCamera_buttons() ) {
+       else if ( buttons == OrientCamera_buttons() ) {
                XYWnd_OrientCamera( this, x, y, *g_pParentWnd->GetCamWnd() );
        }
  
@@@ -1575,25 -1592,17 +1603,25 @@@ void XYWnd::XY_DisableBackground( void 
  
  void WXY_BackgroundSelect( void ){
        bool brushesSelected = Scene_countSelectedBrushes( GlobalSceneGraph() ) != 0;
 +
 +      ui::Window main_window = MainFrame_getWindow();
 +
        if ( !brushesSelected ) {
 -              ui::alert( ui::root, "You have to select some brushes to get the bounding box for.\n",
 +              ui::alert( main_window, "You have to select some brushes to get the bounding box for.\n",
                                                "No selection", ui::alert_type::OK, ui::alert_icon::Error );
                return;
        }
  
 -      const char *filename = MainFrame_getWindow().file_dialog( TRUE, "Background Image", NULL, NULL );
 +      const char *filename = main_window.file_dialog( TRUE, "Background Image", NULL, NULL );
 +
        g_pParentWnd->ActiveXY()->XY_DisableBackground();
 +
        if ( filename ) {
                g_pParentWnd->ActiveXY()->XY_LoadBackgroundImage( filename );
        }
 +
 +      // Draw the background image immediately (do not wait for user input).
 +      g_pParentWnd->ActiveXY()->Redraw();
  }
  
  /*
@@@ -2061,6 -2070,7 +2089,7 @@@ void XYWnd::XY_DrawBlockGrid()
  void XYWnd::DrawCameraIcon( const Vector3& origin, const Vector3& angles ){
        Cam.fov = 48 / m_fScale;
        Cam.box = 16 / m_fScale;
+ //    globalOutputStream() << "pitch " << angles[CAMERA_PITCH] << "   yaw " << angles[CAMERA_YAW] << "\n";
  
        if ( m_viewType == XY ) {
                Cam.x = origin[0];
        else if ( m_viewType == YZ ) {
                Cam.x = origin[1];
                Cam.y = origin[2];
-               Cam.a = degrees_to_radians( angles[CAMERA_PITCH] );
+               Cam.a = degrees_to_radians( ( angles[CAMERA_YAW] > 180 ) ? ( 180.0f - angles[CAMERA_PITCH] ) : angles[CAMERA_PITCH] );
        }
        else
        {
                Cam.x = origin[0];
                Cam.y = origin[2];
-               Cam.a = degrees_to_radians( angles[CAMERA_PITCH] );
+               Cam.a = degrees_to_radians( ( angles[CAMERA_YAW] < 270 && angles[CAMERA_YAW] > 90 ) ? ( 180.0f - angles[CAMERA_PITCH] ) : angles[CAMERA_PITCH] );
        }
  
        //glColor3f( 0.0, 0.0, 1.0 );
@@@ -2364,7 -2374,7 +2393,7 @@@ RenderStateFlags m_globalstate
  Shader* m_state_selected;
  };
  
 -void XYWnd::updateProjection(){
 +void XYWnd::updateProjection( bool reconstruct ){
        m_projection[0] = 1.0f / static_cast<float>( m_nWidth / 2 );
        m_projection[5] = 1.0f / static_cast<float>( m_nHeight / 2 );
        m_projection[10] = 1.0f / ( g_MaxWorldCoord * m_fScale );
  
        m_projection[15] = 1.0f;
  
 +      if (reconstruct) {
        m_view.Construct( m_projection, m_modelview, m_nWidth, m_nHeight );
  }
 +}
  
  // note: modelview matrix must have a uniform scale, otherwise strange things happen when rendering the rotation manipulator.
 -void XYWnd::updateModelview(){
 +void XYWnd::updateModelview( bool reconstruct ){
        int nDim1 = ( m_viewType == YZ ) ? 1 : 0;
        int nDim2 = ( m_viewType == XY ) ? 1 : 2;
  
        m_modelview[3] = m_modelview[7] = m_modelview[11] = 0;
        m_modelview[15] = 1;
  
 +      if (reconstruct) {
        m_view.Construct( m_projection, m_modelview, m_nWidth, m_nHeight );
 +      }
  }
  
  /*
@@@ -3044,7 -3050,7 +3073,7 @@@ void Orthographic_constructPreferences
        page.appendCheckBox( "", "Solid selection boxes ( no stipple )", g_xywindow_globals.m_bNoStipple );
        //page.appendCheckBox( "", "Display size info", g_xywindow_globals_private.m_bSizePaint );
        page.appendCheckBox( "", "Chase mouse during drags", g_xywindow_globals_private.m_bChaseMouse );
      page.appendCheckBox( "", "Update views on camera move", g_xywindow_globals_private.m_bCamXYUpdate );
//    page.appendCheckBox( "", "Update views on camera move", g_xywindow_globals_private.m_bCamXYUpdate );
  }
  void Orthographic_constructPage( PreferenceGroup& group ){
        PreferencesPage page( group.createPage( "Orthographic", "Orthographic View Preferences" ) );
@@@ -3100,7 -3106,7 +3129,7 @@@ void XYWindow_Construct()
  
        GlobalPreferenceSystem().registerPreference( "ClipCaulk", make_property_string( g_clip_useCaulk ) );
  
      GlobalPreferenceSystem().registerPreference( "NewRightClick", make_property_string( g_xywindow_globals.m_bRightClick ) );
//    GlobalPreferenceSystem().registerPreference( "NewRightClick", make_property_string( g_xywindow_globals.m_bRightClick ) );
        GlobalPreferenceSystem().registerPreference( "ImprovedWheelZoom", make_property_string( g_xywindow_globals.m_bImprovedWheelZoom ) );
        GlobalPreferenceSystem().registerPreference( "ChaseMouse", make_property_string( g_xywindow_globals_private.m_bChaseMouse ) );
        GlobalPreferenceSystem().registerPreference( "SizePainting", make_property_string( g_xywindow_globals_private.m_bSizePaint ) );
        GlobalPreferenceSystem().registerPreference( "SI_ShowCoords", make_property_string( g_xywindow_globals_private.show_coordinates ) );
        GlobalPreferenceSystem().registerPreference( "SI_ShowOutlines", make_property_string( g_xywindow_globals_private.show_outline ) );
        GlobalPreferenceSystem().registerPreference( "SI_ShowAxis", make_property_string( g_xywindow_globals_private.show_axis ) );
      GlobalPreferenceSystem().registerPreference( "CamXYUpdate", make_property_string( g_xywindow_globals_private.m_bCamXYUpdate ) );
//    GlobalPreferenceSystem().registerPreference( "CamXYUpdate", make_property_string( g_xywindow_globals_private.m_bCamXYUpdate ) );
        GlobalPreferenceSystem().registerPreference( "ShowWorkzone", make_property_string( g_xywindow_globals_private.d_show_work ) );
  
        GlobalPreferenceSystem().registerPreference( "SI_AxisColors0", make_property_string( g_xywindow_globals.AxisColorX ) );
diff --combined radiant/xywindow.h
index 36a5f1cb786258cf6f4999ebfe918b9ca9131487,1097a28c82a703bd9a56a47deb4122f94db9b1a3..41fb89f89b9b053d6ce663a4687ce9fb3e722156
@@@ -128,10 -128,7 +128,10 @@@ void ZoomIn()
  void ZoomOut();
  void ZoomInWithMouse( int pointx, int pointy );
  
 +void Redraw();
 +
  void RenderActive();
 +
  void SetActive( bool b ){
        m_bActive = b;
        RenderActive();
@@@ -169,8 -166,8 +169,8 @@@ guint m_chasemouse_handler
  void ChaseMouse();
  bool chaseMouseMotion( int pointx, int pointy );
  
 -void updateModelview();
 -void updateProjection();
 +void updateModelview(bool reconstruct = true);
 +void updateProjection(bool reconstruct = true);
  Matrix4 m_projection;
  Matrix4 m_modelview;
  
@@@ -213,10 -210,12 +213,12 @@@ bool m_entityCreate
  
  public:
  void ButtonState_onMouseDown( unsigned int buttons ){
-       m_buttonstate |= buttons;
+       //m_buttonstate |= buttons;
+       m_buttonstate = buttons;
  }
  void ButtonState_onMouseUp( unsigned int buttons ){
-       m_buttonstate &= ~buttons;
+       //m_buttonstate &= ~buttons;
+       m_buttonstate = 0;
  }
  unsigned int getButtonState() const {
        return m_buttonstate;
@@@ -262,13 -261,11 +264,13 @@@ struct xywindow_globals_
        Vector3 color_selbrushes;
        Vector3 color_clipper;
        Vector3 color_viewname;
 +      Vector3 color_gridminor_alt;
 +      Vector3 color_gridmajor_alt;
        Vector3 AxisColorX;
        Vector3 AxisColorY;
        Vector3 AxisColorZ;
  
      bool m_bRightClick;
//    bool m_bRightClick;
        bool m_bNoStipple;
        bool m_bImprovedWheelZoom;
  
                color_selbrushes( 1.f, 0.f, 0.f ),
                color_clipper( 0.f, 0.f, 1.f ),
                color_viewname( 0.5f, 0.f, 0.75f ),
 +              color_gridminor_alt( 0.f, 0.f, 0.f ),
 +              color_gridmajor_alt( 0.f, 0.f, 0.f ),
  
                AxisColorX( 1.f, 0.f, 0.f ),
                AxisColorY( 0.f, 1.f, 0.f ),
                AxisColorZ( 0.f, 0.f, 1.f ),
              m_bRightClick( true ),
//            m_bRightClick( true ),
                m_bNoStipple( true ),
                m_bImprovedWheelZoom( true ){
        }