]> git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Merge commit 'c3765eb6e7f6879e5a34c48f71e5ee7bc8b8851f' into master-merge
authorThomas Debesse <dev@illwieckz.net>
Mon, 20 Jun 2022 02:44:49 +0000 (04:44 +0200)
committerThomas Debesse <dev@illwieckz.net>
Mon, 20 Jun 2022 02:44:49 +0000 (04:44 +0200)
1  2 
Makefile
contrib/camera/camera.cpp
radiant/CMakeLists.txt
radiant/environment.cpp
radiant/environment.h
radiant/main.cpp
radiant/mainframe.cpp
radiant/plugintoolbar.cpp

diff --combined Makefile
index 4bc44466a271318d596b54fb4f8e55ce66112502,8ab3f4daab13ba1b4b9d1d2ee2e261006340a786..4a4e0bb2084e1a9e2e19084427b93bb22e8e8116
+++ b/Makefile
@@@ -1,13 -1,3 +1,13 @@@
 +# TODO: when deleting this Makefile, don't forget to also delete conftest.cpp
 +# which is only required by this Makefile
 +
 +ifeq ($(I_KNOW_MAKEFILE_IS_DEPRECATED),)
 +    $(shell printf 'ERROR: Makefile build is deprecated, use CMake instead, see README.md\n\n' >&2)
 +    $(error I_KNOW_MAKEFILE_IS_DEPRECATED is not set)
 +else
 +    $(shell printf 'WARNING: deprecated Makefile build enforced\n\n' >&2)
 +endif
 +
  MAKEFILE_CONF      ?= Makefile.conf
  -include $(MAKEFILE_CONF)
  
@@@ -24,8 -14,6 +24,8 @@@ CXXFLAGS           ?
  CPPFLAGS           ?=
  LIBS               ?=
  RADIANT_ABOUTMSG   ?= Custom build
 +RADIANT_NAME       ?= NetRadiant
 +RADIANT_BASENAME   ?= netradiant
  
  # warning: this directory may NOT contain any files other than the ones written by this Makefile!
  # NEVER SET THIS TO A SYSTEM WIDE "bin" DIRECTORY!
@@@ -210,7 -198,7 +210,7 @@@ ifeq ($(OS),Linux
  else
  
  ifeq ($(OS),Win32)
 -      CPPFLAGS_COMMON += -DWIN32 -D_WIN32 -D_inline=inline
 +      CPPFLAGS_COMMON += -DWIN32 -D_WIN32 -D_inline=inline -DWORKAROUND_WINDOWS_FLOATING_WINDOW=1 -DWORKAROUND_WINDOWS_GTK2_GLWIDGET=1
        CFLAGS_COMMON += -mms-bitfields
        LDFLAGS_DLL = -Wl,--add-stdcall-alias
        LIBS_COMMON = -lws2_32 -luser32 -lgdi32 -lole32
@@@ -264,13 -252,16 +264,13 @@@ RADIANT_VERSION_NUMBER = $(RADIANT_MAJO
  RADIANT_VERSION = $(RADIANT_VERSION_NUMBER)n
  Q3MAP_VERSION = 2.5.17n
  
 -# Executable extension
 -RADIANT_EXECUTABLE := $(EXE)
 -
  GIT_VERSION := $(shell $(GIT) rev-parse --short HEAD $(STDERR_TO_DEVNULL))
  ifneq ($(GIT_VERSION),)
        RADIANT_VERSION := $(RADIANT_VERSION)-git-$(GIT_VERSION)
        Q3MAP_VERSION := $(Q3MAP_VERSION)-git-$(GIT_VERSION)
  endif
  
 -CPPFLAGS += -DRADIANT_VERSION="\"$(RADIANT_VERSION)\"" -DRADIANT_MAJOR_VERSION="\"$(RADIANT_MAJOR_VERSION)\"" -DRADIANT_MINOR_VERSION="\"$(RADIANT_MINOR_VERSION)\"" -DRADIANT_PATCH_VERSION="\"$(RADIANT_PATCH_VERSION)\"" -DRADIANT_ABOUTMSG="\"$(RADIANT_ABOUTMSG)\"" -DQ3MAP_VERSION="\"$(Q3MAP_VERSION)\"" -DRADIANT_EXECUTABLE="\"$(RADIANT_EXECUTABLE)\""
 +CPPFLAGS += -DRADIANT_VERSION="\"$(RADIANT_VERSION)\"" -DRADIANT_VERSION_STRING="\"$(RADIANT_VERSION_STRING)\"" -DRADIANT_ABOUTMSG="\"$(RADIANT_ABOUTMSG)\"" -DRADIANT_NAME="\"${RADIANT_NAME}\"" -DRADIANT_BASENAME="\"${RADIANT_BASENAME}\"" -DQ3MAP_VERSION="\"$(Q3MAP_VERSION)\""
  CPPFLAGS += -DGTK_TARGET=2
  
  .PHONY: all
@@@ -441,8 -432,8 +441,8 @@@ binaries-qdata3: 
  
  .PHONY: binaries-h2data
  binaries-h2data: \
 -      $(INSTALLDIR)/heretic2/h2data.$(EXE) \
 -      $(INSTALLDIR)/heretic2/h2data \
 +      $(INSTALLDIR)/h2data.$(EXE) \
 +      $(INSTALLDIR)/h2data \
  
  .PHONY: binaries-tools-quake3
  binaries-tools-quake3: \
@@@ -674,6 -665,7 +674,7 @@@ $(INSTALLDIR)/radiant.$(EXE): 
        radiant/error.o \
        radiant/feedback.o \
        radiant/filetypes.o \
+       radiant/filterbar.o \
        radiant/filters.o \
        radiant/findtexturedialog.o \
        radiant/glwidget.o \
@@@ -1027,45 -1019,46 +1028,45 @@@ $(INSTALLDIR)/plugins/bkgrnd2d.$(DLL): 
        contrib/bkgrnd2d/dialog.o \
        contrib/bkgrnd2d/plugin.o \
  
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): LIBS_EXTRA := $(LIBS_XML)
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) -Itools/quake2/qdata_heretic2/common -Itools/quake2/qdata_heretic2/qcommon -Itools/quake2/qdata_heretic2 -Itools/quake2/common -Ilibs -Iinclude
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): \
 -      tools/quake2/qdata_heretic2/common/bspfile.o \
 -      tools/quake2/qdata_heretic2/common/cmdlib.o \
 -      tools/quake2/qdata_heretic2/common/inout.o \
 -      tools/quake2/qdata_heretic2/common/l3dslib.o \
 -      tools/quake2/qdata_heretic2/common/lbmlib.o \
 -      tools/quake2/qdata_heretic2/common/mathlib.o \
 -      tools/quake2/qdata_heretic2/common/md4.o \
 -      tools/quake2/qdata_heretic2/common/path_init.o \
 -      tools/quake2/qdata_heretic2/common/qfiles.o \
 -      tools/quake2/qdata_heretic2/common/scriplib.o \
 -      tools/quake2/qdata_heretic2/common/threads.o \
 -      tools/quake2/qdata_heretic2/common/token.o \
 -      tools/quake2/qdata_heretic2/common/trilib.o \
 -      tools/quake2/qdata_heretic2/qcommon/reference.o \
 -      tools/quake2/qdata_heretic2/qcommon/resourcemanager.o \
 -      tools/quake2/qdata_heretic2/qcommon/skeletons.o \
 -      tools/quake2/qdata_heretic2/animcomp.o \
 -      tools/quake2/qdata_heretic2/book.o \
 -      tools/quake2/qdata_heretic2/fmodels.o \
 -      tools/quake2/qdata_heretic2/images.o \
 -      tools/quake2/qdata_heretic2/jointed.o \
 -      tools/quake2/qdata_heretic2/models.o \
 -      tools/quake2/qdata_heretic2/pics.o \
 -      tools/quake2/qdata_heretic2/qdata.o \
 -      tools/quake2/qdata_heretic2/qd_skeletons.o \
 -      tools/quake2/qdata_heretic2/sprites.o \
 -      tools/quake2/qdata_heretic2/svdcmp.o \
 -      tools/quake2/qdata_heretic2/tables.o \
 -      tools/quake2/qdata_heretic2/tmix.o \
 -      tools/quake2/qdata_heretic2/video.o \
 +$(INSTALLDIR)/h2data.$(EXE): LIBS_EXTRA := $(LIBS_XML)
 +$(INSTALLDIR)/h2data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) -Itools/heretic2/common -Itools/heretic2/qcommon -Itools/heretic2/h2data -Itools/quake2/common -Ilibs -Iinclude
 +$(INSTALLDIR)/h2data.$(EXE): \
 +      tools/heretic2/common/bspfile.o \
 +      tools/heretic2/common/cmdlib.o \
 +      tools/heretic2/common/inout.o \
 +      tools/heretic2/common/l3dslib.o \
 +      tools/heretic2/common/lbmlib.o \
 +      tools/heretic2/common/mathlib.o \
 +      tools/heretic2/common/md4.o \
 +      tools/heretic2/common/path_init.o \
 +      tools/heretic2/common/qfiles.o \
 +      tools/heretic2/common/scriplib.o \
 +      tools/heretic2/common/threads.o \
 +      tools/heretic2/common/token.o \
 +      tools/heretic2/common/trilib.o \
 +      tools/heretic2/qcommon/reference.o \
 +      tools/heretic2/qcommon/resourcemanager.o \
 +      tools/heretic2/qcommon/skeletons.o \
 +      tools/heretic2/h2data/animcomp.o \
 +      tools/heretic2/h2data/book.o \
 +      tools/heretic2/h2data/fmodels.o \
 +      tools/heretic2/h2data/images.o \
 +      tools/heretic2/h2data/jointed.o \
 +      tools/heretic2/h2data/models.o \
 +      tools/heretic2/h2data/pics.o \
 +      tools/heretic2/h2data/qdata.o \
 +      tools/heretic2/h2data/qd_skeletons.o \
 +      tools/heretic2/h2data/sprites.o \
 +      tools/heretic2/h2data/svdcmp.o \
 +      tools/heretic2/h2data/tables.o \
 +      tools/heretic2/h2data/tmix.o \
 +      tools/heretic2/h2data/video.o \
        libl_net.$(A) \
        $(if $(findstring $(OS),Win32),icons/h2data.o,) \
  
  .PHONY: install-data
  install-data: binaries
 -      $(MKDIR) $(INSTALLDIR)/games
 -      DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" DOWNLOADDIR="$(DOWNLOADDIR)" INSTALLDIR="$(INSTALLDIR)" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" SH="$(SH)" CP="$(CP)" CP_R="$(CP_R)" $(SH) gamepack-manager
 +      DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" DOWNLOADDIR="$(DOWNLOADDIR)" INSTALLDIR="$(INSTALLDIR)/gamepacks" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" CP="$(CP)" CP_R="$(CP_R)" ./gamepack-manager
        $(ECHO) $(RADIANT_MAJOR_VERSION) > $(INSTALLDIR)/RADIANT_MAJOR
        $(ECHO) $(RADIANT_MINOR_VERSION) > $(INSTALLDIR)/RADIANT_MINOR
        $(ECHO) $(RADIANT_PATCH_VERSION) > $(INSTALLDIR)/RADIANT_PATCH
index 08b0b5bbbc6eca36f0969db174e5ff8d355442c2,60996afc67b373563e26c1d94013145058a97bf1..3831c0beb384a91bac81c340e3cd6a31fafa096b
@@@ -49,17 -49,12 +49,17 @@@ int g_iEditMode = 0
  int g_iActiveTarget = -1;
  int g_iPreviewRunning = 0;              // 0: no preview 1: start preview 2: preview in progress
  
 -static const char *PLUGIN_ABOUT = "Camera v1.0 for NetRadiant\n"
 -                                                                "by Arnout van Meer (rr2do2@splashdamage.com)\n\n"
 -                                                                "This product contains software technology\n"
 -                                                                "from id Software, Inc. ('id Technology').\n"
 -                                                                "id Technology (c) 2001, 2002 id Software, Inc.";
 -
 +static const char *PLUGIN_ABOUT =
 +                      PLUGIN_NAME " " PLUGIN_VERSION " for "
 +                      RADIANT_NAME " " RADIANT_VERSION "\n\n"
 +                      "Written by Arnout van Meer <rr2do2@splashdamage.com)\n\n"
 +                      "Written by Geoffrey DeWan <gdewan@prairienet.org>\n\n"
 +                      "This product contains software technology\n"
 +                      "from id Software, Inc. ('id Technology').\n"
 +                      "id Technology (c) 2001, 2002 id Software, Inc.\n\n";
 +                      "Built against "
 +                      RADIANT_NAME " " RADIANT_VERSION "\n"
 +                      __DATE__;
  
  #include "iplugin.h"
  
@@@ -89,7 -84,7 +89,7 @@@ const char* QERPlug_Init( void* hApp, v
  
        GetFileTypeRegistry()->addType( "camera", "", filetype_t( "Camera file", "*.camera" ) );
  
 -      return "Camera for NetRadiant";
 +      return "Camera for " RADIANT_NAME;
  }
  
  const char* QERPlug_GetName(){
@@@ -137,7 -132,7 +137,7 @@@ class CameraInspectorButton : public IT
  {
  public:
  virtual const char* getImage() const {
-       return "camera_insp.jpg";
+       return "camera_insp.png";
  }
  virtual const char* getText() const {
        return "Inspector";
@@@ -226,6 -221,8 +226,6 @@@ bool CameraSynapseClient::RequestAPI( A
        return false;
  }
  
 -#include "version.h"
 -
  const char* CameraSynapseClient::GetInfo(){
        return "Camera plugin v1.0 - Arnout van Meer - built " __DATE__ " " RADIANT_VERSION;
  }
diff --combined radiant/CMakeLists.txt
index f93b00cf1329a8662fb132c1cd897372a38808a9,1edced736eade534b52a073ac430bd96d805b16d..08e76bcf0a839f78b90ca38f14ca5432df6b263e
@@@ -34,8 -34,10 +34,9 @@@ set(RADIANTLIS
      error.cpp error.h
      feedback.cpp feedback.h
      filetypes.cpp filetypes.h
+     filterbar.cpp filterbar.h
      filters.cpp filters.h
      findtexturedialog.cpp findtexturedialog.h
 -    glwidget.cpp glwidget.h
      grid.cpp grid.h
      groupdialog.cpp groupdialog.h
      gtkdlgs.cpp gtkdlgs.h
      xmlstuff.cpp xmlstuff.h
      xywindow.cpp xywindow.h
  )
 +
  if (WIN32)
      list(APPEND RADIANTLIST multimon.cpp multimon.h)
 -endif()
 +endif ()
  
 -radiant_tool(radiant WIN32 radiant.rc ${RADIANTLIST})
 -add_dependencies(radiant modules)
 -target_link_libraries(radiant
 +radiant_tool(${RADIANT_BASENAME} WIN32 radiant.rc ${RADIANTLIST})
 +add_dependencies(${RADIANT_BASENAME} modules)
 +target_link_libraries(${RADIANT_BASENAME}
      ${CMAKE_DL_LIBS}
      ${LIBXML2_LIBRARIES}
      ${OPENGL_gl_LIBRARY}
      splines
      stream
      string
 +    transformpath
      uilib
      xmllib
  )
 +
  if (X11_LIBRARIES)
 -    target_link_libraries(radiant ${X11_LIBRARIES})
 +    target_link_libraries(${RADIANT_BASENAME} ${X11_LIBRARIES})
  endif ()
  
 -copy_dlls(radiant)
 +# PIE executables were wrongly recognized as shared libraries on Linux
 +# because of a mistake in the mime-type library.
 +# Because of that it was not possible to launch such executables from
 +# the file browser.
 +#
 +# While the problem is fixed upstream, most distributions still ship
 +# with a faulty mime-type library and may do for a long time.
 +#
 +# See: https://gitlab.freedesktop.org/xdg/shared-mime-info/-/issues/11
 +#
 +# It may produce such warning though at strip time:
 +#   warning: allocated section `.dynstr' not in segment
 +# and produce an unusable binary, better not strip when using this hack.
 +#
 +# This hack is only wanted for release build and
 +# when NetRadiant is not installed system-wide.
 +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux"
 +    AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release"
 +    AND NOT FHS_INSTALL)
 +    target_link_libraries(${RADIANT_BASENAME} -no-pie)
 +endif()
 +
 +copy_dlls(${RADIANT_BASENAME})
diff --combined radiant/environment.cpp
index e9cf2dc513e6f40f5cb986055fea64049435ab7b,a84a7d42bc7ba0b10fc25b465aa2098fbff582ae..bd07533a6825ab1f06178d02ab57d606830f8194
@@@ -140,10 -140,8 +140,10 @@@ void gamedetect()
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz.exe", buf, p - buf ) )
  #elif GDEF_OS_MACOS
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "Nexuiz.app/Contents/Info.plist", buf, p - buf ) )
 -#else
 +#elif GDEF_OS_LINUX
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz-linux-glx.sh", buf, p - buf ) )
 +#else
 +                      if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", NULL, buf, p - buf ) )
  #endif
                        { return; }
  
  
  namespace
  {
 -CopiedString home_path;
 -CopiedString app_path;
 +      // executable file path
 +      CopiedString app_filepath;
 +      // directory paths
 +      CopiedString home_path;
 +      CopiedString app_path;
 +      CopiedString lib_path;
 +      CopiedString data_path;
 +}
 +
 +const char* environment_get_app_filepath(){
 +      return app_filepath.c_str();
  }
  
  const char* environment_get_home_path(){
@@@ -189,20 -178,10 +189,20 @@@ const char* environment_get_app_path()
        return app_path.c_str();
  }
  
 +const char *environment_get_lib_path()
 +{
 +      return lib_path.c_str();
 +}
 +
 +const char *environment_get_data_path()
 +{
 +      return data_path.c_str();
 +}
 +
  bool portable_app_setup(){
        StringOutputStream confdir( 256 );
        confdir << app_path.c_str() << "settings/";
 -      if ( file_exists( confdir.c_str() ) ) {
 +      if ( file_is_directory( confdir.c_str() ) ) {
                home_path = confdir.c_str();
                return true;
        }
  const char* LINK_NAME =
  #if GDEF_OS_LINUX
        "/proc/self/exe"
 -#else // FreeBSD and OSX
 +#else // FreeBSD and macOS
        "/proc/curproc/file"
  #endif
  ;
  
 -/// brief Returns the filename of the executable belonging to the current process, or 0 if not found.
 +/// brief Returns the filename of the executable belonging to the current process, or empty string if not found.
  char const* getexename( char *buf ){
        /* Now read the symbolic link */
 -      int ret = readlink( LINK_NAME, buf, PATH_MAX );
 +      const int ret = readlink( LINK_NAME, buf, PATH_MAX );
  
        if ( ret == -1 ) {
                globalOutputStream() << "getexename: falling back to argv[0]: " << makeQuoted( g_argv[0] );
 -              const char* path = realpath( g_argv[0], buf );
 -              if ( path == 0 ) {
 +              if( realpath( g_argv[0], buf ) == 0 ) {
                        /* In case of an error, leave the handling up to the caller */
 -                      return "";
 +                      *buf = '\0';
                }
        }
 +      else {
 +              /* Ensure proper NUL termination */
 +              buf[ret] = 0;
 +      }
  
 -      /* Ensure proper NUL termination */
 -      buf[ret] = 0;
 +      return buf;
 +}
  
 +char const* getexepath( char *buf ) {
        /* delete the program name */
        *( strrchr( buf, '/' ) ) = '\0';
  
@@@ -273,69 -248,16 +273,69 @@@ void environment_init( int argc, char c
  
        {
                char real[PATH_MAX];
 -              app_path = getexename( real );
 -              ASSERT_MESSAGE( !string_empty( app_path.c_str() ), "failed to deduce app path" );
 +              app_filepath = getexename( real );
 +              ASSERT_MESSAGE( !string_empty( app_filepath.c_str() ), "failed to deduce app path" );
 +
 +              strncpy( real, app_filepath.c_str(), strlen( app_filepath.c_str() ) );
 +              app_path = getexepath( real );
 +      }
 +
 +      {
 +#if defined(RADIANT_FHS_INSTALL)
 +              StringOutputStream buffer;
 +      #if defined(RADIANT_ADDONS_DIR)
 +              buffer << RADIANT_ADDONS_DIR << "/";
 +      #else
 +              buffer << app_path.c_str() << "../lib/";
 +              buffer << RADIANT_LIB_ARCH << "/";
 +              buffer << RADIANT_BASENAME << "/";
 +      #endif
 +              lib_path = buffer.c_str();
 +#else
 +              lib_path = app_path.c_str();
 +#endif
 +      }
 +
 +      {
 +#if defined(RADIANT_FHS_INSTALL)
 +              StringOutputStream buffer;
 +      #if defined(RADIANT_DATA_DIR)
 +              buffer << RADIANT_DATA_DIR << "/";
 +      #else
 +              buffer << app_path.c_str() << "../share/";
 +              buffer << RADIANT_BASENAME << "/";
 +      #endif
 +              data_path = buffer.c_str();
 +#else
 +              data_path = app_path.c_str();
 +#endif
        }
  
        if ( !portable_app_setup() ) {
                StringOutputStream home( 256 );
 -              home << DirectoryCleaned( g_get_user_config_dir() ) << "netradiant/";
 +#if GDEF_OS_MACOS
 +              /* This is used on macOS, this will produce
 +              ~/Library/Application Support/NetRadiant folder. */
 +              home << DirectoryCleaned( g_get_home_dir() );
 +              Q_mkdir( home.c_str() );
 +              home << "Library/";
 +              Q_mkdir( home.c_str() );
 +              home << "Application Support/";
 +              Q_mkdir( home.c_str() );
 +              home << RADIANT_NAME << "/";
 +#else // if GDEF_OS_XDG
 +              /* This is used on both Linux and FreeBSD,
 +              this will produce ~/.config/netradiant folder
 +              when environment has default settings, the
 +              XDG_CONFIG_HOME variable modifies it. */
 +              home << DirectoryCleaned( g_get_user_config_dir() );
 +              Q_mkdir( home.c_str() );
 +              home << RADIANT_BASENAME << "/";
 +#endif // ! GDEF_OS_MACOS
                Q_mkdir( home.c_str() );
                home_path = home.c_str();
        }
 +
        gamedetect();
  }
  
  
  #include <windows.h>
  
- char openCmdMap[260];
+ char* openCmdMap;
  
  void cmdMap(){
-       openCmdMap[0] = '\0';
+       openCmdMap = NULL;
        for ( int i = 1; i < g_argc; ++i )
        {
                if ( !stricmp( g_argv[i] + strlen(g_argv[i]) - 4, ".map" ) ){
-                       strcpy( openCmdMap, g_argv[i] );
+                       openCmdMap = g_argv[i];
                }
        }
  }
@@@ -361,14 -283,7 +361,14 @@@ void environment_init( int argc, char c
        {
                // get path to the editor
                char filename[MAX_PATH + 1];
 +              StringOutputStream app_filepath_stream( 256 );
 +              StringOutputStream app_path_stream( 256 );
 +
                GetModuleFileName( 0, filename, MAX_PATH );
 +              
 +              app_filepath_stream << PathCleaned( filename );
 +              app_filepath = app_filepath_stream.c_str();
 +
                char* last_separator = strrchr( filename, '\\' );
                if ( last_separator != 0 ) {
                        *( last_separator + 1 ) = '\0';
                {
                        filename[0] = '\0';
                }
 -              StringOutputStream app( 256 );
 -              app << PathCleaned( filename );
 -              app_path = app.c_str();
 +
 +              app_path_stream << PathCleaned( filename );
 +              app_path = app_path_stream.c_str();
 +
 +              lib_path = app_path;
 +              data_path = app_path;
        }
  
        if ( !portable_app_setup() ) {
                char *appdata = getenv( "APPDATA" );
 +
                StringOutputStream home( 256 );
                home << PathCleaned( appdata );
 -              home << "/NetRadiantSettings/";
 +              home << "/";
 +              home << RADIANT_NAME;
 +              home << "/";
 +
                Q_mkdir( home.c_str() );
                home_path = home.c_str();
        }
diff --combined radiant/environment.h
index 19209d0fafeb41183c2ad68479331bbbcca4bce9,98c33ab94568a90d726896e0298ac94db8f97969..2e57fb123c677ea1cf3fe0025f999ee591f20c2f
  #define INCLUDED_ENVIRONMENT_H
  
  void environment_init( int argc, char const* argv[] );
 +
 +const char* environment_get_app_filepath();
 +
  const char* environment_get_home_path();
  const char* environment_get_app_path();
 +const char *environment_get_lib_path();
 +const char *environment_get_data_path();
  
  extern int g_argc;
  extern char const** g_argv;
  
  #if defined( WIN32 )
- extern char openCmdMap[260];
+ extern char* openCmdMap;
  #endif
  
  
diff --combined radiant/main.cpp
index ef559617ef9ab088e2ba6e0f5bd01200c809fe8f,d27d8c6b11a0ae78f93a8955e2ffe1710aa99154..8564fc7b62036596cee9ecbe4bb108d44953f26f
@@@ -64,6 -64,8 +64,6 @@@
  #include "main.h"
  #include "globaldefs.h"
  
 -#include "version.h"
 -
  #include "debugging/debugging.h"
  
  #include "iundo.h"
@@@ -302,12 -304,12 +302,12 @@@ bool handleMessage()
                ScopedLock lock( m_lock );
          if (GDEF_DEBUG) {
              m_buffer << "Break into the debugger?\n";
 -            bool handled = ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
 +            bool handled = ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
              m_buffer.clear();
              return handled;
          } else {
              m_buffer << "Please report this error to the developers\n";
 -            ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
 +            ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
              m_buffer.clear();
          }
        }
@@@ -327,10 -329,7 +327,10 @@@ void paths_init()
  
        Q_mkdir( g_strSettingsPath.c_str() );
  
 +      g_strAppFilePath = environment_get_app_filepath();
        g_strAppPath = environment_get_app_path();
 +      g_strLibPath = environment_get_lib_path();
 +      g_strDataPath = environment_get_data_path();
  
        // radiant is installed in the parent dir of "tools/"
        // NOTE: this is not very easy for debugging
        // (for now I had to create symlinks)
        {
                StringOutputStream path( 256 );
 -              path << g_strAppPath.c_str() << "bitmaps/";
 +              path << g_strDataPath.c_str() << "bitmaps/";
                BitmapsPath_set( path.c_str() );
        }
  
        // we will set this right after the game selection is done
 -      g_strGameToolsPath = g_strAppPath;
 +      g_strGameToolsPath = g_strDataPath;
  }
  
  bool check_version_file( const char* filename, const char* version ){
        return false;
  }
  
 -bool check_version(){
 -      // a safe check to avoid people running broken installations
 -      // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
 -      // make something idiot proof and someone will make better idiots, this may be overkill
 -      // let's leave it disabled in debug mode in any case
 -      // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
 -    if (GDEF_DEBUG) {
 -        return true;
 -    }
 -    // locate and open RADIANT_MAJOR and RADIANT_MINOR
 -    bool bVerIsGood = true;
 -    {
 -        StringOutputStream ver_file_name(256);
 -        ver_file_name << AppPath_get() << "RADIANT_MAJOR";
 -        bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
 -    }
 -    {
 -        StringOutputStream ver_file_name(256);
 -        ver_file_name << AppPath_get() << "RADIANT_MINOR";
 -        bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
 -    }
 -
 -    if (!bVerIsGood) {
 -        StringOutputStream msg(256);
 -        msg
 -                << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
 -                        "Make sure you run the right/latest editor binary you installed\n"
 -                << AppPath_get();
 -        ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Default);
 -    }
 -    return bVerIsGood;
 -}
 -
  void create_global_pid(){
        /*!
           the global prefs loading / game selection dialog might fail for any reason we don't know about
                if ( remove( g_pidFile.c_str() ) == -1 ) {
                        StringOutputStream msg( 256 );
                        msg << "WARNING: Could not delete " << g_pidFile.c_str();
 -                      ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +                      ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
                }
  
                // in debug, never prompt to clean registry, turn console logging auto after a failed start
                if (!GDEF_DEBUG) {
                        StringOutputStream msg(256);
 -                      msg << "Radiant failed to start properly the last time it was run.\n"
 +                      msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
                                        "The failure may be related to current global preferences.\n"
                                        "Do you want to reset global preferences to defaults?";
  
 -                      if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
 +                      if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
                                g_GamesDialog.Reset();
                        }
  
                        msg.clear();
                        msg << "Logging console output to " << SettingsPath_get()
 -                              << "radiant.log\nRefer to the log if Radiant fails to start again.";
 +                              << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
  
 -                      ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
 +                      ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
                }
  
                // set without saving, the class is not in a coherent state yet
                // just do the value change and call to start logging, CGamesDialog will pickup when relevant
                g_GamesDialog.m_bForceLogConsole = true;
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
        }
  
        // create a primary .pid for global init run
@@@ -425,7 -457,7 +425,7 @@@ void remove_global_pid()
        if ( remove( g_pidFile.c_str() ) == -1 ) {
                StringOutputStream msg( 256 );
                msg << "WARNING: Could not delete " << g_pidFile.c_str();
 -              ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +              ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
        }
  }
  
@@@ -443,30 -475,30 +443,30 @@@ void create_local_pid()
                if ( remove( g_pidGameFile.c_str() ) == -1 ) {
                        StringOutputStream msg;
                        msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
 -                      ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +                      ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
                }
  
                // in debug, never prompt to clean registry, turn console logging auto after a failed start
                if (!GDEF_DEBUG) {
                        StringOutputStream msg;
 -                      msg << "Radiant failed to start properly the last time it was run.\n"
 +                      msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
                                        "The failure may be caused by current preferences.\n"
                                        "Do you want to reset all preferences to defaults?";
  
 -                      if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
 +                      if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
                                Preferences_Reset();
                        }
  
                        msg.clear();
                        msg << "Logging console output to " << SettingsPath_get()
 -                              << "radiant.log\nRefer to the log if Radiant fails to start again.";
 +                              << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
  
 -                      ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
 +                      ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
                }
  
                // force console logging on! (will go in prefs too)
                g_GamesDialog.m_bForceLogConsole = true;
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
        }
        else
        {
@@@ -502,23 -534,7 +502,23 @@@ void user_shortcuts_save()
        SaveCommandMap( path.c_str() );
  }
  
 +/* 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. */
 +bool g_dontStart = false;
 +
  int main( int argc, char* argv[] ){
 +#if GTK_TARGET == 3
 +      // HACK: force legacy GL backend as we don't support GL3 yet
 +      setenv("GDK_GL", "LEGACY", 0);
 +#if GDEF_OS_LINUX || GDEF_OS_BSD
 +      setenv("GDK_BACKEND", "x11", 0);
 +#endif
 +#endif // GTK_TARGET == 3
        crt_init();
  
        streams_init();
  #endif
  
        const char* mapname = NULL;
 +
 +#if GDEF_OS_WINDOWS
 +      StringOutputStream mapname_buffer( 256 );
 +#endif
 +
      char const *error = NULL;
 +
        if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
                g_print( "%s\n", error );
                return -1;
        }
  
        // Gtk already removed parsed `--options`
 -      if (argc == 2) {
 -              if ( strlen( argv[1] ) > 1 ) {
 -                      if ( g_str_has_suffix( argv[1], ".map" ) ) {
 -                              if ( g_path_is_absolute( argv[1] ) ) {
 -                                      mapname = argv[1];
 -                              }
 -                              else {
 -                                      mapname = g_build_filename( g_get_current_dir(), argv[1], NULL );
 +      if ( argc == 2 ) {
 +              if ( strlen( argv[ 1 ] ) > 1 ) {
 +                      mapname = argv[ 1 ];
 +
 +                      if ( g_str_has_suffix( mapname, ".map" ) ) {
 +                              if ( !g_path_is_absolute( mapname ) ) {
 +                                      mapname = g_build_filename( g_get_current_dir(), mapname, NULL );
                                }
 +
 +#if GDEF_OS_WINDOWS
 +                              mapname_buffer << PathCleaned( mapname );
 +                              mapname = mapname_buffer.c_str();
 +#endif
                        }
                        else {
 -                              g_print( "bad file name, will not load: %s\n", argv[1] );
 +                              g_print( "bad file name, will not load: %s\n", mapname );
 +                              mapname = NULL;
                        }
                }
        }
 -      else if (argc > 2) {
 +      else if ( argc > 2 ) {
                g_print ( "%s\n", "too many arguments" );
                return -1;
        }
  
        paths_init();
  
 -      if ( !check_version() ) {
 -              return EXIT_FAILURE;
 -      }
 -
        show_splash();
  
        create_global_pid();
        // we may have the console turned on and want to keep it that way
        // so we use a latching system
        if ( g_GamesDialog.m_bForceLogConsole ) {
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
                g_Console_enableLogging = true;
                g_GamesDialog.m_bForceLogConsole = false;
        }
        hide_splash();
  
  #ifdef WIN32
-       if( openCmdMap[0] != '\0' ){
+       if( openCmdMap && *openCmdMap ){
                Map_LoadFile( openCmdMap );
        }
        else
  
        remove_local_pid();
  
 -      ui::main();
 +      /* 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. */
 +      if ( !g_dontStart )
 +      {
 +              ui::main();
 +      }
  
        // avoid saving prefs when the app is minimized
        if ( g_pParentWnd->IsSleeping() ) {
        Radiant_Shutdown();
  
        // close the log file if any
 -      Sys_LogFile( false );
 +      Sys_EnableLogFile( false );
  
        return EXIT_SUCCESS;
  }
diff --combined radiant/mainframe.cpp
index f87f4725bbd5ad5ad9cd462b9661d57d8373e0fe,196a60df37464afe89faf0d1cb1f3246cb99a7c3..be1e985049d7ee19577a8613ef5c7527b7fe73d6
  #include "feedback.h"
  #include "referencecache.h"
  #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
  
  struct layout_globals_t
  {
@@@ -167,8 -156,6 +168,8 @@@ void VFS_Refresh()
        RefreshReferences();
        // also refresh texture browser
        TextureBrowser_RefreshShaders();
 +      // also show textures (all or common)
 +      TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
  }
  
  void VFS_Restart(){
@@@ -227,7 -214,9 +228,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
                }
  
@@@ -452,32 -435,14 +453,32 @@@ void setPakPath( int num, const char* p
  }
  
  
 -// App Path
 +// executable file path (full path)
 +CopiedString g_strAppFilePath;
  
 -CopiedString g_strAppPath;                 ///< holds the full path of the executable
 +// directory paths
 +CopiedString g_strAppPath; 
 +CopiedString g_strLibPath;
 +CopiedString g_strDataPath;
 +
 +const char* AppFilePath_get(){
 +      return g_strAppFilePath.c_str();
 +}
  
  const char* AppPath_get(){
        return g_strAppPath.c_str();
  }
  
 +const char *LibPath_get()
 +{
 +    return g_strLibPath.c_str();
 +}
 +
 +const char *DataPath_get()
 +{
 +    return g_strDataPath.c_str();
 +}
 +
  /// the path to the local rc-dir
  const char* LocalRcPath_get( void ){
        static CopiedString rc_path;
@@@ -576,28 -541,39 +577,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 ){
@@@ -614,14 -590,14 +615,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 ));
@@@ -780,7 -756,7 +781,7 @@@ void Radiant_detachGameToolsPathObserve
  void Radiant_Initialise(){
        GlobalModuleServer_Initialise();
  
 -      Radiant_loadModulesFromRoot( AppPath_get() );
 +      Radiant_loadModulesFromRoot( LibPath_get() );
  
        Preferences_Load();
  
@@@ -809,7 -785,7 +810,7 @@@ void Radiant_Shutdown()
  }
  
  void Exit(){
 -      if ( ConfirmModified( "Exit Radiant" ) ) {
 +      if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
  }
@@@ -970,53 -946,6 +971,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 -1063,6 +1135,7 @@@ ui::MenuItem create_colours_menu()
        create_menu_item_with_mnemonic( menu_3, "Q3Radiant Original", "ColorSchemeQER" );
        create_menu_item_with_mnemonic( menu_3, "Black and Green", "ColorSchemeBlackAndGreen" );
        create_menu_item_with_mnemonic( menu_3, "Maya/Max/Lightwave Emulation", "ColorSchemeYdnar" );
 +      create_menu_item_with_mnemonic(menu_3, "Adwaita Dark", "ColorSchemeAdwaitaDark");
  
        menu_separator( menu_in_menu );
  
@@@ -1808,11 -1736,9 +1809,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();
@@@ -1924,18 -1850,15 +1925,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 );
@@@ -2162,9 -2085,6 +2163,9 @@@ ui::MenuItem create_view_menu( MainFram
                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" );
                menu_separator( camera_menu );
@@@ -2414,7 -2334,7 +2415,7 @@@ ui::MenuItem create_help_menu()
  
        create_menu_item_with_mnemonic( menu, "Bug report", makeCallbackF(OpenBugReportURL) );
        create_menu_item_with_mnemonic( menu, "Shortcuts list", makeCallbackF(DoCommandListDlg) );
 -      create_menu_item_with_mnemonic( menu, "_About", makeCallbackF(DoAbout) );
 +      create_menu_item_with_mnemonic( menu, "_About...", makeCallbackF(DoAbout) );
  
        return help_menu_item;
  }
@@@ -2795,14 -2715,10 +2796,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 ){
@@@ -2959,16 -2875,13 +2960,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 );
  
                        }
                }
                if ( g_Layout_enableFilterToolbar.m_value ){
-                       auto filter_toolbar = create_filter_toolbar();
-                       filter_toolbar.show();
-                       auto space = [&]() {
-                               auto btn = gtk_separator_tool_item_new();
-                                       gtk_widget_show(GTK_WIDGET(btn));
-                                       gtk_container_add(GTK_CONTAINER(filter_toolbar), GTK_WIDGET(btn));
-                       };
-                       toolbar_append_toggle_button( filter_toolbar, "World (ALT + 1)", "f-world.bmp", "FilterWorldBrushes" );
-                       toolbar_append_toggle_button( filter_toolbar, "Details (CTRL + D)", "f-details.bmp", "FilterDetails" );
-                       toolbar_append_toggle_button( filter_toolbar, "Structural (CTRL + SHIFT + D)", "f-structural.bmp", "FilterStructural" );
-                       toolbar_append_toggle_button( filter_toolbar, "Patches (CTRL + P)", "patch_wireframe.png", "FilterPatches" );
-                       space();
-                       toolbar_append_toggle_button( filter_toolbar, "Areaportals (ALT + 3)", "f-areaportal.bmp", "FilterAreaportals" );
-                       toolbar_append_toggle_button( filter_toolbar, "Translucent (ALT + 4)", "f-translucent.bmp", "FilterTranslucent" );
-                       toolbar_append_toggle_button( filter_toolbar, "Liquids (ALT + 5)", "f-liquids.bmp", "FilterLiquids" );
-                       toolbar_append_toggle_button( filter_toolbar, "Caulk (ALT + 6)", "f-caulk.bmp", "FilterCaulk" );
-                       toolbar_append_toggle_button( filter_toolbar, "Clips (ALT + 7)", "f-clip.bmp", "FilterClips" );
-                       toolbar_append_toggle_button( filter_toolbar, "HintsSkips (CTRL + H)", "f-hint.bmp", "FilterHintsSkips" );
-                       //toolbar_append_toggle_button( filter_toolbar, "Paths (ALT + 8)", "texture_lock.bmp", "FilterPaths" );
-                       space();
-                       toolbar_append_toggle_button( filter_toolbar, "Entities (ALT + 2)", "f-entities.bmp", "FilterEntities" );
-                       toolbar_append_toggle_button( filter_toolbar, "Lights (ALT + 0)", "lightinspector.png", "FilterLights" );
-                       toolbar_append_toggle_button( filter_toolbar, "Models (SHIFT + M)", "f-models.bmp", "FilterModels" );
-                       toolbar_append_toggle_button( filter_toolbar, "Triggers (CTRL + SHIFT + T)", "f-triggers.bmp", "FilterTriggers" );
-                       //toolbar_append_toggle_button( filter_toolbar, "Decals (SHIFT + D)", "f-decals.bmp", "FilterDecals" );
-                       space();
-                       toolbar_append_button( filter_toolbar, "InvertFilters", "f-invert.bmp", "InvertFilters" );
-                       toolbar_append_button( filter_toolbar, "ResetFilters", "f-reset.bmp", "ResetFilters" );
-                       PFbox.pack_start( filter_toolbar, FALSE, FALSE, 0 );
+                       ui::Toolbar filter_toolbar = create_filter_toolbar();
+                       PFbox.pack_start( filter_toolbar, TRUE, TRUE, 0 );
                }
        }
  
  
        window.show();
  
 -      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft ) {
 +      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft )
 +      {
                {
                        ui::Widget hsplit = ui::HPaned(ui::New);
                        m_vSplit = hsplit;
  
                gtk_paned_set_position( GTK_PANED( m_vSplit2 ), g_layout_globals.nCamHeight );
        }
 -      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 );
                        }
                        CamWnd_setParent( *m_pCamWnd, window );
  
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, CamWnd_getWidget( *m_pCamWnd ) );
 +
                        g_floating_windows.push_back( window );
                }
  
                        }
                        XY_Top_Shown_Construct( window );
  
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXYWnd->GetWidget() );
 +
                        g_floating_windows.push_back( window );
                }
  
  
                        XZ_Front_Shown_Construct( window );
  
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXZWnd->GetWidget() );
 +
                        g_floating_windows.push_back( window );
                }
  
  
                        YZ_Side_Shown_Construct( window );
  
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pYZWnd->GetWidget() );
 +
                        g_floating_windows.push_back( window );
                }
  
                {
                        auto frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( GroupDialog_getWindow(), TextureBrowser_getGLWidget() );
                }
  
                GroupDialog_show();
        }
 -      else // 4 way
 +      else if ( CurrentStyle() == eSplit )
        {
                m_pCamWnd = NewCamWnd();
                GlobalCamera_setCamWnd( *m_pCamWnd );
                {
              auto frame = create_framed_widget( TextureBrowser_constructWindow( window ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
 +
 +                      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();
 +
 +              ui::Widget split = create_split_views( camera, yz, xy, xz );
 +
 +              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 ) );
 +
 +              gtk_paned_add1( GTK_PANED( hsplit ), split );
 +              gtk_paned_add2( GTK_PANED( hsplit ), vsplit );
 +
 +              gtk_paned_add1( GTK_PANED( vsplit ), texture_window  );
 +              gtk_paned_add2( GTK_PANED( vsplit ), console_window  );
 +
 +              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 );
@@@ -3423,7 -3242,7 +3394,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 );
        }
  }
@@@ -3455,7 -3274,7 +3426,7 @@@ void MainFrame::SetGridStatus()
  }
  
  void GridStatus_onTextureLockEnabledChanged(){
 -      if ( g_pParentWnd != 0 ) {
 +      if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetGridStatus();
        }
  }
@@@ -3500,7 -3319,7 +3471,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 ),
@@@ -3536,9 -3355,9 +3507,9 @@@ void Layout_registerPreferencesPage()
        PreferencesDialog_addInterfacePage( makeCallbackF(Layout_constructPage) );
  }
  
 -
  #include "preferencesystem.h"
  #include "stringio.h"
 +#include "transformpath/transformpath.h"
  
  void MainFrame_Construct(){
        GlobalCommands_insert( "OpenManual", makeCallbackF(OpenHelpURL), Accelerator( GDK_KEY_F1 ) );
        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 );
  }
@@@ -3752,61 -3569,3 +3723,61 @@@ void GLWindow_Construct()
  
  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;
 +      }
 +}
index d710945d99162c249e0765cd4c52af05a1c59583,aea1b321b837528586c445ff417f1724b8afde58..e06491d8f31f259690ec5f91025a4dc749e91880
@@@ -42,13 -42,13 +42,13 @@@ ui::Image new_plugin_image( const char
  
        {
                StringOutputStream fullpath( 256 );
 -              fullpath << AppPath_get() << g_pluginsDir << "bitmaps/" << filename;
 +              fullpath << DataPath_get() << g_pluginsDir << "bitmaps/" << filename;
                if ( auto image = image_new_from_file_with_mask(fullpath.c_str()) ) return image;
        }
  
        {
                StringOutputStream fullpath( 256 );
 -              fullpath << AppPath_get() << g_modulesDir << "bitmaps/" << filename;
 +              fullpath << DataPath_get() << g_modulesDir << "bitmaps/" << filename;
                if ( auto image = image_new_from_file_with_mask(fullpath.c_str()) ) return image;
        }
  
@@@ -131,17 -131,3 +131,3 @@@ ui::Toolbar create_plugin_toolbar()
  
        return toolbar;
  }
- ui::Toolbar create_filter_toolbar(){
-       auto toolbar = ui::Toolbar::from( gtk_toolbar_new() );
-       gtk_orientable_set_orientation( GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL );
-       gtk_toolbar_set_style( toolbar, GTK_TOOLBAR_ICONS );
-       toolbar.show();
-       g_plugin_toolbar = toolbar;
-       PluginToolbar_populate();
-       return toolbar;
- }