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