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