]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/mainframe.cpp
s/nullptr/ui::null/ where appropriate
[xonotic/netradiant.git] / radiant / mainframe.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 //
23 // Main Window for Q3Radiant
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "mainframe.h"
29
30 #include <gtk/gtk.h>
31
32 #include "ifilesystem.h"
33 #include "iundo.h"
34 #include "editable.h"
35 #include "ientity.h"
36 #include "ishaders.h"
37 #include "igl.h"
38 #include "moduleobserver.h"
39
40 #include <ctime>
41
42 #include <gdk/gdkkeysyms.h>
43
44
45 #include "cmdlib.h"
46 #include "stream/stringstream.h"
47 #include "signal/isignal.h"
48 #include "os/path.h"
49 #include "os/file.h"
50 #include "eclasslib.h"
51 #include "moduleobservers.h"
52
53 #include "gtkutil/clipboard.h"
54 #include "gtkutil/frame.h"
55 #include "gtkutil/glwidget.h"
56 #include "gtkutil/image.h"
57 #include "gtkutil/menu.h"
58 #include "gtkutil/paned.h"
59
60 #include "autosave.h"
61 #include "build.h"
62 #include "brushmanip.h"
63 #include "brushmodule.h"
64 #include "camwindow.h"
65 #include "csg.h"
66 #include "commands.h"
67 #include "console.h"
68 #include "entity.h"
69 #include "entityinspector.h"
70 #include "entitylist.h"
71 #include "filters.h"
72 #include "findtexturedialog.h"
73 #include "grid.h"
74 #include "groupdialog.h"
75 #include "gtkdlgs.h"
76 #include "gtkmisc.h"
77 #include "help.h"
78 #include "map.h"
79 #include "mru.h"
80 #include "multimon.h"
81 #include "patchdialog.h"
82 #include "patchmanip.h"
83 #include "plugin.h"
84 #include "pluginmanager.h"
85 #include "pluginmenu.h"
86 #include "plugintoolbar.h"
87 #include "preferences.h"
88 #include "qe3.h"
89 #include "qgl.h"
90 #include "select.h"
91 #include "server.h"
92 #include "surfacedialog.h"
93 #include "textures.h"
94 #include "texwindow.h"
95 #include "url.h"
96 #include "xywindow.h"
97 #include "windowobservers.h"
98 #include "renderstate.h"
99 #include "feedback.h"
100 #include "referencecache.h"
101 #include "texwindow.h"
102
103
104 struct layout_globals_t
105 {
106         WindowPosition m_position;
107
108
109         int nXYHeight;
110         int nXYWidth;
111         int nCamWidth;
112         int nCamHeight;
113         int nState;
114
115         layout_globals_t() :
116                 m_position( -1, -1, 640, 480 ),
117
118                 nXYHeight( 300 ),
119                 nXYWidth( 300 ),
120                 nCamWidth( 200 ),
121                 nCamHeight( 200 ),
122                 nState( GDK_WINDOW_STATE_MAXIMIZED ){
123         }
124 };
125
126 layout_globals_t g_layout_globals;
127 glwindow_globals_t g_glwindow_globals;
128
129
130 // VFS
131
132 bool g_vfsInitialized = false;
133
134 void VFS_Init(){
135         if ( g_vfsInitialized ) return;
136         QE_InitVFS();
137         GlobalFileSystem().initialise();
138         g_vfsInitialized = true;
139 }
140 void VFS_Shutdown(){
141         if ( !g_vfsInitialized ) return;
142         GlobalFileSystem().shutdown();
143         g_vfsInitialized = false;
144 }
145 void VFS_Refresh(){
146         if ( !g_vfsInitialized ) return;
147         GlobalFileSystem().clear();
148         QE_InitVFS();
149         GlobalFileSystem().refresh();
150         g_vfsInitialized = true;
151         // also refresh texture browser
152         TextureBrowser_RefreshShaders();
153 }
154 void VFS_Restart(){
155         VFS_Shutdown();
156         VFS_Init();
157 }
158
159 class VFSModuleObserver : public ModuleObserver
160 {
161 public:
162 void realise(){
163         VFS_Init();
164 }
165 void unrealise(){
166         VFS_Shutdown();
167 }
168 };
169
170 VFSModuleObserver g_VFSModuleObserver;
171
172 void VFS_Construct(){
173         Radiant_attachHomePathsObserver( g_VFSModuleObserver );
174 }
175 void VFS_Destroy(){
176         Radiant_detachHomePathsObserver( g_VFSModuleObserver );
177 }
178
179 // Home Paths
180
181 #ifdef WIN32
182 #include <shlobj.h>
183 #include <objbase.h>
184 const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
185 #define qREFKNOWNFOLDERID GUID
186 #define qKF_FLAG_CREATE 0x8000
187 #define qKF_FLAG_NO_ALIAS 0x1000
188 typedef HRESULT ( WINAPI qSHGetKnownFolderPath_t )( qREFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath );
189 static qSHGetKnownFolderPath_t *qSHGetKnownFolderPath;
190 #endif
191 void HomePaths_Realise(){
192         do
193         {
194                 const char* prefix = g_pGameDescription->getKeyValue( "prefix" );
195                 if ( !string_empty( prefix ) ) {
196                         StringOutputStream path( 256 );
197
198 #if defined( __APPLE__ )
199                         path.clear();
200                         path << DirectoryCleaned( g_get_home_dir() ) << "Library/Application Support" << ( prefix + 1 ) << "/";
201                         if ( file_is_directory( path.c_str() ) ) {
202                                 g_qeglobals.m_userEnginePath = path.c_str();
203                                 break;
204                         }
205                         path.clear();
206                         path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
207 #endif
208
209 #if defined( WIN32 )
210                         TCHAR mydocsdir[MAX_PATH + 1];
211                         wchar_t *mydocsdirw;
212                         HMODULE shfolder = LoadLibrary( "shfolder.dll" );
213                         if ( shfolder ) {
214                                 qSHGetKnownFolderPath = (qSHGetKnownFolderPath_t *) GetProcAddress( shfolder, "SHGetKnownFolderPath" );
215                         }
216                         else{
217                                 qSHGetKnownFolderPath = NULL;
218                         }
219                         CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
220                         if ( qSHGetKnownFolderPath && qSHGetKnownFolderPath( qFOLDERID_SavedGames, qKF_FLAG_CREATE | qKF_FLAG_NO_ALIAS, NULL, &mydocsdirw ) == S_OK ) {
221                                 memset( mydocsdir, 0, sizeof( mydocsdir ) );
222                                 wcstombs( mydocsdir, mydocsdirw, sizeof( mydocsdir ) - 1 );
223                                 CoTaskMemFree( mydocsdirw );
224                                 path.clear();
225                                 path << DirectoryCleaned( mydocsdir ) << ( prefix + 1 ) << "/";
226                                 if ( file_is_directory( path.c_str() ) ) {
227                                         g_qeglobals.m_userEnginePath = path.c_str();
228                                         CoUninitialize();
229                                         FreeLibrary( shfolder );
230                                         break;
231                                 }
232                         }
233                         CoUninitialize();
234                         if ( shfolder ) {
235                                 FreeLibrary( shfolder );
236                         }
237                         if ( SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir ) ) {
238                                 path.clear();
239                                 path << DirectoryCleaned( mydocsdir ) << "My Games/" << ( prefix + 1 ) << "/";
240                                 // win32: only add it if it already exists
241                                 if ( file_is_directory( path.c_str() ) ) {
242                                         g_qeglobals.m_userEnginePath = path.c_str();
243                                         break;
244                                 }
245                         }
246 #endif
247
248 #if defined( POSIX )
249                         path.clear();
250                         path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
251                         g_qeglobals.m_userEnginePath = path.c_str();
252                         break;
253 #endif
254                 }
255
256                 g_qeglobals.m_userEnginePath = EnginePath_get();
257         }
258         while ( 0 );
259
260         Q_mkdir( g_qeglobals.m_userEnginePath.c_str() );
261
262         {
263                 StringOutputStream path( 256 );
264                 path << g_qeglobals.m_userEnginePath.c_str() << gamename_get() << '/';
265                 g_qeglobals.m_userGamePath = path.c_str();
266         }
267         ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "HomePaths_Realise: user-game-path is empty" );
268         Q_mkdir( g_qeglobals.m_userGamePath.c_str() );
269 }
270
271 ModuleObservers g_homePathObservers;
272
273 void Radiant_attachHomePathsObserver( ModuleObserver& observer ){
274         g_homePathObservers.attach( observer );
275 }
276
277 void Radiant_detachHomePathsObserver( ModuleObserver& observer ){
278         g_homePathObservers.detach( observer );
279 }
280
281 class HomePathsModuleObserver : public ModuleObserver
282 {
283 std::size_t m_unrealised;
284 public:
285 HomePathsModuleObserver() : m_unrealised( 1 ){
286 }
287 void realise(){
288         if ( --m_unrealised == 0 ) {
289                 HomePaths_Realise();
290                 g_homePathObservers.realise();
291         }
292 }
293 void unrealise(){
294         if ( ++m_unrealised == 1 ) {
295                 g_homePathObservers.unrealise();
296         }
297 }
298 };
299
300 HomePathsModuleObserver g_HomePathsModuleObserver;
301
302 void HomePaths_Construct(){
303         Radiant_attachEnginePathObserver( g_HomePathsModuleObserver );
304 }
305 void HomePaths_Destroy(){
306         Radiant_detachEnginePathObserver( g_HomePathsModuleObserver );
307 }
308
309
310 // Engine Path
311
312 CopiedString g_strEnginePath;
313 ModuleObservers g_enginePathObservers;
314 std::size_t g_enginepath_unrealised = 1;
315
316 void Radiant_attachEnginePathObserver( ModuleObserver& observer ){
317         g_enginePathObservers.attach( observer );
318 }
319
320 void Radiant_detachEnginePathObserver( ModuleObserver& observer ){
321         g_enginePathObservers.detach( observer );
322 }
323
324
325 void EnginePath_Realise(){
326         if ( --g_enginepath_unrealised == 0 ) {
327                 g_enginePathObservers.realise();
328         }
329 }
330
331
332 const char* EnginePath_get(){
333         ASSERT_MESSAGE( g_enginepath_unrealised == 0, "EnginePath_get: engine path not realised" );
334         return g_strEnginePath.c_str();
335 }
336
337 void EnginePath_Unrealise(){
338         if ( ++g_enginepath_unrealised == 1 ) {
339                 g_enginePathObservers.unrealise();
340         }
341 }
342
343 void setEnginePath( const char* path ){
344         StringOutputStream buffer( 256 );
345         buffer << DirectoryCleaned( path );
346         if ( !path_equal( buffer.c_str(), g_strEnginePath.c_str() ) ) {
347 #if 0
348                 while ( !ConfirmModified( "Paths Changed" ) )
349                 {
350                         if ( Map_Unnamed( g_map ) ) {
351                                 Map_SaveAs();
352                         }
353                         else
354                         {
355                                 Map_Save();
356                         }
357                 }
358                 Map_RegionOff();
359 #endif
360
361                 ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Changing Engine Path" );
362
363                 EnginePath_Unrealise();
364
365                 g_strEnginePath = buffer.c_str();
366
367                 EnginePath_Realise();
368         }
369 }
370
371
372 // App Path
373
374 CopiedString g_strAppPath;                 ///< holds the full path of the executable
375
376 const char* AppPath_get(){
377         return g_strAppPath.c_str();
378 }
379
380 /// the path to the local rc-dir
381 const char* LocalRcPath_get( void ){
382         static CopiedString rc_path;
383         if ( rc_path.empty() ) {
384                 StringOutputStream stream( 256 );
385                 stream << GlobalRadiant().getSettingsPath() << g_pGameDescription->mGameFile.c_str() << "/";
386                 rc_path = stream.c_str();
387         }
388         return rc_path.c_str();
389 }
390
391 /// directory for temp files
392 /// NOTE: on *nix this is were we check for .pid
393 CopiedString g_strSettingsPath;
394 const char* SettingsPath_get(){
395         return g_strSettingsPath.c_str();
396 }
397
398
399 /*!
400    points to the game tools directory, for instance
401    C:/Program Files/Quake III Arena/GtkRadiant
402    (or other games)
403    this is one of the main variables that are configured by the game selection on startup
404    [GameToolsPath]/plugins
405    [GameToolsPath]/modules
406    and also q3map, bspc
407  */
408 CopiedString g_strGameToolsPath;           ///< this is set by g_GamesDialog
409
410 const char* GameToolsPath_get(){
411         return g_strGameToolsPath.c_str();
412 }
413
414 void EnginePathImport( CopiedString& self, const char* value ){
415         setEnginePath( value );
416 }
417 typedef ReferenceCaller1<CopiedString, const char*, EnginePathImport> EnginePathImportCaller;
418
419 void Paths_constructPreferences( PreferencesPage& page ){
420         page.appendPathEntry( "Engine Path", true,
421                                                   StringImportCallback( EnginePathImportCaller( g_strEnginePath ) ),
422                                                   StringExportCallback( StringExportCaller( g_strEnginePath ) )
423                                                   );
424 }
425 void Paths_constructPage( PreferenceGroup& group ){
426         PreferencesPage page( group.createPage( "Paths", "Path Settings" ) );
427         Paths_constructPreferences( page );
428 }
429 void Paths_registerPreferencesPage(){
430         PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, Paths_constructPage>() );
431 }
432
433
434 class PathsDialog : public Dialog
435 {
436 public:
437 ui::Window BuildDialog(){
438         auto frame = create_dialog_frame( "Path settings", ui::Shadow::ETCHED_IN );
439
440         auto vbox2 = create_dialog_vbox( 0, 4 );
441         frame.add(vbox2);
442
443         {
444                 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
445                 Paths_constructPreferences( preferencesPage );
446         }
447
448         return ui::Window(create_simple_modal_dialog_window( "Engine Path Not Found", m_modal, frame ));
449 }
450 };
451
452 PathsDialog g_PathsDialog;
453
454 void EnginePath_verify(){
455         if ( !file_exists( g_strEnginePath.c_str() ) ) {
456                 g_PathsDialog.Create();
457                 g_PathsDialog.DoModal();
458                 g_PathsDialog.Destroy();
459         }
460 }
461
462 namespace
463 {
464 CopiedString g_gamename;
465 CopiedString g_gamemode;
466 ModuleObservers g_gameNameObservers;
467 ModuleObservers g_gameModeObservers;
468 }
469
470 void Radiant_attachGameNameObserver( ModuleObserver& observer ){
471         g_gameNameObservers.attach( observer );
472 }
473
474 void Radiant_detachGameNameObserver( ModuleObserver& observer ){
475         g_gameNameObservers.detach( observer );
476 }
477
478 const char* basegame_get(){
479         return g_pGameDescription->getRequiredKeyValue( "basegame" );
480 }
481
482 const char* gamename_get(){
483         const char* gamename = g_gamename.c_str();
484         if ( string_empty( gamename ) ) {
485                 return basegame_get();
486         }
487         return gamename;
488 }
489
490 void gamename_set( const char* gamename ){
491         if ( !string_equal( gamename, g_gamename.c_str() ) ) {
492                 g_gameNameObservers.unrealise();
493                 g_gamename = gamename;
494                 g_gameNameObservers.realise();
495         }
496 }
497
498 void Radiant_attachGameModeObserver( ModuleObserver& observer ){
499         g_gameModeObservers.attach( observer );
500 }
501
502 void Radiant_detachGameModeObserver( ModuleObserver& observer ){
503         g_gameModeObservers.detach( observer );
504 }
505
506 const char* gamemode_get(){
507         return g_gamemode.c_str();
508 }
509
510 void gamemode_set( const char* gamemode ){
511         if ( !string_equal( gamemode, g_gamemode.c_str() ) ) {
512                 g_gameModeObservers.unrealise();
513                 g_gamemode = gamemode;
514                 g_gameModeObservers.realise();
515         }
516 }
517
518
519 #include "os/dir.h"
520
521 class CLoadModule
522 {
523 const char* m_path;
524 public:
525 CLoadModule( const char* path ) : m_path( path ){
526 }
527 void operator()( const char* name ) const {
528         char fullname[1024];
529         ASSERT_MESSAGE( strlen( m_path ) + strlen( name ) < 1024, "" );
530         strcpy( fullname, m_path );
531         strcat( fullname, name );
532         globalOutputStream() << "Found '" << fullname << "'\n";
533         GlobalModuleServer_loadModule( fullname );
534 }
535 };
536
537 const char* const c_library_extension =
538 #if defined( CMAKE_SHARED_MODULE_SUFFIX )
539     CMAKE_SHARED_MODULE_SUFFIX
540 #elif defined( WIN32 )
541         "dll"
542 #elif defined ( __APPLE__ )
543         "dylib"
544 #elif defined( __linux__ ) || defined ( __FreeBSD__ )
545         "so"
546 #endif
547 ;
548
549 void Radiant_loadModules( const char* path ){
550         Directory_forEach( path, MatchFileExtension<CLoadModule>( c_library_extension, CLoadModule( path ) ) );
551 }
552
553 void Radiant_loadModulesFromRoot( const char* directory ){
554         {
555                 StringOutputStream path( 256 );
556                 path << directory << g_pluginsDir;
557                 Radiant_loadModules( path.c_str() );
558         }
559
560         if ( !string_equal( g_pluginsDir, g_modulesDir ) ) {
561                 StringOutputStream path( 256 );
562                 path << directory << g_modulesDir;
563                 Radiant_loadModules( path.c_str() );
564         }
565 }
566
567 //! Make COLOR_BRUSHES override worldspawn eclass colour.
568 void SetWorldspawnColour( const Vector3& colour ){
569         EntityClass* worldspawn = GlobalEntityClassManager().findOrInsert( "worldspawn", true );
570         eclass_release_state( worldspawn );
571         worldspawn->color = colour;
572         eclass_capture_state( worldspawn );
573 }
574
575
576 class WorldspawnColourEntityClassObserver : public ModuleObserver
577 {
578 std::size_t m_unrealised;
579 public:
580 WorldspawnColourEntityClassObserver() : m_unrealised( 1 ){
581 }
582 void realise(){
583         if ( --m_unrealised == 0 ) {
584                 SetWorldspawnColour( g_xywindow_globals.color_brushes );
585         }
586 }
587 void unrealise(){
588         if ( ++m_unrealised == 1 ) {
589         }
590 }
591 };
592
593 WorldspawnColourEntityClassObserver g_WorldspawnColourEntityClassObserver;
594
595
596 ModuleObservers g_gameToolsPathObservers;
597
598 void Radiant_attachGameToolsPathObserver( ModuleObserver& observer ){
599         g_gameToolsPathObservers.attach( observer );
600 }
601
602 void Radiant_detachGameToolsPathObserver( ModuleObserver& observer ){
603         g_gameToolsPathObservers.detach( observer );
604 }
605
606 void Radiant_Initialise(){
607         GlobalModuleServer_Initialise();
608
609         Radiant_loadModulesFromRoot( AppPath_get() );
610
611         Preferences_Load();
612
613         bool success = Radiant_Construct( GlobalModuleServer_get() );
614         ASSERT_MESSAGE( success, "module system failed to initialise - see radiant.log for error messages" );
615
616         g_gameToolsPathObservers.realise();
617         g_gameModeObservers.realise();
618         g_gameNameObservers.realise();
619 }
620
621 void Radiant_Shutdown(){
622         g_gameNameObservers.unrealise();
623         g_gameModeObservers.unrealise();
624         g_gameToolsPathObservers.unrealise();
625
626         if ( !g_preferences_globals.disable_ini ) {
627                 globalOutputStream() << "Start writing prefs\n";
628                 Preferences_Save();
629                 globalOutputStream() << "Done prefs\n";
630         }
631
632         Radiant_Destroy();
633
634         GlobalModuleServer_Shutdown();
635 }
636
637 void Exit(){
638         if ( ConfirmModified( "Exit Radiant" ) ) {
639                 gtk_main_quit();
640         }
641 }
642
643
644 void Undo(){
645         GlobalUndoSystem().undo();
646         SceneChangeNotify();
647 }
648
649 void Redo(){
650         GlobalUndoSystem().redo();
651         SceneChangeNotify();
652 }
653
654 void deleteSelection(){
655         UndoableCommand undo( "deleteSelected" );
656         Select_Delete();
657 }
658
659 void Map_ExportSelected( TextOutputStream& ostream ){
660         Map_ExportSelected( ostream, Map_getFormat( g_map ) );
661 }
662
663 void Map_ImportSelected( TextInputStream& istream ){
664         Map_ImportSelected( istream, Map_getFormat( g_map ) );
665 }
666
667 void Selection_Copy(){
668         clipboard_copy( Map_ExportSelected );
669 }
670
671 void Selection_Paste(){
672         clipboard_paste( Map_ImportSelected );
673 }
674
675 void Copy(){
676         if ( SelectedFaces_empty() ) {
677                 Selection_Copy();
678         }
679         else
680         {
681                 SelectedFaces_copyTexture();
682         }
683 }
684
685 void Paste(){
686         if ( SelectedFaces_empty() ) {
687                 UndoableCommand undo( "paste" );
688
689                 GlobalSelectionSystem().setSelectedAll( false );
690                 Selection_Paste();
691         }
692         else
693         {
694                 SelectedFaces_pasteTexture();
695         }
696 }
697
698 void PasteToCamera(){
699         CamWnd& camwnd = *g_pParentWnd->GetCamWnd();
700         GlobalSelectionSystem().setSelectedAll( false );
701
702         UndoableCommand undo( "pasteToCamera" );
703
704         Selection_Paste();
705
706         // Work out the delta
707         Vector3 mid;
708         Select_GetMid( mid );
709         Vector3 delta = vector3_subtracted( vector3_snapped( Camera_getOrigin( camwnd ), GetSnapGridSize() ), mid );
710
711         // Move to camera
712         GlobalSelectionSystem().translateSelected( delta );
713 }
714
715
716 void ColorScheme_Original(){
717         TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), Vector3( 0.25f, 0.25f, 0.25f ) );
718
719         g_camwindow_globals.color_selbrushes3d = Vector3( 1.0f, 0.0f, 0.0f );
720         g_camwindow_globals.color_cameraback = Vector3( 0.25f, 0.25f, 0.25f );
721         CamWnd_Update( *g_pParentWnd->GetCamWnd() );
722
723         g_xywindow_globals.color_gridback = Vector3( 1.0f, 1.0f, 1.0f );
724         g_xywindow_globals.color_gridminor = Vector3( 0.75f, 0.75f, 0.75f );
725         g_xywindow_globals.color_gridmajor = Vector3( 0.5f, 0.5f, 0.5f );
726         g_xywindow_globals.color_gridminor_alt = Vector3( 0.5f, 0.0f, 0.0f );
727         g_xywindow_globals.color_gridmajor_alt = Vector3( 1.0f, 0.0f, 0.0f );
728         g_xywindow_globals.color_gridblock = Vector3( 0.0f, 0.0f, 1.0f );
729         g_xywindow_globals.color_gridtext = Vector3( 0.0f, 0.0f, 0.0f );
730         g_xywindow_globals.color_selbrushes = Vector3( 1.0f, 0.0f, 0.0f );
731         g_xywindow_globals.color_clipper = Vector3( 0.0f, 0.0f, 1.0f );
732         g_xywindow_globals.color_brushes = Vector3( 0.0f, 0.0f, 0.0f );
733         SetWorldspawnColour( g_xywindow_globals.color_brushes );
734         g_xywindow_globals.color_viewname = Vector3( 0.5f, 0.0f, 0.75f );
735         XY_UpdateAllWindows();
736 }
737
738 void ColorScheme_QER(){
739         TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), Vector3( 0.25f, 0.25f, 0.25f ) );
740
741         g_camwindow_globals.color_cameraback = Vector3( 0.25f, 0.25f, 0.25f );
742         g_camwindow_globals.color_selbrushes3d = Vector3( 1.0f, 0.0f, 0.0f );
743         CamWnd_Update( *g_pParentWnd->GetCamWnd() );
744
745         g_xywindow_globals.color_gridback = Vector3( 1.0f, 1.0f, 1.0f );
746         g_xywindow_globals.color_gridminor = Vector3( 1.0f, 1.0f, 1.0f );
747         g_xywindow_globals.color_gridmajor = Vector3( 0.5f, 0.5f, 0.5f );
748         g_xywindow_globals.color_gridblock = Vector3( 0.0f, 0.0f, 1.0f );
749         g_xywindow_globals.color_gridtext = Vector3( 0.0f, 0.0f, 0.0f );
750         g_xywindow_globals.color_selbrushes = Vector3( 1.0f, 0.0f, 0.0f );
751         g_xywindow_globals.color_clipper = Vector3( 0.0f, 0.0f, 1.0f );
752         g_xywindow_globals.color_brushes = Vector3( 0.0f, 0.0f, 0.0f );
753         SetWorldspawnColour( g_xywindow_globals.color_brushes );
754         g_xywindow_globals.color_viewname = Vector3( 0.5f, 0.0f, 0.75f );
755         XY_UpdateAllWindows();
756 }
757
758 void ColorScheme_Black(){
759         TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), Vector3( 0.25f, 0.25f, 0.25f ) );
760
761         g_camwindow_globals.color_cameraback = Vector3( 0.25f, 0.25f, 0.25f );
762         g_camwindow_globals.color_selbrushes3d = Vector3( 1.0f, 0.0f, 0.0f );
763         CamWnd_Update( *g_pParentWnd->GetCamWnd() );
764
765         g_xywindow_globals.color_gridback = Vector3( 0.0f, 0.0f, 0.0f );
766         g_xywindow_globals.color_gridminor = Vector3( 0.2f, 0.2f, 0.2f );
767         g_xywindow_globals.color_gridmajor = Vector3( 0.3f, 0.5f, 0.5f );
768         g_xywindow_globals.color_gridblock = Vector3( 0.0f, 0.0f, 1.0f );
769         g_xywindow_globals.color_gridtext = Vector3( 1.0f, 1.0f, 1.0f );
770         g_xywindow_globals.color_selbrushes = Vector3( 1.0f, 0.0f, 0.0f );
771         g_xywindow_globals.color_clipper = Vector3( 0.0f, 0.0f, 1.0f );
772         g_xywindow_globals.color_brushes = Vector3( 1.0f, 1.0f, 1.0f );
773         SetWorldspawnColour( g_xywindow_globals.color_brushes );
774         g_xywindow_globals.color_viewname = Vector3( 0.7f, 0.7f, 0.0f );
775         XY_UpdateAllWindows();
776 }
777
778 /* ydnar: to emulate maya/max/lightwave color schemes */
779 void ColorScheme_Ydnar(){
780         TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), Vector3( 0.25f, 0.25f, 0.25f ) );
781
782         g_camwindow_globals.color_cameraback = Vector3( 0.25f, 0.25f, 0.25f );
783         g_camwindow_globals.color_selbrushes3d = Vector3( 1.0f, 0.0f, 0.0f );
784         CamWnd_Update( *g_pParentWnd->GetCamWnd() );
785
786         g_xywindow_globals.color_gridback = Vector3( 0.77f, 0.77f, 0.77f );
787         g_xywindow_globals.color_gridminor = Vector3( 0.83f, 0.83f, 0.83f );
788         g_xywindow_globals.color_gridmajor = Vector3( 0.89f, 0.89f, 0.89f );
789         g_xywindow_globals.color_gridblock = Vector3( 1.0f, 1.0f, 1.0f );
790         g_xywindow_globals.color_gridtext = Vector3( 0.0f, 0.0f, 0.0f );
791         g_xywindow_globals.color_selbrushes = Vector3( 1.0f, 0.0f, 0.0f );
792         g_xywindow_globals.color_clipper = Vector3( 0.0f, 0.0f, 1.0f );
793         g_xywindow_globals.color_brushes = Vector3( 0.0f, 0.0f, 0.0f );
794         SetWorldspawnColour( g_xywindow_globals.color_brushes );
795         g_xywindow_globals.color_viewname = Vector3( 0.5f, 0.0f, 0.75f );
796         XY_UpdateAllWindows();
797 }
798
799 typedef Callback1<Vector3&> GetColourCallback;
800 typedef Callback1<const Vector3&> SetColourCallback;
801
802 class ChooseColour
803 {
804 GetColourCallback m_get;
805 SetColourCallback m_set;
806 public:
807 ChooseColour( const GetColourCallback& get, const SetColourCallback& set )
808         : m_get( get ), m_set( set ){
809 }
810 void operator()(){
811         Vector3 colour;
812         m_get( colour );
813         color_dialog( MainFrame_getWindow(), colour );
814         m_set( colour );
815 }
816 };
817
818
819
820 void Colour_get( const Vector3& colour, Vector3& other ){
821         other = colour;
822 }
823 typedef ConstReferenceCaller1<Vector3, Vector3&, Colour_get> ColourGetCaller;
824
825 void Colour_set( Vector3& colour, const Vector3& other ){
826         colour = other;
827         SceneChangeNotify();
828 }
829 typedef ReferenceCaller1<Vector3, const Vector3&, Colour_set> ColourSetCaller;
830
831 void BrushColour_set( const Vector3& other ){
832         g_xywindow_globals.color_brushes = other;
833         SetWorldspawnColour( g_xywindow_globals.color_brushes );
834         SceneChangeNotify();
835 }
836 typedef FreeCaller1<const Vector3&, BrushColour_set> BrushColourSetCaller;
837
838 void ClipperColour_set( const Vector3& other ){
839         g_xywindow_globals.color_clipper = other;
840         Brush_clipperColourChanged();
841         SceneChangeNotify();
842 }
843 typedef FreeCaller1<const Vector3&, ClipperColour_set> ClipperColourSetCaller;
844
845 void TextureBrowserColour_get( Vector3& other ){
846         other = TextureBrowser_getBackgroundColour( GlobalTextureBrowser() );
847 }
848 typedef FreeCaller1<Vector3&, TextureBrowserColour_get> TextureBrowserColourGetCaller;
849
850 void TextureBrowserColour_set( const Vector3& other ){
851         TextureBrowser_setBackgroundColour( GlobalTextureBrowser(), other );
852 }
853 typedef FreeCaller1<const Vector3&, TextureBrowserColour_set> TextureBrowserColourSetCaller;
854
855
856 class ColoursMenu
857 {
858 public:
859 ChooseColour m_textureback;
860 ChooseColour m_xyback;
861 ChooseColour m_gridmajor;
862 ChooseColour m_gridminor;
863 ChooseColour m_gridmajor_alt;
864 ChooseColour m_gridminor_alt;
865 ChooseColour m_gridtext;
866 ChooseColour m_gridblock;
867 ChooseColour m_cameraback;
868 ChooseColour m_brush;
869 ChooseColour m_selectedbrush;
870 ChooseColour m_selectedbrush3d;
871 ChooseColour m_clipper;
872 ChooseColour m_viewname;
873
874 ColoursMenu() :
875         m_textureback( TextureBrowserColourGetCaller(), TextureBrowserColourSetCaller() ),
876         m_xyback( ColourGetCaller( g_xywindow_globals.color_gridback ), ColourSetCaller( g_xywindow_globals.color_gridback ) ),
877         m_gridmajor( ColourGetCaller( g_xywindow_globals.color_gridmajor ), ColourSetCaller( g_xywindow_globals.color_gridmajor ) ),
878         m_gridminor( ColourGetCaller( g_xywindow_globals.color_gridminor ), ColourSetCaller( g_xywindow_globals.color_gridminor ) ),
879         m_gridmajor_alt( ColourGetCaller( g_xywindow_globals.color_gridmajor_alt ), ColourSetCaller( g_xywindow_globals.color_gridmajor_alt ) ),
880         m_gridminor_alt( ColourGetCaller( g_xywindow_globals.color_gridminor_alt ), ColourSetCaller( g_xywindow_globals.color_gridminor_alt ) ),
881         m_gridtext( ColourGetCaller( g_xywindow_globals.color_gridtext ), ColourSetCaller( g_xywindow_globals.color_gridtext ) ),
882         m_gridblock( ColourGetCaller( g_xywindow_globals.color_gridblock ), ColourSetCaller( g_xywindow_globals.color_gridblock ) ),
883         m_cameraback( ColourGetCaller( g_camwindow_globals.color_cameraback ), ColourSetCaller( g_camwindow_globals.color_cameraback ) ),
884         m_brush( ColourGetCaller( g_xywindow_globals.color_brushes ), BrushColourSetCaller() ),
885         m_selectedbrush( ColourGetCaller( g_xywindow_globals.color_selbrushes ), ColourSetCaller( g_xywindow_globals.color_selbrushes ) ),
886         m_selectedbrush3d( ColourGetCaller( g_camwindow_globals.color_selbrushes3d ), ColourSetCaller( g_camwindow_globals.color_selbrushes3d ) ),
887         m_clipper( ColourGetCaller( g_xywindow_globals.color_clipper ), ClipperColourSetCaller() ),
888         m_viewname( ColourGetCaller( g_xywindow_globals.color_viewname ), ColourSetCaller( g_xywindow_globals.color_viewname ) ){
889 }
890 };
891
892 ColoursMenu g_ColoursMenu;
893
894 ui::MenuItem create_colours_menu(){
895         auto colours_menu_item = new_sub_menu_item_with_mnemonic( "Colors" );
896         auto menu_in_menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( colours_menu_item ) ));
897         if ( g_Layout_enableDetachableMenus.m_value ) {
898                 menu_tearoff( menu_in_menu );
899         }
900
901         auto menu_3 = create_sub_menu_with_mnemonic( menu_in_menu, "Themes" );
902         if ( g_Layout_enableDetachableMenus.m_value ) {
903                 menu_tearoff( menu_3 );
904         }
905
906         create_menu_item_with_mnemonic( menu_3, "QE4 Original", "ColorSchemeOriginal" );
907         create_menu_item_with_mnemonic( menu_3, "Q3Radiant Original", "ColorSchemeQER" );
908         create_menu_item_with_mnemonic( menu_3, "Black and Green", "ColorSchemeBlackAndGreen" );
909         create_menu_item_with_mnemonic( menu_3, "Maya/Max/Lightwave Emulation", "ColorSchemeYdnar" );
910
911         menu_separator( menu_in_menu );
912
913         create_menu_item_with_mnemonic( menu_in_menu, "_Texture Background...", "ChooseTextureBackgroundColor" );
914         create_menu_item_with_mnemonic( menu_in_menu, "Grid Background...", "ChooseGridBackgroundColor" );
915         create_menu_item_with_mnemonic( menu_in_menu, "Grid Major...", "ChooseGridMajorColor" );
916         create_menu_item_with_mnemonic( menu_in_menu, "Grid Minor...", "ChooseGridMinorColor" );
917         create_menu_item_with_mnemonic( menu_in_menu, "Grid Major Small...", "ChooseSmallGridMajorColor" );
918         create_menu_item_with_mnemonic( menu_in_menu, "Grid Minor Small...", "ChooseSmallGridMinorColor" );
919         create_menu_item_with_mnemonic( menu_in_menu, "Grid Text...", "ChooseGridTextColor" );
920         create_menu_item_with_mnemonic( menu_in_menu, "Grid Block...", "ChooseGridBlockColor" );
921         create_menu_item_with_mnemonic( menu_in_menu, "Default Brush...", "ChooseBrushColor" );
922         create_menu_item_with_mnemonic( menu_in_menu, "Camera Background...", "ChooseCameraBackgroundColor" );
923         create_menu_item_with_mnemonic( menu_in_menu, "Selected Brush...", "ChooseSelectedBrushColor" );
924         create_menu_item_with_mnemonic( menu_in_menu, "Selected Brush (Camera)...", "ChooseCameraSelectedBrushColor" );
925         create_menu_item_with_mnemonic( menu_in_menu, "Clipper...", "ChooseClipperColor" );
926         create_menu_item_with_mnemonic( menu_in_menu, "Active View name...", "ChooseOrthoViewNameColor" );
927
928         return colours_menu_item;
929 }
930
931
932 void Restart(){
933         PluginsMenu_clear();
934         PluginToolbar_clear();
935
936         Radiant_Shutdown();
937         Radiant_Initialise();
938
939         PluginsMenu_populate();
940
941         PluginToolbar_populate();
942 }
943
944
945 void thunk_OnSleep(){
946         g_pParentWnd->OnSleep();
947 }
948
949 void OpenHelpURL(){
950         OpenURL( "https://gitlab.com/xonotic/xonotic/wikis/Mapping" );
951 }
952
953 void OpenBugReportURL(){
954         OpenURL( "https://gitlab.com/xonotic/netradiant/issues" );
955 }
956
957
958 ui::Widget g_page_console;
959
960 void Console_ToggleShow(){
961         GroupDialog_showPage( g_page_console );
962 }
963
964 ui::Widget g_page_entity;
965
966 void EntityInspector_ToggleShow(){
967         GroupDialog_showPage( g_page_entity );
968 }
969
970
971
972 void SetClipMode( bool enable );
973 void ModeChangeNotify();
974
975 typedef void ( *ToolMode )();
976 ToolMode g_currentToolMode = 0;
977 bool g_currentToolModeSupportsComponentEditing = false;
978 ToolMode g_defaultToolMode = 0;
979
980
981
982 void SelectionSystem_DefaultMode(){
983         GlobalSelectionSystem().SetMode( SelectionSystem::ePrimitive );
984         GlobalSelectionSystem().SetComponentMode( SelectionSystem::eDefault );
985         ModeChangeNotify();
986 }
987
988
989 bool EdgeMode(){
990         return GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
991                    && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eEdge;
992 }
993
994 bool VertexMode(){
995         return GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
996                    && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eVertex;
997 }
998
999 bool FaceMode(){
1000         return GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
1001                    && GlobalSelectionSystem().ComponentMode() == SelectionSystem::eFace;
1002 }
1003
1004 template<bool( *BoolFunction ) ( )>
1005 class BoolFunctionExport
1006 {
1007 public:
1008 static void apply( const BoolImportCallback& importCallback ){
1009         importCallback( BoolFunction() );
1010 }
1011 };
1012
1013 typedef FreeCaller1<const BoolImportCallback&, &BoolFunctionExport<EdgeMode>::apply> EdgeModeApplyCaller;
1014 EdgeModeApplyCaller g_edgeMode_button_caller;
1015 BoolExportCallback g_edgeMode_button_callback( g_edgeMode_button_caller );
1016 ToggleItem g_edgeMode_button( g_edgeMode_button_callback );
1017
1018 typedef FreeCaller1<const BoolImportCallback&, &BoolFunctionExport<VertexMode>::apply> VertexModeApplyCaller;
1019 VertexModeApplyCaller g_vertexMode_button_caller;
1020 BoolExportCallback g_vertexMode_button_callback( g_vertexMode_button_caller );
1021 ToggleItem g_vertexMode_button( g_vertexMode_button_callback );
1022
1023 typedef FreeCaller1<const BoolImportCallback&, &BoolFunctionExport<FaceMode>::apply> FaceModeApplyCaller;
1024 FaceModeApplyCaller g_faceMode_button_caller;
1025 BoolExportCallback g_faceMode_button_callback( g_faceMode_button_caller );
1026 ToggleItem g_faceMode_button( g_faceMode_button_callback );
1027
1028 void ComponentModeChanged(){
1029         g_edgeMode_button.update();
1030         g_vertexMode_button.update();
1031         g_faceMode_button.update();
1032 }
1033
1034 void ComponentMode_SelectionChanged( const Selectable& selectable ){
1035         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent
1036                  && GlobalSelectionSystem().countSelected() == 0 ) {
1037                 SelectionSystem_DefaultMode();
1038                 ComponentModeChanged();
1039         }
1040 }
1041
1042 void SelectEdgeMode(){
1043 #if 0
1044         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1045                 GlobalSelectionSystem().Select( false );
1046         }
1047 #endif
1048
1049         if ( EdgeMode() ) {
1050                 SelectionSystem_DefaultMode();
1051         }
1052         else if ( GlobalSelectionSystem().countSelected() != 0 ) {
1053                 if ( !g_currentToolModeSupportsComponentEditing ) {
1054                         g_defaultToolMode();
1055                 }
1056
1057                 GlobalSelectionSystem().SetMode( SelectionSystem::eComponent );
1058                 GlobalSelectionSystem().SetComponentMode( SelectionSystem::eEdge );
1059         }
1060
1061         ComponentModeChanged();
1062
1063         ModeChangeNotify();
1064 }
1065
1066 void SelectVertexMode(){
1067 #if 0
1068         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1069                 GlobalSelectionSystem().Select( false );
1070         }
1071 #endif
1072
1073         if ( VertexMode() ) {
1074                 SelectionSystem_DefaultMode();
1075         }
1076         else if ( GlobalSelectionSystem().countSelected() != 0 ) {
1077                 if ( !g_currentToolModeSupportsComponentEditing ) {
1078                         g_defaultToolMode();
1079                 }
1080
1081                 GlobalSelectionSystem().SetMode( SelectionSystem::eComponent );
1082                 GlobalSelectionSystem().SetComponentMode( SelectionSystem::eVertex );
1083         }
1084
1085         ComponentModeChanged();
1086
1087         ModeChangeNotify();
1088 }
1089
1090 void SelectFaceMode(){
1091 #if 0
1092         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1093                 GlobalSelectionSystem().Select( false );
1094         }
1095 #endif
1096
1097         if ( FaceMode() ) {
1098                 SelectionSystem_DefaultMode();
1099         }
1100         else if ( GlobalSelectionSystem().countSelected() != 0 ) {
1101                 if ( !g_currentToolModeSupportsComponentEditing ) {
1102                         g_defaultToolMode();
1103                 }
1104
1105                 GlobalSelectionSystem().SetMode( SelectionSystem::eComponent );
1106                 GlobalSelectionSystem().SetComponentMode( SelectionSystem::eFace );
1107         }
1108
1109         ComponentModeChanged();
1110
1111         ModeChangeNotify();
1112 }
1113
1114
1115 class CloneSelected : public scene::Graph::Walker
1116 {
1117 bool doMakeUnique;
1118 NodeSmartReference worldspawn;
1119 public:
1120 CloneSelected( bool d ) : doMakeUnique( d ), worldspawn( Map_FindOrInsertWorldspawn( g_map ) ){
1121 }
1122 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1123         if ( path.size() == 1 ) {
1124                 return true;
1125         }
1126
1127         // ignore worldspawn, but keep checking children
1128         NodeSmartReference me( path.top().get() );
1129         if ( me == worldspawn ) {
1130                 return true;
1131         }
1132
1133         if ( !path.top().get().isRoot() ) {
1134                 Selectable* selectable = Instance_getSelectable( instance );
1135                 if ( selectable != 0
1136                          && selectable->isSelected() ) {
1137                         return false;
1138                 }
1139         }
1140
1141         return true;
1142 }
1143 void post( const scene::Path& path, scene::Instance& instance ) const {
1144         if ( path.size() == 1 ) {
1145                 return;
1146         }
1147
1148         // ignore worldspawn, but keep checking children
1149         NodeSmartReference me( path.top().get() );
1150         if ( me == worldspawn ) {
1151                 return;
1152         }
1153
1154         if ( !path.top().get().isRoot() ) {
1155                 Selectable* selectable = Instance_getSelectable( instance );
1156                 if ( selectable != 0
1157                          && selectable->isSelected() ) {
1158                         NodeSmartReference clone( Node_Clone( path.top() ) );
1159                         if ( doMakeUnique ) {
1160                                 Map_gatherNamespaced( clone );
1161                         }
1162                         Node_getTraversable( path.parent().get() )->insert( clone );
1163                 }
1164         }
1165 }
1166 };
1167
1168 void Scene_Clone_Selected( scene::Graph& graph, bool doMakeUnique ){
1169         graph.traverse( CloneSelected( doMakeUnique ) );
1170
1171         Map_mergeClonedNames();
1172 }
1173
1174 enum ENudgeDirection
1175 {
1176         eNudgeUp = 1,
1177         eNudgeDown = 3,
1178         eNudgeLeft = 0,
1179         eNudgeRight = 2,
1180 };
1181
1182 struct AxisBase
1183 {
1184         Vector3 x;
1185         Vector3 y;
1186         Vector3 z;
1187         AxisBase( const Vector3& x_, const Vector3& y_, const Vector3& z_ )
1188                 : x( x_ ), y( y_ ), z( z_ ){
1189         }
1190 };
1191
1192 AxisBase AxisBase_forViewType( VIEWTYPE viewtype ){
1193         switch ( viewtype )
1194         {
1195         case XY:
1196                 return AxisBase( g_vector3_axis_x, g_vector3_axis_y, g_vector3_axis_z );
1197         case XZ:
1198                 return AxisBase( g_vector3_axis_x, g_vector3_axis_z, g_vector3_axis_y );
1199         case YZ:
1200                 return AxisBase( g_vector3_axis_y, g_vector3_axis_z, g_vector3_axis_x );
1201         }
1202
1203         ERROR_MESSAGE( "invalid viewtype" );
1204         return AxisBase( Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ), Vector3( 0, 0, 0 ) );
1205 }
1206
1207 Vector3 AxisBase_axisForDirection( const AxisBase& axes, ENudgeDirection direction ){
1208         switch ( direction )
1209         {
1210         case eNudgeLeft:
1211                 return vector3_negated( axes.x );
1212         case eNudgeUp:
1213                 return axes.y;
1214         case eNudgeRight:
1215                 return axes.x;
1216         case eNudgeDown:
1217                 return vector3_negated( axes.y );
1218         }
1219
1220         ERROR_MESSAGE( "invalid direction" );
1221         return Vector3( 0, 0, 0 );
1222 }
1223
1224 void NudgeSelection( ENudgeDirection direction, float fAmount, VIEWTYPE viewtype ){
1225         AxisBase axes( AxisBase_forViewType( viewtype ) );
1226         Vector3 view_direction( vector3_negated( axes.z ) );
1227         Vector3 nudge( vector3_scaled( AxisBase_axisForDirection( axes, direction ), fAmount ) );
1228         GlobalSelectionSystem().NudgeManipulator( nudge, view_direction );
1229 }
1230
1231 void Selection_Clone(){
1232         if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1233                 UndoableCommand undo( "cloneSelected" );
1234
1235                 Scene_Clone_Selected( GlobalSceneGraph(), false );
1236
1237                 //NudgeSelection(eNudgeRight, GetGridSize(), GlobalXYWnd_getCurrentViewType());
1238                 //NudgeSelection(eNudgeDown, GetGridSize(), GlobalXYWnd_getCurrentViewType());
1239         }
1240 }
1241
1242 void Selection_Clone_MakeUnique(){
1243         if ( GlobalSelectionSystem().Mode() == SelectionSystem::ePrimitive ) {
1244                 UndoableCommand undo( "cloneSelectedMakeUnique" );
1245
1246                 Scene_Clone_Selected( GlobalSceneGraph(), true );
1247
1248                 //NudgeSelection(eNudgeRight, GetGridSize(), GlobalXYWnd_getCurrentViewType());
1249                 //NudgeSelection(eNudgeDown, GetGridSize(), GlobalXYWnd_getCurrentViewType());
1250         }
1251 }
1252
1253 // called when the escape key is used (either on the main window or on an inspector)
1254 void Selection_Deselect(){
1255         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1256                 if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) {
1257                         GlobalSelectionSystem().setSelectedAllComponents( false );
1258                 }
1259                 else
1260                 {
1261                         SelectionSystem_DefaultMode();
1262                         ComponentModeChanged();
1263                 }
1264         }
1265         else
1266         {
1267                 if ( GlobalSelectionSystem().countSelectedComponents() != 0 ) {
1268                         GlobalSelectionSystem().setSelectedAllComponents( false );
1269                 }
1270                 else
1271                 {
1272                         GlobalSelectionSystem().setSelectedAll( false );
1273                 }
1274         }
1275 }
1276
1277
1278 void Selection_NudgeUp(){
1279         UndoableCommand undo( "nudgeSelectedUp" );
1280         NudgeSelection( eNudgeUp, GetGridSize(), GlobalXYWnd_getCurrentViewType() );
1281 }
1282
1283 void Selection_NudgeDown(){
1284         UndoableCommand undo( "nudgeSelectedDown" );
1285         NudgeSelection( eNudgeDown, GetGridSize(), GlobalXYWnd_getCurrentViewType() );
1286 }
1287
1288 void Selection_NudgeLeft(){
1289         UndoableCommand undo( "nudgeSelectedLeft" );
1290         NudgeSelection( eNudgeLeft, GetGridSize(), GlobalXYWnd_getCurrentViewType() );
1291 }
1292
1293 void Selection_NudgeRight(){
1294         UndoableCommand undo( "nudgeSelectedRight" );
1295         NudgeSelection( eNudgeRight, GetGridSize(), GlobalXYWnd_getCurrentViewType() );
1296 }
1297
1298
1299 void TranslateToolExport( const BoolImportCallback& importCallback ){
1300         importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eTranslate );
1301 }
1302
1303 void RotateToolExport( const BoolImportCallback& importCallback ){
1304         importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eRotate );
1305 }
1306
1307 void ScaleToolExport( const BoolImportCallback& importCallback ){
1308         importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eScale );
1309 }
1310
1311 void DragToolExport( const BoolImportCallback& importCallback ){
1312         importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eDrag );
1313 }
1314
1315 void ClipperToolExport( const BoolImportCallback& importCallback ){
1316         importCallback( GlobalSelectionSystem().ManipulatorMode() == SelectionSystem::eClip );
1317 }
1318
1319 FreeCaller1<const BoolImportCallback&, TranslateToolExport> g_translatemode_button_caller;
1320 BoolExportCallback g_translatemode_button_callback( g_translatemode_button_caller );
1321 ToggleItem g_translatemode_button( g_translatemode_button_callback );
1322
1323 FreeCaller1<const BoolImportCallback&, RotateToolExport> g_rotatemode_button_caller;
1324 BoolExportCallback g_rotatemode_button_callback( g_rotatemode_button_caller );
1325 ToggleItem g_rotatemode_button( g_rotatemode_button_callback );
1326
1327 FreeCaller1<const BoolImportCallback&, ScaleToolExport> g_scalemode_button_caller;
1328 BoolExportCallback g_scalemode_button_callback( g_scalemode_button_caller );
1329 ToggleItem g_scalemode_button( g_scalemode_button_callback );
1330
1331 FreeCaller1<const BoolImportCallback&, DragToolExport> g_dragmode_button_caller;
1332 BoolExportCallback g_dragmode_button_callback( g_dragmode_button_caller );
1333 ToggleItem g_dragmode_button( g_dragmode_button_callback );
1334
1335 FreeCaller1<const BoolImportCallback&, ClipperToolExport> g_clipper_button_caller;
1336 BoolExportCallback g_clipper_button_callback( g_clipper_button_caller );
1337 ToggleItem g_clipper_button( g_clipper_button_callback );
1338
1339 void ToolChanged(){
1340         g_translatemode_button.update();
1341         g_rotatemode_button.update();
1342         g_scalemode_button.update();
1343         g_dragmode_button.update();
1344         g_clipper_button.update();
1345 }
1346
1347 const char* const c_ResizeMode_status = "QE4 Drag Tool: move and resize objects";
1348
1349 void DragMode(){
1350         if ( g_currentToolMode == DragMode && g_defaultToolMode != DragMode ) {
1351                 g_defaultToolMode();
1352         }
1353         else
1354         {
1355                 g_currentToolMode = DragMode;
1356                 g_currentToolModeSupportsComponentEditing = true;
1357
1358                 OnClipMode( false );
1359
1360                 Sys_Status( c_ResizeMode_status );
1361                 GlobalSelectionSystem().SetManipulatorMode( SelectionSystem::eDrag );
1362                 ToolChanged();
1363                 ModeChangeNotify();
1364         }
1365 }
1366
1367
1368 const char* const c_TranslateMode_status = "Translate Tool: translate objects and components";
1369
1370 void TranslateMode(){
1371         if ( g_currentToolMode == TranslateMode && g_defaultToolMode != TranslateMode ) {
1372                 g_defaultToolMode();
1373         }
1374         else
1375         {
1376                 g_currentToolMode = TranslateMode;
1377                 g_currentToolModeSupportsComponentEditing = true;
1378
1379                 OnClipMode( false );
1380
1381                 Sys_Status( c_TranslateMode_status );
1382                 GlobalSelectionSystem().SetManipulatorMode( SelectionSystem::eTranslate );
1383                 ToolChanged();
1384                 ModeChangeNotify();
1385         }
1386 }
1387
1388 const char* const c_RotateMode_status = "Rotate Tool: rotate objects and components";
1389
1390 void RotateMode(){
1391         if ( g_currentToolMode == RotateMode && g_defaultToolMode != RotateMode ) {
1392                 g_defaultToolMode();
1393         }
1394         else
1395         {
1396                 g_currentToolMode = RotateMode;
1397                 g_currentToolModeSupportsComponentEditing = true;
1398
1399                 OnClipMode( false );
1400
1401                 Sys_Status( c_RotateMode_status );
1402                 GlobalSelectionSystem().SetManipulatorMode( SelectionSystem::eRotate );
1403                 ToolChanged();
1404                 ModeChangeNotify();
1405         }
1406 }
1407
1408 const char* const c_ScaleMode_status = "Scale Tool: scale objects and components";
1409
1410 void ScaleMode(){
1411         if ( g_currentToolMode == ScaleMode && g_defaultToolMode != ScaleMode ) {
1412                 g_defaultToolMode();
1413         }
1414         else
1415         {
1416                 g_currentToolMode = ScaleMode;
1417                 g_currentToolModeSupportsComponentEditing = true;
1418
1419                 OnClipMode( false );
1420
1421                 Sys_Status( c_ScaleMode_status );
1422                 GlobalSelectionSystem().SetManipulatorMode( SelectionSystem::eScale );
1423                 ToolChanged();
1424                 ModeChangeNotify();
1425         }
1426 }
1427
1428
1429 const char* const c_ClipperMode_status = "Clipper Tool: apply clip planes to objects";
1430
1431
1432 void ClipperMode(){
1433         if ( g_currentToolMode == ClipperMode && g_defaultToolMode != ClipperMode ) {
1434                 g_defaultToolMode();
1435         }
1436         else
1437         {
1438                 g_currentToolMode = ClipperMode;
1439                 g_currentToolModeSupportsComponentEditing = false;
1440
1441                 SelectionSystem_DefaultMode();
1442
1443                 OnClipMode( true );
1444
1445                 Sys_Status( c_ClipperMode_status );
1446                 GlobalSelectionSystem().SetManipulatorMode( SelectionSystem::eClip );
1447                 ToolChanged();
1448                 ModeChangeNotify();
1449         }
1450 }
1451
1452
1453 void Texdef_Rotate( float angle ){
1454         StringOutputStream command;
1455         command << "brushRotateTexture -angle " << angle;
1456         UndoableCommand undo( command.c_str() );
1457         Select_RotateTexture( angle );
1458 }
1459
1460 void Texdef_RotateClockwise(){
1461         Texdef_Rotate( static_cast<float>( fabs( g_si_globals.rotate ) ) );
1462 }
1463
1464 void Texdef_RotateAntiClockwise(){
1465         Texdef_Rotate( static_cast<float>( -fabs( g_si_globals.rotate ) ) );
1466 }
1467
1468 void Texdef_Scale( float x, float y ){
1469         StringOutputStream command;
1470         command << "brushScaleTexture -x " << x << " -y " << y;
1471         UndoableCommand undo( command.c_str() );
1472         Select_ScaleTexture( x, y );
1473 }
1474
1475 void Texdef_ScaleUp(){
1476         Texdef_Scale( 0, g_si_globals.scale[1] );
1477 }
1478
1479 void Texdef_ScaleDown(){
1480         Texdef_Scale( 0, -g_si_globals.scale[1] );
1481 }
1482
1483 void Texdef_ScaleLeft(){
1484         Texdef_Scale( -g_si_globals.scale[0],0 );
1485 }
1486
1487 void Texdef_ScaleRight(){
1488         Texdef_Scale( g_si_globals.scale[0],0 );
1489 }
1490
1491 void Texdef_Shift( float x, float y ){
1492         StringOutputStream command;
1493         command << "brushShiftTexture -x " << x << " -y " << y;
1494         UndoableCommand undo( command.c_str() );
1495         Select_ShiftTexture( x, y );
1496 }
1497
1498 void Texdef_ShiftLeft(){
1499         Texdef_Shift( -g_si_globals.shift[0], 0 );
1500 }
1501
1502 void Texdef_ShiftRight(){
1503         Texdef_Shift( g_si_globals.shift[0], 0 );
1504 }
1505
1506 void Texdef_ShiftUp(){
1507         Texdef_Shift( 0, g_si_globals.shift[1] );
1508 }
1509
1510 void Texdef_ShiftDown(){
1511         Texdef_Shift( 0, -g_si_globals.shift[1] );
1512 }
1513
1514
1515
1516 class SnappableSnapToGridSelected : public scene::Graph::Walker
1517 {
1518 float m_snap;
1519 public:
1520 SnappableSnapToGridSelected( float snap )
1521         : m_snap( snap ){
1522 }
1523 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1524         if ( path.top().get().visible() ) {
1525                 Snappable* snappable = Node_getSnappable( path.top() );
1526                 if ( snappable != 0
1527                          && Instance_getSelectable( instance )->isSelected() ) {
1528                         snappable->snapto( m_snap );
1529                 }
1530         }
1531         return true;
1532 }
1533 };
1534
1535 void Scene_SnapToGrid_Selected( scene::Graph& graph, float snap ){
1536         graph.traverse( SnappableSnapToGridSelected( snap ) );
1537 }
1538
1539 class ComponentSnappableSnapToGridSelected : public scene::Graph::Walker
1540 {
1541 float m_snap;
1542 public:
1543 ComponentSnappableSnapToGridSelected( float snap )
1544         : m_snap( snap ){
1545 }
1546 bool pre( const scene::Path& path, scene::Instance& instance ) const {
1547         if ( path.top().get().visible() ) {
1548                 ComponentSnappable* componentSnappable = Instance_getComponentSnappable( instance );
1549                 if ( componentSnappable != 0
1550                          && Instance_getSelectable( instance )->isSelected() ) {
1551                         componentSnappable->snapComponents( m_snap );
1552                 }
1553         }
1554         return true;
1555 }
1556 };
1557
1558 void Scene_SnapToGrid_Component_Selected( scene::Graph& graph, float snap ){
1559         graph.traverse( ComponentSnappableSnapToGridSelected( snap ) );
1560 }
1561
1562 void Selection_SnapToGrid(){
1563         StringOutputStream command;
1564         command << "snapSelected -grid " << GetGridSize();
1565         UndoableCommand undo( command.c_str() );
1566
1567         if ( GlobalSelectionSystem().Mode() == SelectionSystem::eComponent ) {
1568                 Scene_SnapToGrid_Component_Selected( GlobalSceneGraph(), GetGridSize() );
1569         }
1570         else
1571         {
1572                 Scene_SnapToGrid_Selected( GlobalSceneGraph(), GetGridSize() );
1573         }
1574 }
1575
1576
1577 static gint qe_every_second( gpointer data ){
1578         GdkModifierType mask;
1579
1580         gdk_window_get_pointer( 0, 0, 0, &mask );
1581
1582         if ( ( mask & ( GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK ) ) == 0 ) {
1583                 QE_CheckAutoSave();
1584         }
1585
1586         return TRUE;
1587 }
1588
1589 guint s_qe_every_second_id = 0;
1590
1591 void EverySecondTimer_enable(){
1592         if ( s_qe_every_second_id == 0 ) {
1593                 s_qe_every_second_id = g_timeout_add( 1000, qe_every_second, 0 );
1594         }
1595 }
1596
1597 void EverySecondTimer_disable(){
1598         if ( s_qe_every_second_id != 0 ) {
1599                 g_source_remove( s_qe_every_second_id );
1600                 s_qe_every_second_id = 0;
1601         }
1602 }
1603
1604 gint window_realize_remove_decoration( ui::Widget widget, gpointer data ){
1605         gdk_window_set_decorations( gtk_widget_get_window(widget), (GdkWMDecoration)( GDK_DECOR_ALL | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE ) );
1606         return FALSE;
1607 }
1608
1609 class WaitDialog
1610 {
1611 public:
1612 ui::Window m_window{ui::null};
1613 ui::Label m_label{ui::null};
1614 };
1615
1616 WaitDialog create_wait_dialog( const char* title, const char* text ){
1617         WaitDialog dialog;
1618
1619         dialog.m_window = MainFrame_getWindow().create_floating_window(title);
1620         gtk_window_set_resizable( dialog.m_window, FALSE );
1621         gtk_container_set_border_width( GTK_CONTAINER( dialog.m_window ), 0 );
1622         gtk_window_set_position( dialog.m_window, GTK_WIN_POS_CENTER_ON_PARENT );
1623
1624         dialog.m_window.connect( "realize", G_CALLBACK( window_realize_remove_decoration ), 0 );
1625
1626         {
1627                 dialog.m_label = ui::Label( text );
1628                 gtk_misc_set_alignment( GTK_MISC( dialog.m_label ), 0.0, 0.5 );
1629                 gtk_label_set_justify( dialog.m_label, GTK_JUSTIFY_LEFT );
1630                 dialog.m_label.show();
1631                 gtk_widget_set_size_request( GTK_WIDGET( dialog.m_label ), 200, -1 );
1632
1633                 dialog.m_window.add(dialog.m_label);
1634         }
1635         return dialog;
1636 }
1637
1638 namespace
1639 {
1640 clock_t g_lastRedrawTime = 0;
1641 const clock_t c_redrawInterval = clock_t( CLOCKS_PER_SEC / 10 );
1642
1643 bool redrawRequired(){
1644         clock_t currentTime = std::clock();
1645         if ( currentTime - g_lastRedrawTime >= c_redrawInterval ) {
1646                 g_lastRedrawTime = currentTime;
1647                 return true;
1648         }
1649         return false;
1650 }
1651 }
1652
1653 bool MainFrame_isActiveApp(){
1654         //globalOutputStream() << "listing\n";
1655         GList* list = gtk_window_list_toplevels();
1656         for ( GList* i = list; i != 0; i = g_list_next( i ) )
1657         {
1658                 //globalOutputStream() << "toplevel.. ";
1659                 if ( gtk_window_is_active( GTK_WINDOW( i->data ) ) ) {
1660                         //globalOutputStream() << "is active\n";
1661                         return true;
1662                 }
1663                 //globalOutputStream() << "not active\n";
1664         }
1665         return false;
1666 }
1667
1668 typedef std::list<CopiedString> StringStack;
1669 StringStack g_wait_stack;
1670 WaitDialog g_wait;
1671
1672 bool ScreenUpdates_Enabled(){
1673         return g_wait_stack.empty();
1674 }
1675
1676 void ScreenUpdates_process(){
1677         if ( redrawRequired() && gtk_widget_get_visible( g_wait.m_window ) ) {
1678                 ui::process();
1679         }
1680 }
1681
1682
1683 void ScreenUpdates_Disable( const char* message, const char* title ){
1684         if ( g_wait_stack.empty() ) {
1685                 EverySecondTimer_disable();
1686
1687                 ui::process();
1688
1689                 bool isActiveApp = MainFrame_isActiveApp();
1690
1691                 g_wait = create_wait_dialog( title, message );
1692                 gtk_grab_add( GTK_WIDGET( g_wait.m_window ) );
1693
1694                 if ( isActiveApp ) {
1695                         g_wait.m_window.show();
1696                         ScreenUpdates_process();
1697                 }
1698         }
1699         else if ( gtk_widget_get_visible( g_wait.m_window ) ) {
1700                 gtk_label_set_text( g_wait.m_label, message );
1701                 ScreenUpdates_process();
1702         }
1703         g_wait_stack.push_back( message );
1704 }
1705
1706 void ScreenUpdates_Enable(){
1707         ASSERT_MESSAGE( !ScreenUpdates_Enabled(), "screen updates already enabled" );
1708         g_wait_stack.pop_back();
1709         if ( g_wait_stack.empty() ) {
1710                 EverySecondTimer_enable();
1711                 //gtk_widget_set_sensitive(GTK_WIDGET(MainFrame_getWindow()), TRUE);
1712
1713                 gtk_grab_remove( GTK_WIDGET( g_wait.m_window ) );
1714                 destroy_floating_window( g_wait.m_window );
1715                 g_wait.m_window = ui::Window{ui::null};
1716
1717                 //gtk_window_present(MainFrame_getWindow());
1718         }
1719         else if ( gtk_widget_get_visible( g_wait.m_window ) ) {
1720                 gtk_label_set_text( g_wait.m_label, g_wait_stack.back().c_str() );
1721                 ScreenUpdates_process();
1722         }
1723 }
1724
1725
1726
1727 void GlobalCamera_UpdateWindow(){
1728         if ( g_pParentWnd != 0 ) {
1729                 CamWnd_Update( *g_pParentWnd->GetCamWnd() );
1730         }
1731 }
1732
1733 void XY_UpdateWindow( MainFrame& mainframe ){
1734         if ( mainframe.GetXYWnd() != 0 ) {
1735                 XYWnd_Update( *mainframe.GetXYWnd() );
1736         }
1737 }
1738
1739 void XZ_UpdateWindow( MainFrame& mainframe ){
1740         if ( mainframe.GetXZWnd() != 0 ) {
1741                 XYWnd_Update( *mainframe.GetXZWnd() );
1742         }
1743 }
1744
1745 void YZ_UpdateWindow( MainFrame& mainframe ){
1746         if ( mainframe.GetYZWnd() != 0 ) {
1747                 XYWnd_Update( *mainframe.GetYZWnd() );
1748         }
1749 }
1750
1751 void XY_UpdateAllWindows( MainFrame& mainframe ){
1752         XY_UpdateWindow( mainframe );
1753         XZ_UpdateWindow( mainframe );
1754         YZ_UpdateWindow( mainframe );
1755 }
1756
1757 void XY_UpdateAllWindows(){
1758         if ( g_pParentWnd != 0 ) {
1759                 XY_UpdateAllWindows( *g_pParentWnd );
1760         }
1761 }
1762
1763 void UpdateAllWindows(){
1764         GlobalCamera_UpdateWindow();
1765         XY_UpdateAllWindows();
1766 }
1767
1768
1769 void ModeChangeNotify(){
1770         SceneChangeNotify();
1771 }
1772
1773 void ClipperChangeNotify(){
1774         GlobalCamera_UpdateWindow();
1775         XY_UpdateAllWindows();
1776 }
1777
1778
1779 LatchedInt g_Layout_viewStyle( 0, "Window Layout" );
1780 LatchedBool g_Layout_enableDetachableMenus( true, "Detachable Menus" );
1781 LatchedBool g_Layout_enablePatchToolbar( true, "Patch Toolbar" );
1782 LatchedBool g_Layout_enablePluginToolbar( true, "Plugin Toolbar" );
1783
1784
1785
1786 ui::MenuItem create_file_menu(){
1787         // File menu
1788         auto file_menu_item = new_sub_menu_item_with_mnemonic( "_File" );
1789         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( file_menu_item ) ));
1790         if ( g_Layout_enableDetachableMenus.m_value ) {
1791                 menu_tearoff( menu );
1792         }
1793
1794         create_menu_item_with_mnemonic( menu, "_New Map", "NewMap" );
1795         menu_separator( menu );
1796
1797 #if 0
1798         //++timo temporary experimental stuff for sleep mode..
1799         create_menu_item_with_mnemonic( menu, "_Sleep", "Sleep" );
1800         menu_separator( menu );
1801         // end experimental
1802 #endif
1803
1804         create_menu_item_with_mnemonic( menu, "_Open...", "OpenMap" );
1805
1806         create_menu_item_with_mnemonic( menu, "_Import...", "ImportMap" );
1807         create_menu_item_with_mnemonic( menu, "_Save", "SaveMap" );
1808         create_menu_item_with_mnemonic( menu, "Save _as...", "SaveMapAs" );
1809         create_menu_item_with_mnemonic( menu, "_Export selected...", "ExportSelected" );
1810         menu_separator( menu );
1811         create_menu_item_with_mnemonic( menu, "Save re_gion...", "SaveRegion" );
1812         menu_separator( menu );
1813         create_menu_item_with_mnemonic( menu, "_Refresh models", "RefreshReferences" );
1814         menu_separator( menu );
1815         create_menu_item_with_mnemonic( menu, "Pro_ject settings...", "ProjectSettings" );
1816         menu_separator( menu );
1817         create_menu_item_with_mnemonic( menu, "_Pointfile...", "TogglePointfile" );
1818         menu_separator( menu );
1819         MRU_constructMenu( menu );
1820         menu_separator( menu );
1821         create_menu_item_with_mnemonic( menu, "E_xit", "Exit" );
1822
1823         return file_menu_item;
1824 }
1825
1826 ui::MenuItem create_edit_menu(){
1827         // Edit menu
1828         auto edit_menu_item = new_sub_menu_item_with_mnemonic( "_Edit" );
1829         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( edit_menu_item ) ));
1830         if ( g_Layout_enableDetachableMenus.m_value ) {
1831                 menu_tearoff( menu );
1832         }
1833         create_menu_item_with_mnemonic( menu, "_Undo", "Undo" );
1834         create_menu_item_with_mnemonic( menu, "_Redo", "Redo" );
1835         menu_separator( menu );
1836         create_menu_item_with_mnemonic( menu, "_Copy", "Copy" );
1837         create_menu_item_with_mnemonic( menu, "_Paste", "Paste" );
1838         create_menu_item_with_mnemonic( menu, "P_aste To Camera", "PasteToCamera" );
1839         menu_separator( menu );
1840         create_menu_item_with_mnemonic( menu, "_Duplicate", "CloneSelection" );
1841         create_menu_item_with_mnemonic( menu, "Duplicate, make uni_que", "CloneSelectionAndMakeUnique" );
1842         create_menu_item_with_mnemonic( menu, "D_elete", "DeleteSelection" );
1843         menu_separator( menu );
1844         create_menu_item_with_mnemonic( menu, "Pa_rent", "ParentSelection" );
1845         menu_separator( menu );
1846         create_menu_item_with_mnemonic( menu, "C_lear Selection", "UnSelectSelection" );
1847         create_menu_item_with_mnemonic( menu, "_Invert Selection", "InvertSelection" );
1848         create_menu_item_with_mnemonic( menu, "Select i_nside", "SelectInside" );
1849         create_menu_item_with_mnemonic( menu, "Select _touching", "SelectTouching" );
1850
1851         auto convert_menu = create_sub_menu_with_mnemonic( menu, "E_xpand Selection" );
1852         if ( g_Layout_enableDetachableMenus.m_value ) {
1853                 menu_tearoff( convert_menu );
1854         }
1855         create_menu_item_with_mnemonic( convert_menu, "To Whole _Entities", "ExpandSelectionToEntities" );
1856
1857         menu_separator( menu );
1858         create_menu_item_with_mnemonic( menu, "Pre_ferences...", "Preferences" );
1859
1860         return edit_menu_item;
1861 }
1862
1863 void fill_view_xy_top_menu( ui::Menu menu ){
1864         create_check_menu_item_with_mnemonic( menu, "XY (Top) View", "ToggleView" );
1865 }
1866
1867
1868 void fill_view_yz_side_menu( ui::Menu menu ){
1869         create_check_menu_item_with_mnemonic( menu, "YZ (Side) View", "ToggleSideView" );
1870 }
1871
1872
1873 void fill_view_xz_front_menu( ui::Menu menu ){
1874         create_check_menu_item_with_mnemonic( menu, "XZ (Front) View", "ToggleFrontView" );
1875 }
1876
1877
1878 ui::Widget g_toggle_z_item;
1879 ui::Widget g_toggle_console_item;
1880 ui::Widget g_toggle_entity_item;
1881 ui::Widget g_toggle_entitylist_item;
1882
1883 ui::MenuItem create_view_menu( MainFrame::EViewStyle style ){
1884         // View menu
1885         auto view_menu_item = new_sub_menu_item_with_mnemonic( "Vie_w" );
1886         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( view_menu_item ) ));
1887         if ( g_Layout_enableDetachableMenus.m_value ) {
1888                 menu_tearoff( menu );
1889         }
1890
1891         if ( style == MainFrame::eFloating ) {
1892                 fill_view_camera_menu( menu );
1893                 fill_view_xy_top_menu( menu );
1894                 fill_view_yz_side_menu( menu );
1895                 fill_view_xz_front_menu( menu );
1896         }
1897         if ( style == MainFrame::eFloating || style == MainFrame::eSplit ) {
1898                 create_menu_item_with_mnemonic( menu, "Console View", "ToggleConsole" );
1899                 create_menu_item_with_mnemonic( menu, "Texture Browser", "ToggleTextures" );
1900                 create_menu_item_with_mnemonic( menu, "Entity Inspector", "ToggleEntityInspector" );
1901         }
1902         else
1903         {
1904                 create_menu_item_with_mnemonic( menu, "Entity Inspector", "ViewEntityInfo" );
1905         }
1906         create_menu_item_with_mnemonic( menu, "_Surface Inspector", "SurfaceInspector" );
1907         create_menu_item_with_mnemonic( menu, "Entity List", "EntityList" );
1908
1909         menu_separator( menu );
1910         {
1911                 auto camera_menu = create_sub_menu_with_mnemonic( menu, "Camera" );
1912                 if ( g_Layout_enableDetachableMenus.m_value ) {
1913                         menu_tearoff( camera_menu );
1914                 }
1915                 create_menu_item_with_mnemonic( camera_menu, "_Center", "CenterView" );
1916                 create_menu_item_with_mnemonic( camera_menu, "_Up Floor", "UpFloor" );
1917                 create_menu_item_with_mnemonic( camera_menu, "_Down Floor", "DownFloor" );
1918                 menu_separator( camera_menu );
1919                 create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane In", "CubicClipZoomIn" );
1920                 create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane Out", "CubicClipZoomOut" );
1921                 menu_separator( camera_menu );
1922                 create_menu_item_with_mnemonic( camera_menu, "Next leak spot", "NextLeakSpot" );
1923                 create_menu_item_with_mnemonic( camera_menu, "Previous leak spot", "PrevLeakSpot" );
1924                 menu_separator( camera_menu );
1925                 create_menu_item_with_mnemonic( camera_menu, "Look Through Selected", "LookThroughSelected" );
1926                 create_menu_item_with_mnemonic( camera_menu, "Look Through Camera", "LookThroughCamera" );
1927         }
1928         menu_separator( menu );
1929         {
1930                 auto orthographic_menu = create_sub_menu_with_mnemonic( menu, "Orthographic" );
1931                 if ( g_Layout_enableDetachableMenus.m_value ) {
1932                         menu_tearoff( orthographic_menu );
1933                 }
1934                 if ( style == MainFrame::eRegular || style == MainFrame::eRegularLeft || style == MainFrame::eFloating ) {
1935                         create_menu_item_with_mnemonic( orthographic_menu, "_Next (XY, YZ, XY)", "NextView" );
1936                         create_menu_item_with_mnemonic( orthographic_menu, "XY (Top)", "ViewTop" );
1937                         create_menu_item_with_mnemonic( orthographic_menu, "YZ", "ViewSide" );
1938                         create_menu_item_with_mnemonic( orthographic_menu, "XZ", "ViewFront" );
1939                         menu_separator( orthographic_menu );
1940                 }
1941
1942                 create_menu_item_with_mnemonic( orthographic_menu, "_XY 100%", "Zoom100" );
1943                 create_menu_item_with_mnemonic( orthographic_menu, "XY Zoom _In", "ZoomIn" );
1944                 create_menu_item_with_mnemonic( orthographic_menu, "XY Zoom _Out", "ZoomOut" );
1945         }
1946
1947         menu_separator( menu );
1948
1949         {
1950                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Show" );
1951                 if ( g_Layout_enableDetachableMenus.m_value ) {
1952                         menu_tearoff( menu_in_menu );
1953                 }
1954                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show _Angles", "ShowAngles" );
1955                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show _Names", "ShowNames" );
1956                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show Blocks", "ShowBlocks" );
1957                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show C_oordinates", "ShowCoordinates" );
1958                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show Window Outline", "ShowWindowOutline" );
1959                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show Axes", "ShowAxes" );
1960                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show Workzone", "ShowWorkzone" );
1961                 create_check_menu_item_with_mnemonic( menu_in_menu, "Show Stats", "ShowStats" );
1962         }
1963
1964         {
1965                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Filter" );
1966                 if ( g_Layout_enableDetachableMenus.m_value ) {
1967                         menu_tearoff( menu_in_menu );
1968                 }
1969                 Filters_constructMenu( menu_in_menu );
1970         }
1971         menu_separator( menu );
1972         {
1973                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Hide/Show" );
1974                 if ( g_Layout_enableDetachableMenus.m_value ) {
1975                         menu_tearoff( menu_in_menu );
1976                 }
1977                 create_menu_item_with_mnemonic( menu_in_menu, "Hide Selected", "HideSelected" );
1978                 create_menu_item_with_mnemonic( menu_in_menu, "Show Hidden", "ShowHidden" );
1979         }
1980         menu_separator( menu );
1981         {
1982                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Region" );
1983                 if ( g_Layout_enableDetachableMenus.m_value ) {
1984                         menu_tearoff( menu_in_menu );
1985                 }
1986                 create_menu_item_with_mnemonic( menu_in_menu, "_Off", "RegionOff" );
1987                 create_menu_item_with_mnemonic( menu_in_menu, "_Set XY", "RegionSetXY" );
1988                 create_menu_item_with_mnemonic( menu_in_menu, "Set _Brush", "RegionSetBrush" );
1989                 create_menu_item_with_mnemonic( menu_in_menu, "Set Se_lected Brushes", "RegionSetSelection" );
1990         }
1991
1992         command_connect_accelerator( "CenterXYView" );
1993
1994         return view_menu_item;
1995 }
1996
1997 ui::MenuItem create_selection_menu(){
1998         // Selection menu
1999         auto selection_menu_item = new_sub_menu_item_with_mnemonic( "M_odify" );
2000         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( selection_menu_item ) ));
2001         if ( g_Layout_enableDetachableMenus.m_value ) {
2002                 menu_tearoff( menu );
2003         }
2004
2005         {
2006                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Components" );
2007                 if ( g_Layout_enableDetachableMenus.m_value ) {
2008                         menu_tearoff( menu_in_menu );
2009                 }
2010                 create_check_menu_item_with_mnemonic( menu_in_menu, "_Edges", "DragEdges" );
2011                 create_check_menu_item_with_mnemonic( menu_in_menu, "_Vertices", "DragVertices" );
2012                 create_check_menu_item_with_mnemonic( menu_in_menu, "_Faces", "DragFaces" );
2013         }
2014
2015         menu_separator( menu );
2016
2017         {
2018                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Nudge" );
2019                 if ( g_Layout_enableDetachableMenus.m_value ) {
2020                         menu_tearoff( menu_in_menu );
2021                 }
2022                 create_menu_item_with_mnemonic( menu_in_menu, "Nudge Left", "SelectNudgeLeft" );
2023                 create_menu_item_with_mnemonic( menu_in_menu, "Nudge Right", "SelectNudgeRight" );
2024                 create_menu_item_with_mnemonic( menu_in_menu, "Nudge Up", "SelectNudgeUp" );
2025                 create_menu_item_with_mnemonic( menu_in_menu, "Nudge Down", "SelectNudgeDown" );
2026         }
2027         {
2028                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Rotate" );
2029                 if ( g_Layout_enableDetachableMenus.m_value ) {
2030                         menu_tearoff( menu_in_menu );
2031                 }
2032                 create_menu_item_with_mnemonic( menu_in_menu, "Rotate X", "RotateSelectionX" );
2033                 create_menu_item_with_mnemonic( menu_in_menu, "Rotate Y", "RotateSelectionY" );
2034                 create_menu_item_with_mnemonic( menu_in_menu, "Rotate Z", "RotateSelectionZ" );
2035         }
2036         {
2037                 auto menu_in_menu = create_sub_menu_with_mnemonic( menu, "Flip" );
2038                 if ( g_Layout_enableDetachableMenus.m_value ) {
2039                         menu_tearoff( menu_in_menu );
2040                 }
2041                 create_menu_item_with_mnemonic( menu_in_menu, "Flip _X", "MirrorSelectionX" );
2042                 create_menu_item_with_mnemonic( menu_in_menu, "Flip _Y", "MirrorSelectionY" );
2043                 create_menu_item_with_mnemonic( menu_in_menu, "Flip _Z", "MirrorSelectionZ" );
2044         }
2045         menu_separator( menu );
2046         create_menu_item_with_mnemonic( menu, "Arbitrary rotation...", "ArbitraryRotation" );
2047         create_menu_item_with_mnemonic( menu, "Arbitrary scale...", "ArbitraryScale" );
2048
2049         return selection_menu_item;
2050 }
2051
2052 ui::MenuItem create_bsp_menu(){
2053         // BSP menu
2054         auto bsp_menu_item = new_sub_menu_item_with_mnemonic( "_Build" );
2055         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( bsp_menu_item ) ));
2056
2057         if ( g_Layout_enableDetachableMenus.m_value ) {
2058                 menu_tearoff( menu );
2059         }
2060
2061         create_menu_item_with_mnemonic( menu, "Customize...", "BuildMenuCustomize" );
2062
2063         menu_separator( menu );
2064
2065         Build_constructMenu( menu );
2066
2067         g_bsp_menu = menu;
2068
2069         return bsp_menu_item;
2070 }
2071
2072 ui::MenuItem create_grid_menu(){
2073         // Grid menu
2074         auto grid_menu_item = new_sub_menu_item_with_mnemonic( "_Grid" );
2075         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( grid_menu_item ) ));
2076         if ( g_Layout_enableDetachableMenus.m_value ) {
2077                 menu_tearoff( menu );
2078         }
2079
2080         Grid_constructMenu( menu );
2081
2082         return grid_menu_item;
2083 }
2084
2085 ui::MenuItem create_misc_menu(){
2086         // Misc menu
2087         auto misc_menu_item = new_sub_menu_item_with_mnemonic( "M_isc" );
2088         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( misc_menu_item ) ));
2089         if ( g_Layout_enableDetachableMenus.m_value ) {
2090                 menu_tearoff( menu );
2091         }
2092
2093 #if 0
2094         create_menu_item_with_mnemonic( menu, "_Benchmark", FreeCaller<GlobalCamera_Benchmark>() );
2095 #endif
2096     menu.add(create_colours_menu());
2097
2098         create_menu_item_with_mnemonic( menu, "Find brush...", "FindBrush" );
2099         create_menu_item_with_mnemonic( menu, "Map Info...", "MapInfo" );
2100         // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=394
2101 //  create_menu_item_with_mnemonic(menu, "_Print XY View", FreeCaller<WXY_Print>());
2102         create_menu_item_with_mnemonic( menu, "_Background select", FreeCaller<WXY_BackgroundSelect>() );
2103         return misc_menu_item;
2104 }
2105
2106 ui::MenuItem create_entity_menu(){
2107         // Brush menu
2108         auto entity_menu_item = new_sub_menu_item_with_mnemonic( "E_ntity" );
2109         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( entity_menu_item ) ));
2110         if ( g_Layout_enableDetachableMenus.m_value ) {
2111                 menu_tearoff( menu );
2112         }
2113
2114         Entity_constructMenu( menu );
2115
2116         return entity_menu_item;
2117 }
2118
2119 ui::MenuItem create_brush_menu(){
2120         // Brush menu
2121         auto brush_menu_item = new_sub_menu_item_with_mnemonic( "B_rush" );
2122         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( brush_menu_item ) ));
2123         if ( g_Layout_enableDetachableMenus.m_value ) {
2124                 menu_tearoff( menu );
2125         }
2126
2127         Brush_constructMenu( menu );
2128
2129         return brush_menu_item;
2130 }
2131
2132 ui::MenuItem create_patch_menu(){
2133         // Curve menu
2134         auto patch_menu_item = new_sub_menu_item_with_mnemonic( "_Curve" );
2135         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( patch_menu_item ) ));
2136         if ( g_Layout_enableDetachableMenus.m_value ) {
2137                 menu_tearoff( menu );
2138         }
2139
2140         Patch_constructMenu( menu );
2141
2142         return patch_menu_item;
2143 }
2144
2145 ui::MenuItem create_help_menu(){
2146         // Help menu
2147         auto help_menu_item = new_sub_menu_item_with_mnemonic( "_Help" );
2148         auto menu = ui::Menu(GTK_MENU( gtk_menu_item_get_submenu( help_menu_item ) ));
2149         if ( g_Layout_enableDetachableMenus.m_value ) {
2150                 menu_tearoff( menu );
2151         }
2152
2153         create_menu_item_with_mnemonic( menu, "Manual", "OpenManual" );
2154
2155         // this creates all the per-game drop downs for the game pack helps
2156         // it will take care of hooking the Sys_OpenURL calls etc.
2157         create_game_help_menu( menu );
2158
2159         create_menu_item_with_mnemonic( menu, "Bug report", FreeCaller<OpenBugReportURL>() );
2160         create_menu_item_with_mnemonic( menu, "Shortcuts list", FreeCaller<DoCommandListDlg>() );
2161         create_menu_item_with_mnemonic( menu, "_About", FreeCaller<DoAbout>() );
2162
2163         return help_menu_item;
2164 }
2165
2166 GtkMenuBar* create_main_menu( MainFrame::EViewStyle style ){
2167         auto menu_bar = ui::MenuBar(GTK_MENU_BAR( gtk_menu_bar_new() ));
2168         menu_bar.show();
2169
2170         menu_bar.add(create_file_menu());
2171         menu_bar.add(create_edit_menu());
2172         menu_bar.add(create_view_menu(style));
2173         menu_bar.add(create_selection_menu());
2174         menu_bar.add(create_bsp_menu());
2175         menu_bar.add(create_grid_menu());
2176         menu_bar.add(create_misc_menu());
2177         menu_bar.add(create_entity_menu());
2178         menu_bar.add(create_brush_menu());
2179         menu_bar.add(create_patch_menu());
2180         menu_bar.add(create_plugins_menu());
2181         menu_bar.add(create_help_menu());
2182
2183         return menu_bar;
2184 }
2185
2186
2187 void PatchInspector_registerShortcuts(){
2188         command_connect_accelerator( "PatchInspector" );
2189 }
2190
2191 void Patch_registerShortcuts(){
2192         command_connect_accelerator( "InvertCurveTextureX" );
2193         command_connect_accelerator( "InvertCurveTextureY" );
2194         command_connect_accelerator( "PatchInsertInsertColumn" );
2195         command_connect_accelerator( "PatchInsertInsertRow" );
2196         command_connect_accelerator( "PatchDeleteLastColumn" );
2197         command_connect_accelerator( "PatchDeleteLastRow" );
2198         command_connect_accelerator( "NaturalizePatch" );
2199         //command_connect_accelerator("CapCurrentCurve");
2200 }
2201
2202 void Manipulators_registerShortcuts(){
2203         toggle_add_accelerator( "MouseRotate" );
2204         toggle_add_accelerator( "MouseTranslate" );
2205         toggle_add_accelerator( "MouseScale" );
2206         toggle_add_accelerator( "MouseDrag" );
2207         toggle_add_accelerator( "ToggleClipper" );
2208 }
2209
2210 void TexdefNudge_registerShortcuts(){
2211         command_connect_accelerator( "TexRotateClock" );
2212         command_connect_accelerator( "TexRotateCounter" );
2213         command_connect_accelerator( "TexScaleUp" );
2214         command_connect_accelerator( "TexScaleDown" );
2215         command_connect_accelerator( "TexScaleLeft" );
2216         command_connect_accelerator( "TexScaleRight" );
2217         command_connect_accelerator( "TexShiftUp" );
2218         command_connect_accelerator( "TexShiftDown" );
2219         command_connect_accelerator( "TexShiftLeft" );
2220         command_connect_accelerator( "TexShiftRight" );
2221 }
2222
2223 void SelectNudge_registerShortcuts(){
2224         command_connect_accelerator( "MoveSelectionDOWN" );
2225         command_connect_accelerator( "MoveSelectionUP" );
2226         //command_connect_accelerator("SelectNudgeLeft");
2227         //command_connect_accelerator("SelectNudgeRight");
2228         //command_connect_accelerator("SelectNudgeUp");
2229         //command_connect_accelerator("SelectNudgeDown");
2230 }
2231
2232 void SnapToGrid_registerShortcuts(){
2233         command_connect_accelerator( "SnapToGrid" );
2234 }
2235
2236 void SelectByType_registerShortcuts(){
2237         command_connect_accelerator( "SelectAllOfType" );
2238 }
2239
2240 void SurfaceInspector_registerShortcuts(){
2241         command_connect_accelerator( "FitTexture" );
2242 }
2243
2244
2245 void register_shortcuts(){
2246         PatchInspector_registerShortcuts();
2247         Patch_registerShortcuts();
2248         Grid_registerShortcuts();
2249         XYWnd_registerShortcuts();
2250         CamWnd_registerShortcuts();
2251         Manipulators_registerShortcuts();
2252         SurfaceInspector_registerShortcuts();
2253         TexdefNudge_registerShortcuts();
2254         SelectNudge_registerShortcuts();
2255         SnapToGrid_registerShortcuts();
2256         SelectByType_registerShortcuts();
2257 }
2258
2259 void File_constructToolbar( ui::Toolbar toolbar ){
2260         toolbar_append_button( toolbar, "Open an existing map (CTRL + O)", "file_open.png", "OpenMap" );
2261         toolbar_append_button( toolbar, "Save the active map (CTRL + S)", "file_save.png", "SaveMap" );
2262 }
2263
2264 void UndoRedo_constructToolbar( ui::Toolbar toolbar ){
2265         toolbar_append_button( toolbar, "Undo (CTRL + Z)", "undo.png", "Undo" );
2266         toolbar_append_button( toolbar, "Redo (CTRL + Y)", "redo.png", "Redo" );
2267 }
2268
2269 void RotateFlip_constructToolbar( ui::Toolbar toolbar ){
2270         toolbar_append_button( toolbar, "x-axis Flip", "brush_flipx.png", "MirrorSelectionX" );
2271         toolbar_append_button( toolbar, "x-axis Rotate", "brush_rotatex.png", "RotateSelectionX" );
2272         toolbar_append_button( toolbar, "y-axis Flip", "brush_flipy.png", "MirrorSelectionY" );
2273         toolbar_append_button( toolbar, "y-axis Rotate", "brush_rotatey.png", "RotateSelectionY" );
2274         toolbar_append_button( toolbar, "z-axis Flip", "brush_flipz.png", "MirrorSelectionZ" );
2275         toolbar_append_button( toolbar, "z-axis Rotate", "brush_rotatez.png", "RotateSelectionZ" );
2276 }
2277
2278 void Select_constructToolbar( ui::Toolbar toolbar ){
2279         toolbar_append_button( toolbar, "Select touching", "selection_selecttouching.png", "SelectTouching" );
2280         toolbar_append_button( toolbar, "Select inside", "selection_selectinside.png", "SelectInside" );
2281 }
2282
2283 void CSG_constructToolbar( ui::Toolbar toolbar ){
2284         toolbar_append_button( toolbar, "CSG Subtract (SHIFT + U)", "selection_csgsubtract.png", "CSGSubtract" );
2285         toolbar_append_button( toolbar, "CSG Merge (CTRL + U)", "selection_csgmerge.png", "CSGMerge" );
2286         toolbar_append_button( toolbar, "Hollow", "selection_makehollow.png", "CSGHollow" );
2287 }
2288
2289 void ComponentModes_constructToolbar( ui::Toolbar toolbar ){
2290         toolbar_append_toggle_button( toolbar, "Select Vertices (V)", "modify_vertices.png", "DragVertices" );
2291         toolbar_append_toggle_button( toolbar, "Select Edges (E)", "modify_edges.png", "DragEdges" );
2292         toolbar_append_toggle_button( toolbar, "Select Faces (F)", "modify_faces.png", "DragFaces" );
2293 }
2294
2295 void Clipper_constructToolbar( ui::Toolbar toolbar ){
2296
2297         toolbar_append_toggle_button( toolbar, "Clipper (X)", "view_clipper.png", "ToggleClipper" );
2298 }
2299
2300 void XYWnd_constructToolbar( ui::Toolbar toolbar ){
2301         toolbar_append_button( toolbar, "Change views", "view_change.png", "NextView" );
2302 }
2303
2304 void Manipulators_constructToolbar( ui::Toolbar toolbar ){
2305         toolbar_append_toggle_button( toolbar, "Translate (W)", "select_mousetranslate.png", "MouseTranslate" );
2306         toolbar_append_toggle_button( toolbar, "Rotate (R)", "select_mouserotate.png", "MouseRotate" );
2307         toolbar_append_toggle_button( toolbar, "Scale", "select_mousescale.png", "MouseScale" );
2308         toolbar_append_toggle_button( toolbar, "Resize (Q)", "select_mouseresize.png", "MouseDrag" );
2309
2310         Clipper_constructToolbar( toolbar );
2311 }
2312
2313 ui::Toolbar create_main_toolbar( MainFrame::EViewStyle style ){
2314         auto toolbar = ui::Toolbar(GTK_TOOLBAR( gtk_toolbar_new() ));
2315         gtk_orientable_set_orientation( GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL );
2316         gtk_toolbar_set_style( toolbar, GTK_TOOLBAR_ICONS );
2317
2318         toolbar.show();
2319
2320         auto space = [&]() {
2321                 auto btn = ui::Widget(GTK_WIDGET(gtk_separator_tool_item_new()));
2322                 btn.show();
2323                 toolbar.add(btn);
2324         };
2325
2326         File_constructToolbar( toolbar );
2327
2328         space();
2329
2330         UndoRedo_constructToolbar( toolbar );
2331
2332         space();
2333
2334         RotateFlip_constructToolbar( toolbar );
2335
2336         space();
2337
2338         Select_constructToolbar( toolbar );
2339
2340         space();
2341
2342         CSG_constructToolbar( toolbar );
2343
2344         space();
2345
2346         ComponentModes_constructToolbar( toolbar );
2347
2348         if ( style == MainFrame::eRegular || style == MainFrame::eRegularLeft || style == MainFrame::eFloating ) {
2349                 space();
2350
2351                 XYWnd_constructToolbar( toolbar );
2352         }
2353
2354         space();
2355
2356         CamWnd_constructToolbar( toolbar );
2357
2358         space();
2359
2360         Manipulators_constructToolbar( toolbar );
2361
2362         if ( g_Layout_enablePatchToolbar.m_value ) {
2363                 space();
2364
2365                 Patch_constructToolbar( toolbar );
2366         }
2367
2368         space();
2369
2370         toolbar_append_toggle_button( toolbar, "Texture Lock (SHIFT +T)", "texture_lock.png", "TogTexLock" );
2371
2372         space();
2373
2374         /*auto g_view_entities_button =*/ toolbar_append_button( toolbar, "Entities (N)", "entities.png", "ToggleEntityInspector" );
2375         auto g_view_console_button = toolbar_append_button( toolbar, "Console (O)", "console.png", "ToggleConsole" );
2376         auto g_view_textures_button = toolbar_append_button( toolbar, "Texture Browser (T)", "texture_browser.png", "ToggleTextures" );
2377         // TODO: call light inspector
2378         //GtkButton* g_view_lightinspector_button = toolbar_append_button(toolbar, "Light Inspector", "lightinspector.png", "ToggleLightInspector");
2379
2380         space();
2381         /*auto g_refresh_models_button =*/ toolbar_append_button( toolbar, "Refresh Models", "refresh_models.png", "RefreshReferences" );
2382
2383
2384         // disable the console and texture button in the regular layouts
2385         if ( style == MainFrame::eRegular || style == MainFrame::eRegularLeft ) {
2386                 gtk_widget_set_sensitive( GTK_WIDGET( g_view_console_button ), FALSE );
2387                 gtk_widget_set_sensitive( GTK_WIDGET( g_view_textures_button ), FALSE );
2388         }
2389
2390         return toolbar;
2391 }
2392
2393 ui::Widget create_main_statusbar( ui::Widget pStatusLabel[c_count_status] ){
2394         auto table = ui::Table( 1, c_count_status, FALSE );
2395         table.show();
2396
2397         {
2398                 auto label = ui::Label( "Label" );
2399                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2400                 gtk_misc_set_padding( GTK_MISC( label ), 4, 2 );
2401                 label.show();
2402                 gtk_table_attach_defaults( table, GTK_WIDGET( label ), 0, 1, 0, 1 );
2403                 pStatusLabel[c_command_status] = ui::Widget(GTK_WIDGET( label ));
2404         }
2405
2406         for ( int i = 1; i < c_count_status; ++i )
2407         {
2408                 auto frame = ui::Frame();
2409                 frame.show();
2410                 gtk_table_attach_defaults( table, GTK_WIDGET( frame ), i, i + 1, 0, 1 );
2411                 gtk_frame_set_shadow_type( frame, GTK_SHADOW_IN );
2412
2413                 auto label = ui::Label( "Label" );
2414                 gtk_label_set_ellipsize( label, PANGO_ELLIPSIZE_END );
2415                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2416                 gtk_misc_set_padding( GTK_MISC( label ), 4, 2 );
2417                 label.show();
2418                 frame.add(label);
2419                 pStatusLabel[i] = ui::Widget(GTK_WIDGET( label ));
2420         }
2421
2422         return ui::Widget(GTK_WIDGET( table ));
2423 }
2424
2425 #if 0
2426
2427
2428 WidgetFocusPrinter g_mainframeWidgetFocusPrinter( "mainframe" );
2429
2430 class WindowFocusPrinter
2431 {
2432 const char* m_name;
2433
2434 static gboolean frame_event( ui::Widget widget, GdkEvent* event, WindowFocusPrinter* self ){
2435         globalOutputStream() << self->m_name << " frame_event\n";
2436         return FALSE;
2437 }
2438 static gboolean keys_changed( ui::Widget widget, WindowFocusPrinter* self ){
2439         globalOutputStream() << self->m_name << " keys_changed\n";
2440         return FALSE;
2441 }
2442 static gboolean notify( ui::Window window, gpointer dummy, WindowFocusPrinter* self ){
2443         if ( gtk_window_is_active( window ) ) {
2444                 globalOutputStream() << self->m_name << " takes toplevel focus\n";
2445         }
2446         else
2447         {
2448                 globalOutputStream() << self->m_name << " loses toplevel focus\n";
2449         }
2450         return FALSE;
2451 }
2452 public:
2453 WindowFocusPrinter( const char* name ) : m_name( name ){
2454 }
2455 void connect( ui::Window toplevel_window ){
2456         toplevel_window.connect( "notify::has_toplevel_focus", G_CALLBACK( notify ), this );
2457         toplevel_window.connect( "notify::is_active", G_CALLBACK( notify ), this );
2458         toplevel_window.connect( "keys_changed", G_CALLBACK( keys_changed ), this );
2459         toplevel_window.connect( "frame_event", G_CALLBACK( frame_event ), this );
2460 }
2461 };
2462
2463 WindowFocusPrinter g_mainframeFocusPrinter( "mainframe" );
2464
2465 #endif
2466
2467 class MainWindowActive
2468 {
2469 static gboolean notify( ui::Window window, gpointer dummy, MainWindowActive* self ){
2470         if ( g_wait.m_window && gtk_window_is_active( window ) && !gtk_widget_get_visible( g_wait.m_window ) ) {
2471                 g_wait.m_window.show();
2472         }
2473
2474         return FALSE;
2475 }
2476 public:
2477 void connect( ui::Window toplevel_window ){
2478         toplevel_window.connect( "notify::is-active", G_CALLBACK( notify ), this );
2479 }
2480 };
2481
2482 MainWindowActive g_MainWindowActive;
2483
2484 SignalHandlerId XYWindowDestroyed_connect( const SignalHandler& handler ){
2485         return g_pParentWnd->GetXYWnd()->onDestroyed.connectFirst( handler );
2486 }
2487
2488 void XYWindowDestroyed_disconnect( SignalHandlerId id ){
2489         g_pParentWnd->GetXYWnd()->onDestroyed.disconnect( id );
2490 }
2491
2492 MouseEventHandlerId XYWindowMouseDown_connect( const MouseEventHandler& handler ){
2493         return g_pParentWnd->GetXYWnd()->onMouseDown.connectFirst( handler );
2494 }
2495
2496 void XYWindowMouseDown_disconnect( MouseEventHandlerId id ){
2497         g_pParentWnd->GetXYWnd()->onMouseDown.disconnect( id );
2498 }
2499
2500 // =============================================================================
2501 // MainFrame class
2502
2503 MainFrame* g_pParentWnd = 0;
2504
2505 ui::Window MainFrame_getWindow()
2506 {
2507         return g_pParentWnd ? g_pParentWnd->m_window : ui::Window{ui::null};
2508 }
2509
2510 std::vector<ui::Widget> g_floating_windows;
2511
2512 MainFrame::MainFrame() : m_window( 0 ), m_idleRedrawStatusText( RedrawStatusTextCaller( *this ) ){
2513         m_pXYWnd = 0;
2514         m_pCamWnd = 0;
2515         m_pZWnd = 0;
2516         m_pYZWnd = 0;
2517         m_pXZWnd = 0;
2518         m_pActiveXY = 0;
2519
2520         for ( int n = 0; n < c_count_status; n++ )
2521         {
2522                 m_pStatusLabel[n] = ui::root;
2523         }
2524
2525         m_bSleeping = false;
2526
2527         Create();
2528 }
2529
2530 MainFrame::~MainFrame(){
2531         SaveWindowInfo();
2532
2533         gtk_widget_hide( GTK_WIDGET( m_window ) );
2534
2535         Shutdown();
2536
2537         for ( std::vector<ui::Widget>::iterator i = g_floating_windows.begin(); i != g_floating_windows.end(); ++i )
2538         {
2539                 gtk_widget_destroy( *i );
2540         }
2541
2542         gtk_widget_destroy( GTK_WIDGET( m_window ) );
2543 }
2544
2545 void MainFrame::SetActiveXY( XYWnd* p ){
2546         if ( m_pActiveXY ) {
2547                 m_pActiveXY->SetActive( false );
2548         }
2549
2550         m_pActiveXY = p;
2551
2552         if ( m_pActiveXY ) {
2553                 m_pActiveXY->SetActive( true );
2554         }
2555
2556 }
2557
2558 void MainFrame::ReleaseContexts(){
2559 #if 0
2560         if ( m_pXYWnd ) {
2561                 m_pXYWnd->DestroyContext();
2562         }
2563         if ( m_pYZWnd ) {
2564                 m_pYZWnd->DestroyContext();
2565         }
2566         if ( m_pXZWnd ) {
2567                 m_pXZWnd->DestroyContext();
2568         }
2569         if ( m_pCamWnd ) {
2570                 m_pCamWnd->DestroyContext();
2571         }
2572         if ( m_pTexWnd ) {
2573                 m_pTexWnd->DestroyContext();
2574         }
2575         if ( m_pZWnd ) {
2576                 m_pZWnd->DestroyContext();
2577         }
2578 #endif
2579 }
2580
2581 void MainFrame::CreateContexts(){
2582 #if 0
2583         if ( m_pCamWnd ) {
2584                 m_pCamWnd->CreateContext();
2585         }
2586         if ( m_pXYWnd ) {
2587                 m_pXYWnd->CreateContext();
2588         }
2589         if ( m_pYZWnd ) {
2590                 m_pYZWnd->CreateContext();
2591         }
2592         if ( m_pXZWnd ) {
2593                 m_pXZWnd->CreateContext();
2594         }
2595         if ( m_pTexWnd ) {
2596                 m_pTexWnd->CreateContext();
2597         }
2598         if ( m_pZWnd ) {
2599                 m_pZWnd->CreateContext();
2600         }
2601 #endif
2602 }
2603
2604 #ifdef _DEBUG
2605 //#define DBG_SLEEP
2606 #endif
2607
2608 void MainFrame::OnSleep(){
2609 #if 0
2610         m_bSleeping ^= 1;
2611         if ( m_bSleeping ) {
2612                 // useful when trying to debug crashes in the sleep code
2613                 globalOutputStream() << "Going into sleep mode..\n";
2614
2615                 globalOutputStream() << "Dispatching sleep msg...";
2616                 DispatchRadiantMsg( RADIANT_SLEEP );
2617                 globalOutputStream() << "Done.\n";
2618
2619                 gtk_window_iconify( m_window );
2620                 GlobalSelectionSystem().setSelectedAll( false );
2621
2622                 GlobalShaderCache().unrealise();
2623                 Shaders_Free();
2624                 GlobalOpenGL_debugAssertNoErrors();
2625                 ScreenUpdates_Disable();
2626
2627                 // release contexts
2628                 globalOutputStream() << "Releasing contexts...";
2629                 ReleaseContexts();
2630                 globalOutputStream() << "Done.\n";
2631         }
2632         else
2633         {
2634                 globalOutputStream() << "Waking up\n";
2635
2636                 gtk_window_deiconify( m_window );
2637
2638                 // create contexts
2639                 globalOutputStream() << "Creating contexts...";
2640                 CreateContexts();
2641                 globalOutputStream() << "Done.\n";
2642
2643                 globalOutputStream() << "Making current on camera...";
2644                 m_pCamWnd->MakeCurrent();
2645                 globalOutputStream() << "Done.\n";
2646
2647                 globalOutputStream() << "Reloading shaders...";
2648                 Shaders_Load();
2649                 GlobalShaderCache().realise();
2650                 globalOutputStream() << "Done.\n";
2651
2652                 ScreenUpdates_Enable();
2653
2654                 globalOutputStream() << "Dispatching wake msg...";
2655                 DispatchRadiantMsg( RADIANT_WAKEUP );
2656                 globalOutputStream() << "Done\n";
2657         }
2658 #endif
2659 }
2660
2661
2662 ui::Window create_splash(){
2663         ui::Window window = ui::Window( ui::window_type::TOP );
2664         gtk_window_set_decorated( window, FALSE );
2665         gtk_window_set_resizable( window, FALSE );
2666         gtk_window_set_modal( window, TRUE );
2667         gtk_window_set_default_size( window, -1, -1 );
2668         gtk_window_set_position( window, GTK_WIN_POS_CENTER );
2669         gtk_container_set_border_width( GTK_CONTAINER( window ), 0 );
2670
2671         auto image = new_local_image( "splash.png" );
2672         image.show();
2673         window.add(image);
2674
2675         gtk_widget_set_size_request( GTK_WIDGET( window ), -1, -1 );
2676         window.show();
2677
2678         return window;
2679 }
2680
2681 static ui::Window splash_screen{ui::null};
2682
2683 void show_splash(){
2684         splash_screen = create_splash();
2685
2686         ui::process();
2687 }
2688
2689 void hide_splash(){
2690         gtk_widget_destroy( GTK_WIDGET( splash_screen ) );
2691 }
2692
2693 WindowPositionTracker g_posCamWnd;
2694 WindowPositionTracker g_posXYWnd;
2695 WindowPositionTracker g_posXZWnd;
2696 WindowPositionTracker g_posYZWnd;
2697
2698 static gint mainframe_delete( ui::Widget widget, GdkEvent *event, gpointer data ){
2699         if ( ConfirmModified( "Exit Radiant" ) ) {
2700                 gtk_main_quit();
2701         }
2702
2703         return TRUE;
2704 }
2705
2706 void MainFrame::Create(){
2707         ui::Window window = ui::Window( ui::window_type::TOP );
2708
2709         GlobalWindowObservers_connectTopLevel( window );
2710
2711         gtk_window_set_transient_for( splash_screen, window );
2712
2713 #if !defined( WIN32 )
2714         {
2715                 GdkPixbuf* pixbuf = pixbuf_new_from_file_with_mask( "bitmaps/icon.png" );
2716                 if ( pixbuf != 0 ) {
2717                         gtk_window_set_icon( window, pixbuf );
2718                         g_object_unref( pixbuf );
2719                 }
2720         }
2721 #endif
2722
2723         gtk_widget_add_events( GTK_WIDGET( window ), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK );
2724         window.connect( "delete_event", G_CALLBACK( mainframe_delete ), this );
2725
2726         m_position_tracker.connect( window );
2727
2728 #if 0
2729         g_mainframeWidgetFocusPrinter.connect( window );
2730         g_mainframeFocusPrinter.connect( window );
2731 #endif
2732
2733         g_MainWindowActive.connect( window );
2734
2735         GetPlugInMgr().Init( window );
2736
2737         ui::Widget vbox = ui::VBox( FALSE, 0 );
2738         window.add(vbox);
2739         vbox.show();
2740
2741         global_accel_connect_window( window );
2742
2743         m_nCurrentStyle = (EViewStyle)g_Layout_viewStyle.m_value;
2744
2745         register_shortcuts();
2746
2747         GtkMenuBar* main_menu = create_main_menu( CurrentStyle() );
2748         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( main_menu ), FALSE, FALSE, 0 );
2749
2750         GtkToolbar* main_toolbar = create_main_toolbar( CurrentStyle() );
2751         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( main_toolbar ), FALSE, FALSE, 0 );
2752
2753         GtkToolbar* plugin_toolbar = create_plugin_toolbar();
2754         if ( !g_Layout_enablePluginToolbar.m_value ) {
2755                 gtk_widget_hide( GTK_WIDGET( plugin_toolbar ) );
2756         }
2757         gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( plugin_toolbar ), FALSE, FALSE, 0 );
2758
2759         ui::Widget main_statusbar = create_main_statusbar( m_pStatusLabel );
2760         gtk_box_pack_end( GTK_BOX( vbox ), main_statusbar, FALSE, TRUE, 2 );
2761
2762         GroupDialog_constructWindow( window );
2763         g_page_entity = GroupDialog_addPage( "Entities", EntityInspector_constructWindow( GroupDialog_getWindow() ), RawStringExportCaller( "Entities" ) );
2764
2765         if ( FloatingGroupDialog() ) {
2766                 g_page_console = GroupDialog_addPage( "Console", Console_constructWindow( GroupDialog_getWindow() ), RawStringExportCaller( "Console" ) );
2767         }
2768
2769 #ifdef WIN32
2770         if ( g_multimon_globals.m_bStartOnPrimMon ) {
2771                 PositionWindowOnPrimaryScreen( g_layout_globals.m_position );
2772                 window_set_position( window, g_layout_globals.m_position );
2773         }
2774         else
2775 #endif
2776         if ( g_layout_globals.nState & GDK_WINDOW_STATE_MAXIMIZED ) {
2777                 gtk_window_maximize( window );
2778                 WindowPosition default_position( -1, -1, 640, 480 );
2779                 window_set_position( window, default_position );
2780         }
2781         else
2782         {
2783                 window_set_position( window, g_layout_globals.m_position );
2784         }
2785
2786         m_window = window;
2787
2788         window.show();
2789
2790         if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft ) {
2791                 {
2792                         ui::Widget vsplit = ui::VPaned();
2793                         m_vSplit = vsplit;
2794                         gtk_box_pack_start( GTK_BOX( vbox ), vsplit, TRUE, TRUE, 0 );
2795                         vsplit.show();
2796
2797                         // console
2798                         ui::Widget console_window = Console_constructWindow( window );
2799                         gtk_paned_pack2( GTK_PANED( vsplit ), console_window, FALSE, TRUE );
2800
2801                         {
2802                                 ui::Widget hsplit = ui::HPaned();
2803                                 hsplit.show();
2804                                 m_hSplit = hsplit;
2805                                 gtk_paned_add1( GTK_PANED( vsplit ), hsplit );
2806
2807                                 // xy
2808                                 m_pXYWnd = new XYWnd();
2809                                 m_pXYWnd->SetViewType( XY );
2810                                 ui::Widget xy_window = ui::Widget(GTK_WIDGET( create_framed_widget( m_pXYWnd->GetWidget() ) ));
2811
2812                                 {
2813                                         ui::Widget vsplit2 = ui::VPaned();
2814                                         vsplit2.show();
2815                                         m_vSplit2 = vsplit2;
2816
2817                                         if ( CurrentStyle() == eRegular ) {
2818                                                 gtk_paned_add1( GTK_PANED( hsplit ), xy_window );
2819                                                 gtk_paned_add2( GTK_PANED( hsplit ), vsplit2 );
2820                                         }
2821                                         else
2822                                         {
2823                                                 gtk_paned_add1( GTK_PANED( hsplit ), vsplit2 );
2824                                                 gtk_paned_add2( GTK_PANED( hsplit ), xy_window );
2825                                         }
2826
2827
2828                                         // camera
2829                                         m_pCamWnd = NewCamWnd();
2830                                         GlobalCamera_setCamWnd( *m_pCamWnd );
2831                                         CamWnd_setParent( *m_pCamWnd, window );
2832                                         GtkFrame* camera_window = create_framed_widget( CamWnd_getWidget( *m_pCamWnd ) );
2833
2834                                         gtk_paned_add1( GTK_PANED( vsplit2 ), GTK_WIDGET( camera_window ) );
2835
2836                                         // textures
2837                                         GtkFrame* texture_window = create_framed_widget( TextureBrowser_constructWindow( window ) );
2838
2839                                         gtk_paned_add2( GTK_PANED( vsplit2 ), GTK_WIDGET( texture_window ) );
2840                                 }
2841                         }
2842                 }
2843
2844                 gtk_paned_set_position( GTK_PANED( m_vSplit ), g_layout_globals.nXYHeight );
2845
2846                 if ( CurrentStyle() == eRegular ) {
2847                         gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nXYWidth );
2848                 }
2849                 else
2850                 {
2851                         gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nCamWidth );
2852                 }
2853
2854                 gtk_paned_set_position( GTK_PANED( m_vSplit2 ), g_layout_globals.nCamHeight );
2855         }
2856         else if ( CurrentStyle() == eFloating ) {
2857                 {
2858                         ui::Window window = ui::Window(create_persistent_floating_window( "Camera", m_window ));
2859                         global_accel_connect_window( window );
2860                         g_posCamWnd.connect( window );
2861
2862                         window.show();
2863
2864                         m_pCamWnd = NewCamWnd();
2865                         GlobalCamera_setCamWnd( *m_pCamWnd );
2866
2867                         {
2868                                 auto frame = create_framed_widget( CamWnd_getWidget( *m_pCamWnd ) );
2869                                 window.add(frame);
2870                         }
2871                         CamWnd_setParent( *m_pCamWnd, window );
2872
2873                         g_floating_windows.push_back( window );
2874                 }
2875
2876                 {
2877                         ui::Window window = ui::Window(create_persistent_floating_window( ViewType_getTitle( XY ), m_window ));
2878                         global_accel_connect_window( window );
2879                         g_posXYWnd.connect( window );
2880
2881                         m_pXYWnd = new XYWnd();
2882                         m_pXYWnd->m_parent = window;
2883                         m_pXYWnd->SetViewType( XY );
2884
2885
2886                         {
2887                                 auto frame = create_framed_widget( m_pXYWnd->GetWidget() );
2888                                 window.add(frame);
2889                         }
2890                         XY_Top_Shown_Construct( window );
2891
2892                         g_floating_windows.push_back( window );
2893                 }
2894
2895                 {
2896                         ui::Window window = ui::Window(create_persistent_floating_window( ViewType_getTitle( XZ ), m_window ));
2897                         global_accel_connect_window( window );
2898                         g_posXZWnd.connect( window );
2899
2900                         m_pXZWnd = new XYWnd();
2901                         m_pXZWnd->m_parent = window;
2902                         m_pXZWnd->SetViewType( XZ );
2903
2904                         {
2905                                 auto frame = create_framed_widget( m_pXZWnd->GetWidget() );
2906                                 window.add(frame);
2907                         }
2908
2909                         XZ_Front_Shown_Construct( window );
2910
2911                         g_floating_windows.push_back( window );
2912                 }
2913
2914                 {
2915                         ui::Window window = ui::Window(create_persistent_floating_window( ViewType_getTitle( YZ ), m_window ));
2916                         global_accel_connect_window( window );
2917                         g_posYZWnd.connect( window );
2918
2919                         m_pYZWnd = new XYWnd();
2920                         m_pYZWnd->m_parent = window;
2921                         m_pYZWnd->SetViewType( YZ );
2922
2923                         {
2924                                 auto frame = create_framed_widget( m_pYZWnd->GetWidget() );
2925                                 window.add(frame);
2926                         }
2927
2928                         YZ_Side_Shown_Construct( window );
2929
2930                         g_floating_windows.push_back( window );
2931                 }
2932
2933                 {
2934                         GtkFrame* frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
2935                         g_page_textures = GroupDialog_addPage( "Textures", ui::Widget(GTK_WIDGET( frame )), TextureBrowserExportTitleCaller() );
2936                 }
2937
2938                 GroupDialog_show();
2939         }
2940         else // 4 way
2941         {
2942                 m_pCamWnd = NewCamWnd();
2943                 GlobalCamera_setCamWnd( *m_pCamWnd );
2944                 CamWnd_setParent( *m_pCamWnd, window );
2945
2946                 ui::Widget camera = CamWnd_getWidget( *m_pCamWnd );
2947
2948                 m_pYZWnd = new XYWnd();
2949                 m_pYZWnd->SetViewType( YZ );
2950
2951                 ui::Widget yz = m_pYZWnd->GetWidget();
2952
2953                 m_pXYWnd = new XYWnd();
2954                 m_pXYWnd->SetViewType( XY );
2955
2956                 ui::Widget xy = m_pXYWnd->GetWidget();
2957
2958                 m_pXZWnd = new XYWnd();
2959                 m_pXZWnd->SetViewType( XZ );
2960
2961                 ui::Widget xz = m_pXZWnd->GetWidget();
2962
2963                 GtkHPaned* split = create_split_views( camera, yz, xy, xz );
2964                 gtk_box_pack_start( GTK_BOX( vbox ), GTK_WIDGET( split ), TRUE, TRUE, 0 );
2965
2966                 {
2967                         GtkFrame* frame = create_framed_widget( TextureBrowser_constructWindow( window ) );
2968                         g_page_textures = GroupDialog_addPage( "Textures", ui::Widget(GTK_WIDGET( frame )), TextureBrowserExportTitleCaller() );
2969                 }
2970         }
2971
2972         EntityList_constructWindow( window );
2973         PreferencesDialog_constructWindow( window );
2974         FindTextureDialog_constructWindow( window );
2975         SurfaceInspector_constructWindow( window );
2976         PatchInspector_constructWindow( window );
2977
2978         SetActiveXY( m_pXYWnd );
2979
2980         AddGridChangeCallback( SetGridStatusCaller( *this ) );
2981         AddGridChangeCallback( ReferenceCaller<MainFrame, XY_UpdateAllWindows>( *this ) );
2982
2983         g_defaultToolMode = DragMode;
2984         g_defaultToolMode();
2985         SetStatusText( m_command_status, c_TranslateMode_status );
2986
2987         EverySecondTimer_enable();
2988
2989         //GlobalShortcuts_reportUnregistered();
2990 }
2991
2992 void MainFrame::SaveWindowInfo(){
2993         if ( !FloatingGroupDialog() ) {
2994                 g_layout_globals.nXYHeight = gtk_paned_get_position( GTK_PANED( m_vSplit ) );
2995
2996                 if ( CurrentStyle() != eRegular ) {
2997                         g_layout_globals.nCamWidth = gtk_paned_get_position( GTK_PANED( m_hSplit ) );
2998                 }
2999                 else
3000                 {
3001                         g_layout_globals.nXYWidth = gtk_paned_get_position( GTK_PANED( m_hSplit ) );
3002                 }
3003
3004                 g_layout_globals.nCamHeight = gtk_paned_get_position( GTK_PANED( m_vSplit2 ) );
3005         }
3006
3007         g_layout_globals.m_position = m_position_tracker.getPosition();
3008
3009         g_layout_globals.nState = gdk_window_get_state( gtk_widget_get_window(GTK_WIDGET( m_window )) );
3010 }
3011
3012 void MainFrame::Shutdown(){
3013         EverySecondTimer_disable();
3014
3015         EntityList_destroyWindow();
3016
3017         delete m_pXYWnd;
3018         m_pXYWnd = 0;
3019         delete m_pYZWnd;
3020         m_pYZWnd = 0;
3021         delete m_pXZWnd;
3022         m_pXZWnd = 0;
3023
3024         TextureBrowser_destroyWindow();
3025
3026         DeleteCamWnd( m_pCamWnd );
3027         m_pCamWnd = 0;
3028
3029         PreferencesDialog_destroyWindow();
3030         SurfaceInspector_destroyWindow();
3031         FindTextureDialog_destroyWindow();
3032         PatchInspector_destroyWindow();
3033
3034         g_DbgDlg.destroyWindow();
3035
3036         // destroying group-dialog last because it may contain texture-browser
3037         GroupDialog_destroyWindow();
3038 }
3039
3040 void MainFrame::RedrawStatusText(){
3041         gtk_label_set_text( GTK_LABEL( m_pStatusLabel[c_command_status] ), m_command_status.c_str() );
3042         gtk_label_set_text( GTK_LABEL( m_pStatusLabel[c_position_status] ), m_position_status.c_str() );
3043         gtk_label_set_text( GTK_LABEL( m_pStatusLabel[c_brushcount_status] ), m_brushcount_status.c_str() );
3044         gtk_label_set_text( GTK_LABEL( m_pStatusLabel[c_texture_status] ), m_texture_status.c_str() );
3045         gtk_label_set_text( GTK_LABEL( m_pStatusLabel[c_grid_status] ), m_grid_status.c_str() );
3046 }
3047
3048 void MainFrame::UpdateStatusText(){
3049         m_idleRedrawStatusText.queueDraw();
3050 }
3051
3052 void MainFrame::SetStatusText( CopiedString& status_text, const char* pText ){
3053         status_text = pText;
3054         UpdateStatusText();
3055 }
3056
3057 void Sys_Status( const char* status ){
3058         if ( g_pParentWnd != 0 ) {
3059                 g_pParentWnd->SetStatusText( g_pParentWnd->m_command_status, status );
3060         }
3061 }
3062
3063 int getRotateIncrement(){
3064         return static_cast<int>( g_si_globals.rotate );
3065 }
3066
3067 int getFarClipDistance(){
3068         return g_camwindow_globals.m_nCubicScale;
3069 }
3070
3071 float ( *GridStatus_getGridSize )() = GetGridSize;
3072 int ( *GridStatus_getRotateIncrement )() = getRotateIncrement;
3073 int ( *GridStatus_getFarClipDistance )() = getFarClipDistance;
3074 bool ( *GridStatus_getTextureLockEnabled )();
3075
3076 void MainFrame::SetGridStatus(){
3077         StringOutputStream status( 64 );
3078         const char* lock = ( GridStatus_getTextureLockEnabled() ) ? "ON" : "OFF";
3079         status << ( GetSnapGridSize() > 0 ? "G:" : "g:" ) << GridStatus_getGridSize()
3080                    << "  R:" << GridStatus_getRotateIncrement()
3081                    << "  C:" << GridStatus_getFarClipDistance()
3082                    << "  L:" << lock;
3083         SetStatusText( m_grid_status, status.c_str() );
3084 }
3085
3086 void GridStatus_onTextureLockEnabledChanged(){
3087         if ( g_pParentWnd != 0 ) {
3088                 g_pParentWnd->SetGridStatus();
3089         }
3090 }
3091
3092 void GlobalGL_sharedContextCreated(){
3093         GLFont *g_font = NULL;
3094
3095         // report OpenGL information
3096         globalOutputStream() << "GL_VENDOR: " << reinterpret_cast<const char*>( glGetString( GL_VENDOR ) ) << "\n";
3097         globalOutputStream() << "GL_RENDERER: " << reinterpret_cast<const char*>( glGetString( GL_RENDERER ) ) << "\n";
3098         globalOutputStream() << "GL_VERSION: " << reinterpret_cast<const char*>( glGetString( GL_VERSION ) ) << "\n";
3099     const auto extensions = reinterpret_cast<const char*>( glGetString(GL_EXTENSIONS ) );
3100     globalOutputStream() << "GL_EXTENSIONS: " << (extensions ? extensions : "") << "\n";
3101
3102         QGL_sharedContextCreated( GlobalOpenGL() );
3103
3104         ShaderCache_extensionsInitialised();
3105
3106         GlobalShaderCache().realise();
3107         Textures_Realise();
3108
3109 #ifdef WIN32
3110         /* win32 is dodgy here, just use courier new then */
3111         g_font = glfont_create( "arial 9" );
3112 #else
3113         GtkSettings *settings = gtk_settings_get_default();
3114         gchar *fontname;
3115         g_object_get( settings, "gtk-font-name", &fontname, NULL );
3116         g_font = glfont_create( fontname );
3117 #endif
3118
3119         GlobalOpenGL().m_font = g_font;
3120 }
3121
3122 void GlobalGL_sharedContextDestroyed(){
3123         Textures_Unrealise();
3124         GlobalShaderCache().unrealise();
3125
3126         QGL_sharedContextDestroyed( GlobalOpenGL() );
3127 }
3128
3129
3130 void Layout_constructPreferences( PreferencesPage& page ){
3131         {
3132                 const char* layouts[] = { "window1.png", "window2.png", "window3.png", "window4.png" };
3133                 page.appendRadioIcons(
3134                         "Window Layout",
3135                         STRING_ARRAY_RANGE( layouts ),
3136                         LatchedIntImportCaller( g_Layout_viewStyle ),
3137                         IntExportCaller( g_Layout_viewStyle.m_latched )
3138                         );
3139         }
3140         page.appendCheckBox(
3141                 "", "Detachable Menus",
3142                 LatchedBoolImportCaller( g_Layout_enableDetachableMenus ),
3143                 BoolExportCaller( g_Layout_enableDetachableMenus.m_latched )
3144                 );
3145         if ( !string_empty( g_pGameDescription->getKeyValue( "no_patch" ) ) ) {
3146                 page.appendCheckBox(
3147                         "", "Patch Toolbar",
3148                         LatchedBoolImportCaller( g_Layout_enablePatchToolbar ),
3149                         BoolExportCaller( g_Layout_enablePatchToolbar.m_latched )
3150                         );
3151         }
3152         page.appendCheckBox(
3153                 "", "Plugin Toolbar",
3154                 LatchedBoolImportCaller( g_Layout_enablePluginToolbar ),
3155                 BoolExportCaller( g_Layout_enablePluginToolbar.m_latched )
3156                 );
3157 }
3158
3159 void Layout_constructPage( PreferenceGroup& group ){
3160         PreferencesPage page( group.createPage( "Layout", "Layout Preferences" ) );
3161         Layout_constructPreferences( page );
3162 }
3163
3164 void Layout_registerPreferencesPage(){
3165         PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Layout_constructPage>() );
3166 }
3167
3168
3169 #include "preferencesystem.h"
3170 #include "stringio.h"
3171
3172 void MainFrame_Construct(){
3173         GlobalCommands_insert( "OpenManual", FreeCaller<OpenHelpURL>(), Accelerator( GDK_KEY_F1 ) );
3174
3175         GlobalCommands_insert( "Sleep", FreeCaller<thunk_OnSleep>(), Accelerator( 'P', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
3176         GlobalCommands_insert( "NewMap", FreeCaller<NewMap>() );
3177         GlobalCommands_insert( "OpenMap", FreeCaller<OpenMap>(), Accelerator( 'O', (GdkModifierType)GDK_CONTROL_MASK ) );
3178         GlobalCommands_insert( "ImportMap", FreeCaller<ImportMap>() );
3179         GlobalCommands_insert( "SaveMap", FreeCaller<SaveMap>(), Accelerator( 'S', (GdkModifierType)GDK_CONTROL_MASK ) );
3180         GlobalCommands_insert( "SaveMapAs", FreeCaller<SaveMapAs>() );
3181         GlobalCommands_insert( "ExportSelected", FreeCaller<ExportMap>() );
3182         GlobalCommands_insert( "SaveRegion", FreeCaller<SaveRegion>() );
3183         GlobalCommands_insert( "RefreshReferences", FreeCaller<RefreshReferences>() );
3184         GlobalCommands_insert( "ProjectSettings", FreeCaller<DoProjectSettings>() );
3185         GlobalCommands_insert( "Exit", FreeCaller<Exit>() );
3186
3187         GlobalCommands_insert( "Undo", FreeCaller<Undo>(), Accelerator( 'Z', (GdkModifierType)GDK_CONTROL_MASK ) );
3188         GlobalCommands_insert( "Redo", FreeCaller<Redo>(), Accelerator( 'Y', (GdkModifierType)GDK_CONTROL_MASK ) );
3189         GlobalCommands_insert( "Copy", FreeCaller<Copy>(), Accelerator( 'C', (GdkModifierType)GDK_CONTROL_MASK ) );
3190         GlobalCommands_insert( "Paste", FreeCaller<Paste>(), Accelerator( 'V', (GdkModifierType)GDK_CONTROL_MASK ) );
3191         GlobalCommands_insert( "PasteToCamera", FreeCaller<PasteToCamera>(), Accelerator( 'V', (GdkModifierType)GDK_MOD1_MASK ) );
3192         GlobalCommands_insert( "CloneSelection", FreeCaller<Selection_Clone>(), Accelerator( GDK_KEY_space ) );
3193         GlobalCommands_insert( "CloneSelectionAndMakeUnique", FreeCaller<Selection_Clone_MakeUnique>(), Accelerator( GDK_KEY_space, (GdkModifierType)GDK_SHIFT_MASK ) );
3194         GlobalCommands_insert( "DeleteSelection", FreeCaller<deleteSelection>(), Accelerator( GDK_KEY_BackSpace ) );
3195         GlobalCommands_insert( "ParentSelection", FreeCaller<Scene_parentSelected>() );
3196         GlobalCommands_insert( "UnSelectSelection", FreeCaller<Selection_Deselect>(), Accelerator( GDK_KEY_Escape ) );
3197         GlobalCommands_insert( "InvertSelection", FreeCaller<Select_Invert>(), Accelerator( 'I' ) );
3198         GlobalCommands_insert( "SelectInside", FreeCaller<Select_Inside>() );
3199         GlobalCommands_insert( "SelectTouching", FreeCaller<Select_Touching>() );
3200         GlobalCommands_insert( "ExpandSelectionToEntities", FreeCaller<Scene_ExpandSelectionToEntities>(), Accelerator( 'E', (GdkModifierType)( GDK_MOD1_MASK | GDK_CONTROL_MASK ) ) );
3201         GlobalCommands_insert( "Preferences", FreeCaller<PreferencesDialog_showDialog>(), Accelerator( 'P' ) );
3202
3203         GlobalCommands_insert( "ToggleConsole", FreeCaller<Console_ToggleShow>(), Accelerator( 'O' ) );
3204         GlobalCommands_insert( "ToggleEntityInspector", FreeCaller<EntityInspector_ToggleShow>(), Accelerator( 'N' ) );
3205         GlobalCommands_insert( "EntityList", FreeCaller<EntityList_toggleShown>(), Accelerator( 'L' ) );
3206
3207         GlobalCommands_insert( "ShowHidden", FreeCaller<Select_ShowAllHidden>(), Accelerator( 'H', (GdkModifierType)GDK_SHIFT_MASK ) );
3208         GlobalCommands_insert( "HideSelected", FreeCaller<HideSelected>(), Accelerator( 'H' ) );
3209
3210         GlobalToggles_insert( "DragVertices", FreeCaller<SelectVertexMode>(), ToggleItem::AddCallbackCaller( g_vertexMode_button ), Accelerator( 'V' ) );
3211         GlobalToggles_insert( "DragEdges", FreeCaller<SelectEdgeMode>(), ToggleItem::AddCallbackCaller( g_edgeMode_button ), Accelerator( 'E' ) );
3212         GlobalToggles_insert( "DragFaces", FreeCaller<SelectFaceMode>(), ToggleItem::AddCallbackCaller( g_faceMode_button ), Accelerator( 'F' ) );
3213
3214         GlobalCommands_insert( "MirrorSelectionX", FreeCaller<Selection_Flipx>() );
3215         GlobalCommands_insert( "RotateSelectionX", FreeCaller<Selection_Rotatex>() );
3216         GlobalCommands_insert( "MirrorSelectionY", FreeCaller<Selection_Flipy>() );
3217         GlobalCommands_insert( "RotateSelectionY", FreeCaller<Selection_Rotatey>() );
3218         GlobalCommands_insert( "MirrorSelectionZ", FreeCaller<Selection_Flipz>() );
3219         GlobalCommands_insert( "RotateSelectionZ", FreeCaller<Selection_Rotatez>() );
3220
3221         GlobalCommands_insert( "ArbitraryRotation", FreeCaller<DoRotateDlg>() );
3222         GlobalCommands_insert( "ArbitraryScale", FreeCaller<DoScaleDlg>() );
3223
3224         GlobalCommands_insert( "BuildMenuCustomize", FreeCaller<DoBuildMenu>() );
3225
3226         GlobalCommands_insert( "FindBrush", FreeCaller<DoFind>() );
3227
3228         GlobalCommands_insert( "MapInfo", FreeCaller<DoMapInfo>(), Accelerator( 'M' ) );
3229
3230
3231         GlobalToggles_insert( "ToggleClipper", FreeCaller<ClipperMode>(), ToggleItem::AddCallbackCaller( g_clipper_button ), Accelerator( 'X' ) );
3232
3233         GlobalToggles_insert( "MouseTranslate", FreeCaller<TranslateMode>(), ToggleItem::AddCallbackCaller( g_translatemode_button ), Accelerator( 'W' ) );
3234         GlobalToggles_insert( "MouseRotate", FreeCaller<RotateMode>(), ToggleItem::AddCallbackCaller( g_rotatemode_button ), Accelerator( 'R' ) );
3235         GlobalToggles_insert( "MouseScale", FreeCaller<ScaleMode>(), ToggleItem::AddCallbackCaller( g_scalemode_button ) );
3236         GlobalToggles_insert( "MouseDrag", FreeCaller<DragMode>(), ToggleItem::AddCallbackCaller( g_dragmode_button ), Accelerator( 'Q' ) );
3237
3238         GlobalCommands_insert( "ColorSchemeOriginal", FreeCaller<ColorScheme_Original>() );
3239         GlobalCommands_insert( "ColorSchemeQER", FreeCaller<ColorScheme_QER>() );
3240         GlobalCommands_insert( "ColorSchemeBlackAndGreen", FreeCaller<ColorScheme_Black>() );
3241         GlobalCommands_insert( "ColorSchemeYdnar", FreeCaller<ColorScheme_Ydnar>() );
3242         GlobalCommands_insert( "ChooseTextureBackgroundColor", makeCallback( g_ColoursMenu.m_textureback ) );
3243         GlobalCommands_insert( "ChooseGridBackgroundColor", makeCallback( g_ColoursMenu.m_xyback ) );
3244         GlobalCommands_insert( "ChooseGridMajorColor", makeCallback( g_ColoursMenu.m_gridmajor ) );
3245         GlobalCommands_insert( "ChooseGridMinorColor", makeCallback( g_ColoursMenu.m_gridminor ) );
3246         GlobalCommands_insert( "ChooseSmallGridMajorColor", makeCallback( g_ColoursMenu.m_gridmajor_alt ) );
3247         GlobalCommands_insert( "ChooseSmallGridMinorColor", makeCallback( g_ColoursMenu.m_gridminor_alt ) );
3248         GlobalCommands_insert( "ChooseGridTextColor", makeCallback( g_ColoursMenu.m_gridtext ) );
3249         GlobalCommands_insert( "ChooseGridBlockColor", makeCallback( g_ColoursMenu.m_gridblock ) );
3250         GlobalCommands_insert( "ChooseBrushColor", makeCallback( g_ColoursMenu.m_brush ) );
3251         GlobalCommands_insert( "ChooseCameraBackgroundColor", makeCallback( g_ColoursMenu.m_cameraback ) );
3252         GlobalCommands_insert( "ChooseSelectedBrushColor", makeCallback( g_ColoursMenu.m_selectedbrush ) );
3253         GlobalCommands_insert( "ChooseCameraSelectedBrushColor", makeCallback( g_ColoursMenu.m_selectedbrush3d ) );
3254         GlobalCommands_insert( "ChooseClipperColor", makeCallback( g_ColoursMenu.m_clipper ) );
3255         GlobalCommands_insert( "ChooseOrthoViewNameColor", makeCallback( g_ColoursMenu.m_viewname ) );
3256
3257
3258         GlobalCommands_insert( "CSGSubtract", FreeCaller<CSG_Subtract>(), Accelerator( 'U', (GdkModifierType)GDK_SHIFT_MASK ) );
3259         GlobalCommands_insert( "CSGMerge", FreeCaller<CSG_Merge>(), Accelerator( 'U', (GdkModifierType)GDK_CONTROL_MASK ) );
3260         GlobalCommands_insert( "CSGHollow", FreeCaller<CSG_MakeHollow>() );
3261
3262         Grid_registerCommands();
3263
3264         GlobalCommands_insert( "SnapToGrid", FreeCaller<Selection_SnapToGrid>(), Accelerator( 'G', (GdkModifierType)GDK_CONTROL_MASK ) );
3265
3266         GlobalCommands_insert( "SelectAllOfType", FreeCaller<Select_AllOfType>(), Accelerator( 'A', (GdkModifierType)GDK_SHIFT_MASK ) );
3267
3268         GlobalCommands_insert( "TexRotateClock", FreeCaller<Texdef_RotateClockwise>(), Accelerator( GDK_KEY_Next, (GdkModifierType)GDK_SHIFT_MASK ) );
3269         GlobalCommands_insert( "TexRotateCounter", FreeCaller<Texdef_RotateAntiClockwise>(), Accelerator( GDK_KEY_Prior, (GdkModifierType)GDK_SHIFT_MASK ) );
3270         GlobalCommands_insert( "TexScaleUp", FreeCaller<Texdef_ScaleUp>(), Accelerator( GDK_KEY_Up, (GdkModifierType)GDK_CONTROL_MASK ) );
3271         GlobalCommands_insert( "TexScaleDown", FreeCaller<Texdef_ScaleDown>(), Accelerator( GDK_KEY_Down, (GdkModifierType)GDK_CONTROL_MASK ) );
3272         GlobalCommands_insert( "TexScaleLeft", FreeCaller<Texdef_ScaleLeft>(), Accelerator( GDK_KEY_Left, (GdkModifierType)GDK_CONTROL_MASK ) );
3273         GlobalCommands_insert( "TexScaleRight", FreeCaller<Texdef_ScaleRight>(), Accelerator( GDK_KEY_Right, (GdkModifierType)GDK_CONTROL_MASK ) );
3274         GlobalCommands_insert( "TexShiftUp", FreeCaller<Texdef_ShiftUp>(), Accelerator( GDK_KEY_Up, (GdkModifierType)GDK_SHIFT_MASK ) );
3275         GlobalCommands_insert( "TexShiftDown", FreeCaller<Texdef_ShiftDown>(), Accelerator( GDK_KEY_Down, (GdkModifierType)GDK_SHIFT_MASK ) );
3276         GlobalCommands_insert( "TexShiftLeft", FreeCaller<Texdef_ShiftLeft>(), Accelerator( GDK_KEY_Left, (GdkModifierType)GDK_SHIFT_MASK ) );
3277         GlobalCommands_insert( "TexShiftRight", FreeCaller<Texdef_ShiftRight>(), Accelerator( GDK_KEY_Right, (GdkModifierType)GDK_SHIFT_MASK ) );
3278
3279         GlobalCommands_insert( "MoveSelectionDOWN", FreeCaller<Selection_MoveDown>(), Accelerator( GDK_KEY_KP_Subtract ) );
3280         GlobalCommands_insert( "MoveSelectionUP", FreeCaller<Selection_MoveUp>(), Accelerator( GDK_KEY_KP_Add ) );
3281
3282         GlobalCommands_insert( "SelectNudgeLeft", FreeCaller<Selection_NudgeLeft>(), Accelerator( GDK_KEY_Left, (GdkModifierType)GDK_MOD1_MASK ) );
3283         GlobalCommands_insert( "SelectNudgeRight", FreeCaller<Selection_NudgeRight>(), Accelerator( GDK_KEY_Right, (GdkModifierType)GDK_MOD1_MASK ) );
3284         GlobalCommands_insert( "SelectNudgeUp", FreeCaller<Selection_NudgeUp>(), Accelerator( GDK_KEY_Up, (GdkModifierType)GDK_MOD1_MASK ) );
3285         GlobalCommands_insert( "SelectNudgeDown", FreeCaller<Selection_NudgeDown>(), Accelerator( GDK_KEY_Down, (GdkModifierType)GDK_MOD1_MASK ) );
3286
3287         Patch_registerCommands();
3288         XYShow_registerCommands();
3289
3290         typedef FreeCaller1<const Selectable&, ComponentMode_SelectionChanged> ComponentModeSelectionChangedCaller;
3291         GlobalSelectionSystem().addSelectionChangeCallback( ComponentModeSelectionChangedCaller() );
3292
3293         GlobalPreferenceSystem().registerPreference( "DetachableMenus", BoolImportStringCaller( g_Layout_enableDetachableMenus.m_latched ), BoolExportStringCaller( g_Layout_enableDetachableMenus.m_latched ) );
3294         GlobalPreferenceSystem().registerPreference( "PatchToolBar", BoolImportStringCaller( g_Layout_enablePatchToolbar.m_latched ), BoolExportStringCaller( g_Layout_enablePatchToolbar.m_latched ) );
3295         GlobalPreferenceSystem().registerPreference( "PluginToolBar", BoolImportStringCaller( g_Layout_enablePluginToolbar.m_latched ), BoolExportStringCaller( g_Layout_enablePluginToolbar.m_latched ) );
3296         GlobalPreferenceSystem().registerPreference( "QE4StyleWindows", IntImportStringCaller( g_Layout_viewStyle.m_latched ), IntExportStringCaller( g_Layout_viewStyle.m_latched ) );
3297         GlobalPreferenceSystem().registerPreference( "XYHeight", IntImportStringCaller( g_layout_globals.nXYHeight ), IntExportStringCaller( g_layout_globals.nXYHeight ) );
3298         GlobalPreferenceSystem().registerPreference( "XYWidth", IntImportStringCaller( g_layout_globals.nXYWidth ), IntExportStringCaller( g_layout_globals.nXYWidth ) );
3299         GlobalPreferenceSystem().registerPreference( "CamWidth", IntImportStringCaller( g_layout_globals.nCamWidth ), IntExportStringCaller( g_layout_globals.nCamWidth ) );
3300         GlobalPreferenceSystem().registerPreference( "CamHeight", IntImportStringCaller( g_layout_globals.nCamHeight ), IntExportStringCaller( g_layout_globals.nCamHeight ) );
3301
3302         GlobalPreferenceSystem().registerPreference( "State", IntImportStringCaller( g_layout_globals.nState ), IntExportStringCaller( g_layout_globals.nState ) );
3303         GlobalPreferenceSystem().registerPreference( "PositionX", IntImportStringCaller( g_layout_globals.m_position.x ), IntExportStringCaller( g_layout_globals.m_position.x ) );
3304         GlobalPreferenceSystem().registerPreference( "PositionY", IntImportStringCaller( g_layout_globals.m_position.y ), IntExportStringCaller( g_layout_globals.m_position.y ) );
3305         GlobalPreferenceSystem().registerPreference( "Width", IntImportStringCaller( g_layout_globals.m_position.w ), IntExportStringCaller( g_layout_globals.m_position.w ) );
3306         GlobalPreferenceSystem().registerPreference( "Height", IntImportStringCaller( g_layout_globals.m_position.h ), IntExportStringCaller( g_layout_globals.m_position.h ) );
3307
3308         GlobalPreferenceSystem().registerPreference( "CamWnd", WindowPositionTrackerImportStringCaller( g_posCamWnd ), WindowPositionTrackerExportStringCaller( g_posCamWnd ) );
3309         GlobalPreferenceSystem().registerPreference( "XYWnd", WindowPositionTrackerImportStringCaller( g_posXYWnd ), WindowPositionTrackerExportStringCaller( g_posXYWnd ) );
3310         GlobalPreferenceSystem().registerPreference( "YZWnd", WindowPositionTrackerImportStringCaller( g_posYZWnd ), WindowPositionTrackerExportStringCaller( g_posYZWnd ) );
3311         GlobalPreferenceSystem().registerPreference( "XZWnd", WindowPositionTrackerImportStringCaller( g_posXZWnd ), WindowPositionTrackerExportStringCaller( g_posXZWnd ) );
3312
3313         {
3314                 const char* ENGINEPATH_ATTRIBUTE =
3315 #if defined( WIN32 )
3316                         "enginepath_win32"
3317 #elif defined( __APPLE__ )
3318                         "enginepath_macos"
3319 #elif defined( __linux__ ) || defined ( __FreeBSD__ )
3320                         "enginepath_linux"
3321 #else
3322 #error "unknown platform"
3323 #endif
3324                 ;
3325                 StringOutputStream path( 256 );
3326                 path << DirectoryCleaned( g_pGameDescription->getRequiredKeyValue( ENGINEPATH_ATTRIBUTE ) );
3327                 g_strEnginePath = path.c_str();
3328         }
3329
3330         GlobalPreferenceSystem().registerPreference( "EnginePath", CopiedStringImportStringCaller( g_strEnginePath ), CopiedStringExportStringCaller( g_strEnginePath ) );
3331
3332         g_Layout_viewStyle.useLatched();
3333         g_Layout_enableDetachableMenus.useLatched();
3334         g_Layout_enablePatchToolbar.useLatched();
3335         g_Layout_enablePluginToolbar.useLatched();
3336
3337         Layout_registerPreferencesPage();
3338         Paths_registerPreferencesPage();
3339
3340         g_brushCount.setCountChangedCallback( FreeCaller<QE_brushCountChanged>() );
3341         g_entityCount.setCountChangedCallback( FreeCaller<QE_entityCountChanged>() );
3342         GlobalEntityCreator().setCounter( &g_entityCount );
3343
3344         GLWidget_sharedContextCreated = GlobalGL_sharedContextCreated;
3345         GLWidget_sharedContextDestroyed = GlobalGL_sharedContextDestroyed;
3346
3347         GlobalEntityClassManager().attach( g_WorldspawnColourEntityClassObserver );
3348 }
3349
3350 void MainFrame_Destroy(){
3351         GlobalEntityClassManager().detach( g_WorldspawnColourEntityClassObserver );
3352
3353         GlobalEntityCreator().setCounter( 0 );
3354         g_entityCount.setCountChangedCallback( Callback() );
3355         g_brushCount.setCountChangedCallback( Callback() );
3356 }
3357
3358
3359 void GLWindow_Construct(){
3360         GlobalPreferenceSystem().registerPreference( "MouseButtons", IntImportStringCaller( g_glwindow_globals.m_nMouseType ), IntExportStringCaller( g_glwindow_globals.m_nMouseType ) );
3361 }
3362
3363 void GLWindow_Destroy(){
3364 }