1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "MapFrame.h"
21 
22 #include "TrenchBroomApp.h"
23 #include "Preferences.h"
24 #include "PreferenceManager.h"
25 #include "IO/DiskFileSystem.h"
26 #include "IO/ResourceUtils.h"
27 #include "Model/EditorContext.h"
28 #include "Model/Node.h"
29 #include "Model/NodeCollection.h"
30 #include "Model/PointFile.h"
31 #include "View/ActionManager.h"
32 #include "View/Autosaver.h"
33 #include "View/BorderLine.h"
34 #include "View/CachingLogger.h"
35 #include "View/CommandIds.h"
36 #include "View/Console.h"
37 #include "View/GLContextManager.h"
38 #include "View/Grid.h"
39 #include "View/InfoPanel.h"
40 #include "View/Inspector.h"
41 #include "View/MapDocument.h"
42 #include "View/MapFrameDropTarget.h"
43 #include "View/Menu.h"
44 #include "View/ReplaceTextureFrame.h"
45 #include "View/SplitterWindow2.h"
46 #include "View/SwitchableMapViewContainer.h"
47 #include "View/ViewUtils.h"
48 
49 #include <wx/clipbrd.h>
50 #include <wx/display.h>
51 #include <wx/filedlg.h>
52 #include <wx/msgdlg.h>
53 #include <wx/persist.h>
54 #include <wx/sizer.h>
55 #include <wx/stdpaths.h>
56 #include <wx/timer.h>
57 
58 #include <cassert>
59 
60 namespace TrenchBroom {
61     namespace View {
MapFrame()62         MapFrame::MapFrame() :
63         wxFrame(NULL, wxID_ANY, "TrenchBroom"),
64         m_frameManager(NULL),
65         m_autosaver(NULL),
66         m_autosaveTimer(NULL),
67         m_contextManager(NULL),
68         m_mapView(NULL),
69         m_console(NULL),
70         m_inspector(NULL),
71         m_lastFocus(NULL),
72         m_gridChoice(NULL) {}
73 
MapFrame(FrameManager * frameManager,MapDocumentSPtr document)74         MapFrame::MapFrame(FrameManager* frameManager, MapDocumentSPtr document) :
75         wxFrame(NULL, wxID_ANY, "TrenchBroom"),
76         m_frameManager(NULL),
77         m_autosaver(NULL),
78         m_autosaveTimer(NULL),
79         m_contextManager(NULL),
80         m_mapView(NULL),
81         m_console(NULL),
82         m_inspector(NULL),
83         m_lastFocus(NULL),
84         m_gridChoice(NULL)  {
85             Create(frameManager, document);
86         }
87 
Create(FrameManager * frameManager,MapDocumentSPtr document)88         void MapFrame::Create(FrameManager* frameManager, MapDocumentSPtr document) {
89             assert(frameManager != NULL);
90             assert(document.get() != NULL);
91 
92             m_frameManager = frameManager;
93             m_document = document;
94             m_autosaver = new Autosaver(m_document);
95 
96             m_contextManager = new GLContextManager();
97 
98             createGui();
99             createToolBar();
100             createMenuBar();
101 
102             m_document->setParentLogger(logger());
103             m_document->setViewEffectsService(m_mapView);
104 
105             m_autosaveTimer = new wxTimer(this);
106             m_autosaveTimer->Start(1000);
107 
108             bindObservers();
109             bindEvents();
110 
111             clearDropTarget();
112         }
113 
~MapFrame()114         MapFrame::~MapFrame() {
115             unbindObservers();
116             removeRecentDocumentsMenu(GetMenuBar());
117 
118             delete m_autosaveTimer;
119             m_autosaveTimer = NULL;
120 
121             delete m_autosaver;
122             m_autosaver = NULL;
123 
124             // The order of deletion here is important because both the document and the children
125             // need the context manager (and its embedded VBO) to clean up their resources.
126 
127             m_document->setViewEffectsService(NULL);
128             m_document.reset();
129             DestroyChildren();
130 
131             delete m_contextManager;
132             m_contextManager = NULL;
133         }
134 
positionOnScreen(wxFrame * reference)135         void MapFrame::positionOnScreen(wxFrame* reference) {
136             const wxDisplay display;
137             const wxRect displaySize = display.GetClientArea();
138             if (reference == NULL) {
139                 SetSize(std::min(displaySize.width, 1024), std::min(displaySize.height, 768));
140                 CenterOnScreen();
141             } else {
142                 wxPoint position = reference->GetPosition();
143                 position.x += 23;
144                 position.y += 23;
145 
146                 if (displaySize.GetBottom() - position.x < 100 ||
147                     displaySize.GetRight() - position.y < 70)
148                     position = displaySize.GetTopLeft();
149 
150                 SetPosition(position);
151                 SetSize(std::min(displaySize.GetRight() - position.x, 1024), std::min(displaySize.GetBottom() - position.y, 768));
152             }
153         }
154 
logger() const155         Logger* MapFrame::logger() const {
156             return m_console;
157         }
158 
setToolBoxDropTarget()159         void MapFrame::setToolBoxDropTarget() {
160             SetDropTarget(NULL);
161             m_mapView->setToolBoxDropTarget();
162         }
163 
clearDropTarget()164         void MapFrame::clearDropTarget() {
165             m_mapView->clearDropTarget();
166             SetDropTarget(new MapFrameDropTarget(m_document, this));
167         }
168 
newDocument(Model::GamePtr game,const Model::MapFormat::Type mapFormat)169         bool MapFrame::newDocument(Model::GamePtr game, const Model::MapFormat::Type mapFormat) {
170             if (!confirmOrDiscardChanges())
171                 return false;
172             m_document->newDocument(mapFormat, MapDocument::DefaultWorldBounds, game);
173             return true;
174         }
175 
openDocument(Model::GamePtr game,const Model::MapFormat::Type mapFormat,const IO::Path & path)176         bool MapFrame::openDocument(Model::GamePtr game, const Model::MapFormat::Type mapFormat, const IO::Path& path) {
177             if (!confirmOrDiscardChanges())
178                 return false;
179             m_document->loadDocument(mapFormat, MapDocument::DefaultWorldBounds, game, path);
180             return true;
181         }
182 
saveDocument()183         bool MapFrame::saveDocument() {
184             try {
185                 const IO::Path& path = m_document->path();
186                 if (path.isAbsolute() && IO::Disk::fileExists(IO::Disk::fixPath(path))) {
187                     m_document->saveDocument();
188                     logger()->info("Saved " + m_document->path().asString());
189                     return true;
190                 }
191                 return saveDocumentAs();
192             } catch (FileSystemException e) {
193                 ::wxMessageBox(e.what(), "", wxOK | wxICON_ERROR, this);
194                 return false;
195             } catch (...) {
196                 ::wxMessageBox("Unknown error while saving " + m_document->path().asString(), "", wxOK | wxICON_ERROR, this);
197                 return false;
198             }
199         }
200 
saveDocumentAs()201         bool MapFrame::saveDocumentAs() {
202             try {
203                 const IO::Path& originalPath = m_document->path();
204                 const IO::Path directory = originalPath.deleteLastComponent();
205                 const IO::Path fileName = originalPath.lastComponent();
206                 wxFileDialog saveDialog(this, "Save map file", directory.asString(), fileName.asString(), "Map files (*.map)|*.map", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
207                 if (saveDialog.ShowModal() == wxID_CANCEL)
208                     return false;
209 
210                 const IO::Path path(saveDialog.GetPath().ToStdString());
211                 m_document->saveDocumentAs(path);
212                 logger()->info("Saved " + m_document->path().asString());
213                 return true;
214             } catch (FileSystemException e) {
215                 ::wxMessageBox(e.what(), "", wxOK | wxICON_ERROR, this);
216                 return false;
217             } catch (...) {
218                 ::wxMessageBox("Unknown error while saving " + m_document->filename(), "", wxOK | wxICON_ERROR, this);
219                 return false;
220             }
221         }
222 
confirmOrDiscardChanges()223         bool MapFrame::confirmOrDiscardChanges() {
224             if (!m_document->modified())
225                 return true;
226             const int result = ::wxMessageBox(m_document->filename() + " has been modified. Do you want to save the changes?", "TrenchBroom", wxYES_NO | wxCANCEL, this);
227             switch (result) {
228                 case wxYES:
229                     return saveDocument();
230                 case wxNO:
231                     return true;
232                 default:
233                     return false;
234             }
235         }
236 
updateTitle()237         void MapFrame::updateTitle() {
238 #ifdef __APPLE__
239             SetTitle(m_document->filename());
240             OSXSetModified(m_document->modified());
241 #else
242             SetTitle(wxString(m_document->filename()) + wxString(m_document->modified() ? "*" : "") + wxString(" - TrenchBroom"));
243 #endif
244             SetRepresentedFilename(m_document->path().asString());
245         }
246 
OnChildFocus(wxChildFocusEvent & event)247         void MapFrame::OnChildFocus(wxChildFocusEvent& event) {
248             if (IsBeingDeleted()) return;
249 
250             wxWindow* focus = FindFocus();
251             if (focus != m_lastFocus && focus != this) {
252                 rebuildMenuBar();
253                 m_lastFocus = focus;
254             }
255         }
256 
rebuildMenuBar()257         void MapFrame::rebuildMenuBar() {
258             wxMenuBar* oldMenuBar = GetMenuBar();
259             removeRecentDocumentsMenu(oldMenuBar);
260             createMenuBar();
261 #ifndef __LINUX__
262 #ifndef wxUSE_IDLEMENUUPDATES
263             // Don't delete the old menu bar on Ubuntu. It will leak, but otherwise we crash on the next idle update.
264             oldMenuBar->Destroy();
265 #endif
266 #endif
267         }
268 
createMenuBar()269         void MapFrame::createMenuBar() {
270             const ActionManager& actionManager = ActionManager::instance();
271             wxMenuBar* menuBar = actionManager.createMenuBar(m_mapView->viewportHasFocus());
272             SetMenuBar(menuBar);
273             addRecentDocumentsMenu(menuBar);
274         }
275 
addRecentDocumentsMenu(wxMenuBar * menuBar)276         void MapFrame::addRecentDocumentsMenu(wxMenuBar* menuBar) {
277             const ActionManager& actionManager = ActionManager::instance();
278             wxMenu* recentDocumentsMenu = actionManager.findRecentDocumentsMenu(menuBar);
279             assert(recentDocumentsMenu != NULL);
280 
281             TrenchBroomApp& app = TrenchBroomApp::instance();
282             app.addRecentDocumentMenu(recentDocumentsMenu);
283         }
284 
removeRecentDocumentsMenu(wxMenuBar * menuBar)285         void MapFrame::removeRecentDocumentsMenu(wxMenuBar* menuBar) {
286             const ActionManager& actionManager = ActionManager::instance();
287             wxMenu* recentDocumentsMenu = actionManager.findRecentDocumentsMenu(menuBar);
288             assert(recentDocumentsMenu != NULL);
289 
290             TrenchBroomApp& app = TrenchBroomApp::instance();
291             app.removeRecentDocumentMenu(recentDocumentsMenu);
292         }
293 
updateRecentDocumentsMenu()294         void MapFrame::updateRecentDocumentsMenu() {
295             if (m_document->path().isAbsolute())
296                 View::TrenchBroomApp::instance().updateRecentDocument(m_document->path());
297         }
298 
createGui()299         void MapFrame::createGui() {
300             SetIcon(wxIcon(wxStandardPaths::Get().GetInstallPrefix() + "/share/icons/hicolor/128x128/apps/trenchbroom.png", wxBITMAP_TYPE_PNG));
301 
302             m_hSplitter = new SplitterWindow2(this);
303             m_hSplitter->setSashGravity(1.0);
304             m_hSplitter->SetName("MapFrameHSplitter");
305 
306             m_vSplitter = new SplitterWindow2(m_hSplitter);
307             m_vSplitter->setSashGravity(1.0);
308             m_vSplitter->SetName("MapFrameVSplitter");
309 
310             InfoPanel* infoPanel = new InfoPanel(m_vSplitter, m_document);
311             m_console = infoPanel->console();
312             m_mapView = new SwitchableMapViewContainer(m_vSplitter, m_console, m_document, *m_contextManager);
313             m_inspector = new Inspector(m_hSplitter, m_document, *m_contextManager);
314 
315             m_mapView->connectTopWidgets(m_inspector);
316 
317             m_vSplitter->splitHorizontally(m_mapView, infoPanel, wxSize(100, 100), wxSize(100, 100));
318             m_hSplitter->splitVertically(m_vSplitter, m_inspector, wxSize(100, 100), wxSize(350, 100));
319 
320             wxSizer* frameSizer = new wxBoxSizer(wxVERTICAL);
321 #if !defined __APPLE__
322             frameSizer->Add(new BorderLine(this), 1, wxEXPAND);
323 #endif
324             frameSizer->Add(m_hSplitter, 1, wxEXPAND);
325 
326             SetSizer(frameSizer);
327 
328             wxPersistenceManager::Get().RegisterAndRestore(m_hSplitter);
329             wxPersistenceManager::Get().RegisterAndRestore(m_vSplitter);
330         }
331 
createToolBar()332         void MapFrame::createToolBar() {
333             wxToolBar* toolBar = CreateToolBar(wxTB_DEFAULT_STYLE | wxTB_NODIVIDER | wxTB_FLAT);
334             toolBar->SetMargins(2, 2);
335             toolBar->AddRadioTool(CommandIds::Menu::EditDeactivateTool, "Default Tool", IO::loadImageResource("NoTool.png"), wxNullBitmap, "Disable Current Tool");
336             toolBar->AddRadioTool(CommandIds::Menu::EditToggleCreateComplexBrushTool, "Brush Tool", IO::loadImageResource("BrushTool.png"), wxNullBitmap, "Brush Tool");
337             toolBar->AddRadioTool(CommandIds::Menu::EditToggleClipTool, "Clip Tool", IO::loadImageResource("ClipTool.png"), wxNullBitmap, "Clip Tool");
338             toolBar->AddRadioTool(CommandIds::Menu::EditToggleVertexTool, "Vertex Tool", IO::loadImageResource("VertexTool.png"), wxNullBitmap, "Vertex Tool");
339             toolBar->AddRadioTool(CommandIds::Menu::EditToggleRotateObjectsTool, "Rotate Tool", IO::loadImageResource("RotateTool.png"), wxNullBitmap, "Rotate Tool");
340             toolBar->AddSeparator();
341             toolBar->AddTool(wxID_DUPLICATE, "Duplicate Objects", IO::loadImageResource("DuplicateObjects.png"), wxNullBitmap, wxITEM_NORMAL, "Duplicate Objects");
342             toolBar->AddTool(CommandIds::Actions::FlipObjectsHorizontally, "Flip Horizontally", IO::loadImageResource("FlipHorizontally.png"), wxNullBitmap, wxITEM_NORMAL, "Flip Horizontally");
343             toolBar->AddTool(CommandIds::Actions::FlipObjectsVertically, "Flip Vertically", IO::loadImageResource("FlipVertically.png"), wxNullBitmap, wxITEM_NORMAL, "Flip Vertically");
344             toolBar->AddSeparator();
345             toolBar->AddCheckTool(CommandIds::Menu::EditToggleTextureLock, "Texture Lock", textureLockBitmap(), wxNullBitmap, "Toggle Texture Lock");
346             toolBar->AddSeparator();
347 
348             const wxString gridSizes[9] = { "Grid 1", "Grid 2", "Grid 4", "Grid 8", "Grid 16", "Grid 32", "Grid 64", "Grid 128", "Grid 256" };
349             m_gridChoice = new wxChoice(toolBar, wxID_ANY, wxDefaultPosition, wxDefaultSize, 9, gridSizes);
350             m_gridChoice->SetSelection(static_cast<int>(m_document->grid().size()));
351             toolBar->AddControl(m_gridChoice);
352 
353             toolBar->Realize();
354         }
355 
bindObservers()356         void MapFrame::bindObservers() {
357             PreferenceManager& prefs = PreferenceManager::instance();
358             prefs.preferenceDidChangeNotifier.addObserver(this, &MapFrame::preferenceDidChange);
359 
360             m_document->documentWasClearedNotifier.addObserver(this, &MapFrame::documentWasCleared);
361             m_document->documentWasNewedNotifier.addObserver(this, &MapFrame::documentDidChange);
362             m_document->documentWasLoadedNotifier.addObserver(this, &MapFrame::documentDidChange);
363             m_document->documentWasSavedNotifier.addObserver(this, &MapFrame::documentDidChange);
364             m_document->documentModificationStateDidChangeNotifier.addObserver(this, &MapFrame::documentModificationStateDidChange);
365 
366             Grid& grid = m_document->grid();
367             grid.gridDidChangeNotifier.addObserver(this, &MapFrame::gridDidChange);
368         }
369 
unbindObservers()370         void MapFrame::unbindObservers() {
371             PreferenceManager& prefs = PreferenceManager::instance();
372             prefs.preferenceDidChangeNotifier.removeObserver(this, &MapFrame::preferenceDidChange);
373 
374             m_document->documentWasClearedNotifier.removeObserver(this, &MapFrame::documentWasCleared);
375             m_document->documentWasNewedNotifier.removeObserver(this, &MapFrame::documentDidChange);
376             m_document->documentWasLoadedNotifier.removeObserver(this, &MapFrame::documentDidChange);
377             m_document->documentWasSavedNotifier.removeObserver(this, &MapFrame::documentDidChange);
378             m_document->documentModificationStateDidChangeNotifier.removeObserver(this, &MapFrame::documentModificationStateDidChange);
379 
380             Grid& grid = m_document->grid();
381             grid.gridDidChangeNotifier.removeObserver(this, &MapFrame::gridDidChange);
382         }
383 
documentWasCleared(View::MapDocument * document)384         void MapFrame::documentWasCleared(View::MapDocument* document) {
385             updateTitle();
386         }
387 
documentDidChange(View::MapDocument * document)388         void MapFrame::documentDidChange(View::MapDocument* document) {
389             updateTitle();
390             updateRecentDocumentsMenu();
391         }
392 
documentModificationStateDidChange()393         void MapFrame::documentModificationStateDidChange() {
394             updateTitle();
395         }
396 
preferenceDidChange(const IO::Path & path)397         void MapFrame::preferenceDidChange(const IO::Path& path) {
398             const ActionManager& actionManager = ActionManager::instance();
399             if (actionManager.isMenuShortcutPreference(path)) {
400                 rebuildMenuBar();
401             } else if (path == Preferences::MapViewLayout.path())
402                 m_mapView->switchToMapView(static_cast<MapViewLayout>(pref(Preferences::MapViewLayout)));
403         }
404 
gridDidChange()405         void MapFrame::gridDidChange() {
406             const Grid& grid = m_document->grid();
407             m_gridChoice->SetSelection(static_cast<int>(grid.size()));
408         }
409 
bindEvents()410         void MapFrame::bindEvents() {
411             Bind(wxEVT_MENU, &MapFrame::OnFileSave, this, wxID_SAVE);
412             Bind(wxEVT_MENU, &MapFrame::OnFileSaveAs, this, wxID_SAVEAS);
413             Bind(wxEVT_MENU, &MapFrame::OnFileLoadPointFile, this, CommandIds::Menu::FileLoadPointFile);
414             Bind(wxEVT_MENU, &MapFrame::OnFileUnloadPointFile, this, CommandIds::Menu::FileUnloadPointFile);
415             Bind(wxEVT_MENU, &MapFrame::OnFileClose, this, wxID_CLOSE);
416 
417             Bind(wxEVT_MENU, &MapFrame::OnEditUndo, this, wxID_UNDO);
418             Bind(wxEVT_MENU, &MapFrame::OnEditRedo, this, wxID_REDO);
419             Bind(wxEVT_MENU, &MapFrame::OnEditRepeat, this, CommandIds::Menu::EditRepeat);
420             Bind(wxEVT_MENU, &MapFrame::OnEditClearRepeat, this, CommandIds::Menu::EditClearRepeat);
421 
422             Bind(wxEVT_MENU, &MapFrame::OnEditCut, this, wxID_CUT);
423             Bind(wxEVT_MENU, &MapFrame::OnEditCopy, this, wxID_COPY);
424             Bind(wxEVT_MENU, &MapFrame::OnEditPaste, this, wxID_PASTE);
425             Bind(wxEVT_MENU, &MapFrame::OnEditPasteAtOriginalPosition, this, CommandIds::Menu::EditPasteAtOriginalPosition);
426             Bind(wxEVT_MENU, &MapFrame::OnEditDuplicate, this, wxID_DUPLICATE);
427             Bind(wxEVT_MENU, &MapFrame::OnEditDelete, this, wxID_DELETE);
428 
429             Bind(wxEVT_MENU, &MapFrame::OnEditSelectAll, this, CommandIds::Menu::EditSelectAll);
430             Bind(wxEVT_MENU, &MapFrame::OnEditSelectSiblings, this, CommandIds::Menu::EditSelectSiblings);
431             Bind(wxEVT_MENU, &MapFrame::OnEditSelectTouching, this, CommandIds::Menu::EditSelectTouching);
432             Bind(wxEVT_MENU, &MapFrame::OnEditSelectInside, this, CommandIds::Menu::EditSelectInside);
433             Bind(wxEVT_MENU, &MapFrame::OnEditSelectTall, this, CommandIds::Menu::EditSelectTall);
434             Bind(wxEVT_MENU, &MapFrame::OnEditSelectByLineNumber, this, CommandIds::Menu::EditSelectByFilePosition);
435             Bind(wxEVT_MENU, &MapFrame::OnEditSelectNone, this, CommandIds::Menu::EditSelectNone);
436 
437             Bind(wxEVT_MENU, &MapFrame::OnEditGroupSelectedObjects, this, CommandIds::Menu::EditGroupSelection);
438             Bind(wxEVT_MENU, &MapFrame::OnEditUngroupSelectedObjects, this, CommandIds::Menu::EditUngroupSelection);
439 
440             Bind(wxEVT_MENU, &MapFrame::OnEditDeactivateTool, this, CommandIds::Menu::EditDeactivateTool);
441             Bind(wxEVT_MENU, &MapFrame::OnEditToggleCreateComplexBrushTool, this, CommandIds::Menu::EditToggleCreateComplexBrushTool);
442             Bind(wxEVT_MENU, &MapFrame::OnEditToggleClipTool, this, CommandIds::Menu::EditToggleClipTool);
443             Bind(wxEVT_MENU, &MapFrame::OnEditToggleRotateObjectsTool, this, CommandIds::Menu::EditToggleRotateObjectsTool);
444             Bind(wxEVT_MENU, &MapFrame::OnEditToggleVertexTool, this, CommandIds::Menu::EditToggleVertexTool);
445 
446             Bind(wxEVT_MENU, &MapFrame::OnEditCsgConvexMerge, this, CommandIds::Menu::EditCsgConvexMerge);
447             Bind(wxEVT_MENU, &MapFrame::OnEditCsgSubtract, this, CommandIds::Menu::EditCsgSubtract);
448             Bind(wxEVT_MENU, &MapFrame::OnEditCsgIntersect, this, CommandIds::Menu::EditCsgIntersect);
449 
450             Bind(wxEVT_MENU, &MapFrame::OnEditReplaceTexture, this, CommandIds::Menu::EditReplaceTexture);
451             Bind(wxEVT_MENU, &MapFrame::OnEditToggleTextureLock, this, CommandIds::Menu::EditToggleTextureLock);
452             Bind(wxEVT_MENU, &MapFrame::OnEditSnapVerticesToInteger, this, CommandIds::Menu::EditSnapVerticesToInteger);
453             Bind(wxEVT_MENU, &MapFrame::OnEditSnapVerticesToGrid, this, CommandIds::Menu::EditSnapVerticesToGrid);
454 
455             Bind(wxEVT_MENU, &MapFrame::OnViewToggleShowGrid, this, CommandIds::Menu::ViewToggleShowGrid);
456             Bind(wxEVT_MENU, &MapFrame::OnViewToggleSnapToGrid, this, CommandIds::Menu::ViewToggleSnapToGrid);
457             Bind(wxEVT_MENU, &MapFrame::OnViewIncGridSize, this, CommandIds::Menu::ViewIncGridSize);
458             Bind(wxEVT_MENU, &MapFrame::OnViewDecGridSize, this, CommandIds::Menu::ViewDecGridSize);
459             Bind(wxEVT_MENU, &MapFrame::OnViewSetGridSize, this, CommandIds::Menu::ViewSetGridSize1, CommandIds::Menu::ViewSetGridSize256);
460 
461             Bind(wxEVT_MENU, &MapFrame::OnViewMoveCameraToNextPoint, this, CommandIds::Menu::ViewMoveCameraToNextPoint);
462             Bind(wxEVT_MENU, &MapFrame::OnViewMoveCameraToPreviousPoint, this, CommandIds::Menu::ViewMoveCameraToPreviousPoint);
463             Bind(wxEVT_MENU, &MapFrame::OnViewFocusCameraOnSelection, this, CommandIds::Menu::ViewFocusCameraOnSelection);
464             Bind(wxEVT_MENU, &MapFrame::OnViewMoveCameraToPosition, this, CommandIds::Menu::ViewMoveCameraToPosition);
465 
466             Bind(wxEVT_MENU, &MapFrame::OnViewHideSelectedObjects, this, CommandIds::Menu::ViewHideSelection);
467             Bind(wxEVT_MENU, &MapFrame::OnViewIsolateSelectedObjects, this, CommandIds::Menu::ViewIsolateSelection);
468             Bind(wxEVT_MENU, &MapFrame::OnViewShowHiddenObjects, this, CommandIds::Menu::ViewUnhideAll);
469 
470             Bind(wxEVT_MENU, &MapFrame::OnViewSwitchToMapInspector, this, CommandIds::Menu::ViewSwitchToMapInspector);
471             Bind(wxEVT_MENU, &MapFrame::OnViewSwitchToEntityInspector, this, CommandIds::Menu::ViewSwitchToEntityInspector);
472             Bind(wxEVT_MENU, &MapFrame::OnViewSwitchToFaceInspector, this, CommandIds::Menu::ViewSwitchToFaceInspector);
473 
474             Bind(wxEVT_MENU, &MapFrame::OnViewToggleMaximizeCurrentView, this, CommandIds::Menu::ViewToggleMaximizeCurrentView);
475             Bind(wxEVT_MENU, &MapFrame::OnViewToggleInfoPanel, this, CommandIds::Menu::ViewToggleInfoPanel);
476             Bind(wxEVT_MENU, &MapFrame::OnViewToggleInspector, this, CommandIds::Menu::ViewToggleInspector);
477 
478             Bind(wxEVT_MENU, &MapFrame::OnDebugPrintVertices, this, CommandIds::Menu::DebugPrintVertices);
479             Bind(wxEVT_MENU, &MapFrame::OnDebugCreateBrush, this, CommandIds::Menu::DebugCreateBrush);
480             Bind(wxEVT_MENU, &MapFrame::OnDebugCopyJSShortcutMap, this, CommandIds::Menu::DebugCopyJSShortcuts);
481 
482             Bind(wxEVT_MENU, &MapFrame::OnFlipObjectsHorizontally, this, CommandIds::Actions::FlipObjectsHorizontally);
483             Bind(wxEVT_MENU, &MapFrame::OnFlipObjectsVertically, this, CommandIds::Actions::FlipObjectsVertically);
484 
485             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_SAVE);
486             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_SAVEAS);
487             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_CLOSE);
488             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_UNDO);
489             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_REDO);
490             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_CUT);
491             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_COPY);
492             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_PASTE);
493             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, wxID_DUPLICATE);
494             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, CommandIds::Menu::Lowest, CommandIds::Menu::Highest);
495 
496             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, CommandIds::Actions::FlipObjectsHorizontally);
497             Bind(wxEVT_UPDATE_UI, &MapFrame::OnUpdateUI, this, CommandIds::Actions::FlipObjectsVertically);
498 
499             Bind(wxEVT_CLOSE_WINDOW, &MapFrame::OnClose, this);
500             Bind(wxEVT_TIMER, &MapFrame::OnAutosaveTimer, this);
501             Bind(wxEVT_CHILD_FOCUS, &MapFrame::OnChildFocus, this);
502 
503             m_gridChoice->Bind(wxEVT_CHOICE, &MapFrame::OnToolBarSetGridSize, this);
504         }
505 
OnFileSave(wxCommandEvent & event)506         void MapFrame::OnFileSave(wxCommandEvent& event) {
507             if (IsBeingDeleted()) return;
508 
509             saveDocument();
510         }
511 
OnFileSaveAs(wxCommandEvent & event)512         void MapFrame::OnFileSaveAs(wxCommandEvent& event) {
513             if (IsBeingDeleted()) return;
514 
515             saveDocumentAs();
516         }
517 
OnFileLoadPointFile(wxCommandEvent & event)518         void MapFrame::OnFileLoadPointFile(wxCommandEvent& event) {
519             if (IsBeingDeleted()) return;
520             if (canLoadPointFile())
521                 m_document->loadPointFile();
522         }
523 
OnFileUnloadPointFile(wxCommandEvent & event)524         void MapFrame::OnFileUnloadPointFile(wxCommandEvent& event) {
525             if (IsBeingDeleted()) return;
526             if (canUnloadPointFile())
527                 m_document->unloadPointFile();
528         }
529 
OnFileClose(wxCommandEvent & event)530         void MapFrame::OnFileClose(wxCommandEvent& event) {
531             if (IsBeingDeleted()) return;
532 
533             Close();
534         }
535 
OnEditUndo(wxCommandEvent & event)536         void MapFrame::OnEditUndo(wxCommandEvent& event) {
537             if (IsBeingDeleted()) return;
538 
539             if (canUndo())
540                 m_document->undoLastCommand();
541         }
542 
OnEditRedo(wxCommandEvent & event)543         void MapFrame::OnEditRedo(wxCommandEvent& event) {
544             if (IsBeingDeleted()) return;
545 
546             if (canRedo())
547                 m_document->redoNextCommand();
548         }
549 
OnEditRepeat(wxCommandEvent & event)550         void MapFrame::OnEditRepeat(wxCommandEvent& event) {
551             if (IsBeingDeleted()) return;
552 
553             m_document->repeatLastCommands();
554         }
555 
OnEditClearRepeat(wxCommandEvent & event)556         void MapFrame::OnEditClearRepeat(wxCommandEvent& event) {
557             if (IsBeingDeleted()) return;
558 
559             m_document->clearRepeatableCommands();
560         }
561 
OnEditCut(wxCommandEvent & event)562         void MapFrame::OnEditCut(wxCommandEvent& event) {
563             if (IsBeingDeleted()) return;
564 
565             if (canCut()) {
566                 copyToClipboard();
567                 Transaction transaction(m_document, "Cut");
568                 m_document->deleteObjects();
569             }
570         }
571 
OnEditCopy(wxCommandEvent & event)572         void MapFrame::OnEditCopy(wxCommandEvent& event) {
573             if (IsBeingDeleted()) return;
574 
575             if (canCopy())
576                 copyToClipboard();
577         }
578 
579         class OpenClipboard {
580         public:
OpenClipboard()581             OpenClipboard() {
582                 if (!wxTheClipboard->IsOpened())
583                     wxTheClipboard->Open();
584             }
585 
~OpenClipboard()586             ~OpenClipboard() {
587                 if (wxTheClipboard->IsOpened())
588                     wxTheClipboard->Close();
589             }
590         };
591 
copyToClipboard()592         void MapFrame::copyToClipboard() {
593             OpenClipboard openClipboard;
594             if (wxTheClipboard->IsOpened()) {
595                 String str;
596                 if (m_document->hasSelectedNodes())
597                     str = m_document->serializeSelectedNodes();
598                 else if (m_document->hasSelectedBrushFaces())
599                     str = m_document->serializeSelectedBrushFaces();
600                 wxTheClipboard->SetData(new wxTextDataObject(str));
601             }
602         }
603 
OnEditPaste(wxCommandEvent & event)604         void MapFrame::OnEditPaste(wxCommandEvent& event) {
605             if (IsBeingDeleted()) return;
606 
607             if (canPaste()) {
608                 const BBox3 referenceBounds = m_document->referenceBounds();
609                 Transaction transaction(m_document);
610                 if (paste() == PT_Node && m_document->hasSelectedNodes()) {
611                     const BBox3 bounds = m_document->selectionBounds();
612                     const Vec3 delta = m_mapView->pasteObjectsDelta(bounds, referenceBounds);
613                     m_document->translateObjects(delta);
614                 }
615             }
616         }
617 
OnEditPasteAtOriginalPosition(wxCommandEvent & event)618         void MapFrame::OnEditPasteAtOriginalPosition(wxCommandEvent& event) {
619             if (IsBeingDeleted()) return;
620 
621             if (canPaste())
622                 paste();
623         }
624 
paste()625         PasteType MapFrame::paste() {
626             OpenClipboard openClipboard;
627             if (!wxTheClipboard->IsOpened() || !wxTheClipboard->IsSupported(wxDF_TEXT)) {
628                 logger()->error("Clipboard is empty");
629                 return PT_Failed;
630             }
631 
632             wxTextDataObject textData;
633             if (!wxTheClipboard->GetData(textData)) {
634                 logger()->error("Could not get clipboard contents");
635                 return PT_Failed;
636             }
637 
638             const String text = textData.GetText().ToStdString();
639             return m_document->paste(text);
640         }
641 
OnEditDelete(wxCommandEvent & event)642         void MapFrame::OnEditDelete(wxCommandEvent& event) {
643             if (IsBeingDeleted()) return;
644 
645             if (canDelete())
646                 m_document->deleteObjects();
647         }
648 
OnEditDuplicate(wxCommandEvent & event)649         void MapFrame::OnEditDuplicate(wxCommandEvent& event) {
650             if (IsBeingDeleted()) return;
651 
652             if (canDuplicate())
653                 m_document->duplicateObjects();
654         }
655 
OnEditSelectAll(wxCommandEvent & event)656         void MapFrame::OnEditSelectAll(wxCommandEvent& event) {
657             if (IsBeingDeleted()) return;
658 
659             if (canSelect())
660                 m_document->selectAllNodes();
661         }
662 
OnEditSelectSiblings(wxCommandEvent & event)663         void MapFrame::OnEditSelectSiblings(wxCommandEvent& event) {
664             if (IsBeingDeleted()) return;
665 
666             if (canSelectSiblings())
667                 m_document->selectSiblings();
668         }
669 
OnEditSelectTouching(wxCommandEvent & event)670         void MapFrame::OnEditSelectTouching(wxCommandEvent& event) {
671             if (IsBeingDeleted()) return;
672 
673             if (canSelectByBrush())
674                 m_document->selectTouching(true);
675         }
676 
OnEditSelectInside(wxCommandEvent & event)677         void MapFrame::OnEditSelectInside(wxCommandEvent& event) {
678             if (IsBeingDeleted()) return;
679 
680             if (canSelectByBrush())
681                 m_document->selectInside(true);
682         }
683 
OnEditSelectTall(wxCommandEvent & event)684         void MapFrame::OnEditSelectTall(wxCommandEvent& event) {
685             if (IsBeingDeleted()) return;
686 
687             if (canSelectTall())
688                 m_mapView->selectTall();
689         }
690 
OnEditSelectByLineNumber(wxCommandEvent & event)691         void MapFrame::OnEditSelectByLineNumber(wxCommandEvent& event) {
692             if (IsBeingDeleted()) return;
693 
694             if (canSelect()) {
695                 const wxString string = wxGetTextFromUser("Enter a comma- or space separated list of line numbers.", "Select by Line Numbers", "", this);
696                 if (string.empty())
697                     return;
698 
699                 std::vector<size_t> positions;
700                 wxStringTokenizer tokenizer(string, ", ");
701                 while (tokenizer.HasMoreTokens()) {
702                     const wxString token = tokenizer.NextToken();
703                     long position;
704                     if (token.ToLong(&position) && position > 0) {
705                         positions.push_back(static_cast<size_t>(position));
706                     }
707                 }
708 
709                 m_document->selectNodesWithFilePosition(positions);
710             }
711         }
712 
OnEditSelectNone(wxCommandEvent & event)713         void MapFrame::OnEditSelectNone(wxCommandEvent& event) {
714             if (IsBeingDeleted()) return;
715 
716             if (canDeselect())
717                 m_document->deselectAll();
718         }
719 
OnEditGroupSelectedObjects(wxCommandEvent & event)720         void MapFrame::OnEditGroupSelectedObjects(wxCommandEvent& event) {
721             if (IsBeingDeleted()) return;
722 
723             if (canGroup()) {
724                 const String name = queryGroupName(this);
725                 if (!name.empty())
726                     m_document->groupSelection(name);
727             }
728         }
729 
OnEditUngroupSelectedObjects(wxCommandEvent & event)730         void MapFrame::OnEditUngroupSelectedObjects(wxCommandEvent& event) {
731             if (IsBeingDeleted()) return;
732 
733             if (canUngroup())
734                 m_document->ungroupSelection();
735         }
736 
OnEditReplaceTexture(wxCommandEvent & event)737         void MapFrame::OnEditReplaceTexture(wxCommandEvent& event) {
738             if (IsBeingDeleted()) return;
739 
740             ReplaceTextureFrame* frame = new ReplaceTextureFrame(this, m_document, *m_contextManager);
741             frame->CenterOnParent();
742             frame->Show();
743         }
744 
OnEditDeactivateTool(wxCommandEvent & event)745         void MapFrame::OnEditDeactivateTool(wxCommandEvent& event) {
746             if (IsBeingDeleted()) return;
747 
748             m_mapView->deactivateTool();
749         }
750 
OnEditToggleCreateComplexBrushTool(wxCommandEvent & event)751         void MapFrame::OnEditToggleCreateComplexBrushTool(wxCommandEvent& event) {
752             if (IsBeingDeleted()) return;
753 
754             m_mapView->toggleCreateComplexBrushTool();
755         }
756 
OnEditToggleClipTool(wxCommandEvent & event)757         void MapFrame::OnEditToggleClipTool(wxCommandEvent& event) {
758             if (IsBeingDeleted()) return;
759 
760             m_mapView->toggleClipTool();
761         }
762 
OnEditToggleRotateObjectsTool(wxCommandEvent & event)763         void MapFrame::OnEditToggleRotateObjectsTool(wxCommandEvent& event) {
764             if (IsBeingDeleted()) return;
765 
766             m_mapView->toggleRotateObjectsTool();
767         }
768 
OnEditToggleVertexTool(wxCommandEvent & event)769         void MapFrame::OnEditToggleVertexTool(wxCommandEvent& event) {
770             if (IsBeingDeleted()) return;
771 
772             m_mapView->toggleVertexTool();
773         }
774 
OnEditCsgConvexMerge(wxCommandEvent & event)775         void MapFrame::OnEditCsgConvexMerge(wxCommandEvent& event) {
776             if (IsBeingDeleted()) return;
777 
778             if (canDoCsgConvexMerge())
779                 m_document->csgConvexMerge();
780         }
781 
OnEditCsgSubtract(wxCommandEvent & event)782         void MapFrame::OnEditCsgSubtract(wxCommandEvent& event) {
783             if (IsBeingDeleted()) return;
784 
785             if (canDoCsgSubtract())
786                 m_document->csgSubtract();
787         }
788 
OnEditCsgIntersect(wxCommandEvent & event)789         void MapFrame::OnEditCsgIntersect(wxCommandEvent& event) {
790             if (IsBeingDeleted()) return;
791 
792             if (canDoCsgIntersect())
793                 m_document->csgIntersect();
794         }
795 
OnEditToggleTextureLock(wxCommandEvent & event)796         void MapFrame::OnEditToggleTextureLock(wxCommandEvent& event) {
797             if (IsBeingDeleted()) return;
798 
799             m_document->setTextureLock(!m_document->textureLock());
800             GetToolBar()->SetToolNormalBitmap(CommandIds::Menu::EditToggleTextureLock, textureLockBitmap());
801         }
802 
textureLockBitmap()803         wxBitmap MapFrame::textureLockBitmap() {
804             if (m_document->textureLock())
805                 return IO::loadImageResource("TextureLockOn.png");
806             return IO::loadImageResource("TextureLockOff.png");
807         }
808 
OnEditSnapVerticesToInteger(wxCommandEvent & event)809         void MapFrame::OnEditSnapVerticesToInteger(wxCommandEvent& event) {
810             if (IsBeingDeleted()) return;
811 
812             if (canSnapVertices())
813                 m_document->snapVertices(1u);
814         }
815 
OnEditSnapVerticesToGrid(wxCommandEvent & event)816         void MapFrame::OnEditSnapVerticesToGrid(wxCommandEvent& event) {
817             if (IsBeingDeleted()) return;
818 
819             if (canSnapVertices())
820                 m_document->snapVertices(m_document->grid().actualSize());
821         }
822 
OnViewToggleShowGrid(wxCommandEvent & event)823         void MapFrame::OnViewToggleShowGrid(wxCommandEvent& event) {
824             if (IsBeingDeleted()) return;
825 
826             m_document->grid().toggleVisible();
827         }
828 
OnViewToggleSnapToGrid(wxCommandEvent & event)829         void MapFrame::OnViewToggleSnapToGrid(wxCommandEvent& event) {
830             if (IsBeingDeleted()) return;
831 
832             m_document->grid().toggleSnap();
833         }
834 
OnViewIncGridSize(wxCommandEvent & event)835         void MapFrame::OnViewIncGridSize(wxCommandEvent& event) {
836             if (IsBeingDeleted()) return;
837 
838             if (canIncGridSize())
839                 m_document->grid().incSize();
840         }
841 
OnViewDecGridSize(wxCommandEvent & event)842         void MapFrame::OnViewDecGridSize(wxCommandEvent& event) {
843             if (IsBeingDeleted()) return;
844 
845             if (canDecGridSize())
846                 m_document->grid().decSize();
847         }
848 
OnViewSetGridSize(wxCommandEvent & event)849         void MapFrame::OnViewSetGridSize(wxCommandEvent& event) {
850             if (IsBeingDeleted()) return;
851 
852             const size_t size = static_cast<size_t>(event.GetId() - CommandIds::Menu::ViewSetGridSize1);
853             assert(size < Grid::MaxSize);
854             m_document->grid().setSize(size);
855         }
856 
OnViewMoveCameraToNextPoint(wxCommandEvent & event)857         void MapFrame::OnViewMoveCameraToNextPoint(wxCommandEvent& event) {
858             if (IsBeingDeleted()) return;
859 
860             if (canMoveCameraToNextPoint())
861                 m_mapView->moveCameraToNextTracePoint();
862         }
863 
OnViewMoveCameraToPreviousPoint(wxCommandEvent & event)864         void MapFrame::OnViewMoveCameraToPreviousPoint(wxCommandEvent& event) {
865             if (IsBeingDeleted()) return;
866 
867             if (canMoveCameraToPreviousPoint())
868                 m_mapView->moveCameraToPreviousTracePoint();
869         }
870 
OnViewFocusCameraOnSelection(wxCommandEvent & event)871         void MapFrame::OnViewFocusCameraOnSelection(wxCommandEvent& event) {
872             if (IsBeingDeleted()) return;
873 
874             if (canFocusCamera())
875                 m_mapView->focusCameraOnSelection(true);
876         }
877 
OnViewMoveCameraToPosition(wxCommandEvent & event)878         void MapFrame::OnViewMoveCameraToPosition(wxCommandEvent& event) {
879             if (IsBeingDeleted()) return;
880 
881             wxTextEntryDialog dialog(this, "Enter a position (x y z) for the camera.", "Move Camera", "0.0 0.0 0.0");
882             if (dialog.ShowModal() == wxID_OK) {
883                 const wxString str = dialog.GetValue();
884                 const Vec3 position = Vec3::parse(str.ToStdString());
885                 m_mapView->moveCameraToPosition(position, true);
886             }
887         }
888 
OnViewHideSelectedObjects(wxCommandEvent & event)889         void MapFrame::OnViewHideSelectedObjects(wxCommandEvent& event) {
890             if (IsBeingDeleted()) return;
891 
892             if (canHide())
893                 m_document->hideSelection();
894         }
895 
OnViewIsolateSelectedObjects(wxCommandEvent & event)896         void MapFrame::OnViewIsolateSelectedObjects(wxCommandEvent& event) {
897             if (IsBeingDeleted()) return;
898 
899             if (canIsolate())
900                 m_document->isolate(m_document->selectedNodes().nodes());
901         }
902 
OnViewShowHiddenObjects(wxCommandEvent & event)903         void MapFrame::OnViewShowHiddenObjects(wxCommandEvent& event) {
904             if (IsBeingDeleted()) return;
905 
906             m_document->showAll();
907         }
908 
OnViewSwitchToMapInspector(wxCommandEvent & event)909         void MapFrame::OnViewSwitchToMapInspector(wxCommandEvent& event) {
910             if (IsBeingDeleted()) return;
911 
912             m_inspector->switchToPage(Inspector::InspectorPage_Map);
913         }
914 
OnViewSwitchToEntityInspector(wxCommandEvent & event)915         void MapFrame::OnViewSwitchToEntityInspector(wxCommandEvent& event) {
916             if (IsBeingDeleted()) return;
917 
918             m_inspector->switchToPage(Inspector::InspectorPage_Entity);
919         }
920 
OnViewSwitchToFaceInspector(wxCommandEvent & event)921         void MapFrame::OnViewSwitchToFaceInspector(wxCommandEvent& event) {
922             if (IsBeingDeleted()) return;
923 
924             m_inspector->switchToPage(Inspector::InspectorPage_Face);
925         }
926 
OnViewToggleMaximizeCurrentView(wxCommandEvent & event)927         void MapFrame::OnViewToggleMaximizeCurrentView(wxCommandEvent& event) {
928             if (IsBeingDeleted()) return;
929 
930             m_mapView->toggleMaximizeCurrentView();
931         }
932 
OnViewToggleInfoPanel(wxCommandEvent & event)933         void MapFrame::OnViewToggleInfoPanel(wxCommandEvent& event) {
934             if (IsBeingDeleted()) return;
935 
936             if (m_vSplitter->isMaximized(m_mapView))
937                 m_vSplitter->restore();
938             else
939                 m_vSplitter->maximize(m_mapView);
940         }
941 
OnViewToggleInspector(wxCommandEvent & event)942         void MapFrame::OnViewToggleInspector(wxCommandEvent& event) {
943             if (IsBeingDeleted()) return;
944 
945             if (m_hSplitter->isMaximized(m_vSplitter))
946                 m_hSplitter->restore();
947             else
948                 m_hSplitter->maximize(m_vSplitter);
949         }
950 
OnDebugPrintVertices(wxCommandEvent & event)951         void MapFrame::OnDebugPrintVertices(wxCommandEvent& event) {
952             if (IsBeingDeleted()) return;
953 
954             m_document->printVertices();
955         }
956 
OnDebugCreateBrush(wxCommandEvent & event)957         void MapFrame::OnDebugCreateBrush(wxCommandEvent& event) {
958             if (IsBeingDeleted()) return;
959 
960             wxTextEntryDialog dialog(this, "Enter a list of at least 4 points (x y z) (x y z) ...", "Create Brush", "");
961             if (dialog.ShowModal() == wxID_OK) {
962                 const wxString str = dialog.GetValue();
963                 const Vec3::List positions = Vec3::parseList(str.ToStdString());
964                 m_document->createBrush(positions);
965             }
966         }
967 
OnDebugCopyJSShortcutMap(wxCommandEvent & event)968         void MapFrame::OnDebugCopyJSShortcutMap(wxCommandEvent& event) {
969             if (IsBeingDeleted()) return;
970 
971             OpenClipboard openClipboard;
972             if (wxTheClipboard->IsOpened()) {
973                 const String str = ActionManager::instance().getJSTable();
974                 wxTheClipboard->SetData(new wxTextDataObject(str));
975             }
976 
977         }
978 
OnFlipObjectsHorizontally(wxCommandEvent & event)979         void MapFrame::OnFlipObjectsHorizontally(wxCommandEvent& event) {
980             if (IsBeingDeleted()) return;
981             m_mapView->flipObjects(Math::Direction_Left);
982         }
983 
OnFlipObjectsVertically(wxCommandEvent & event)984         void MapFrame::OnFlipObjectsVertically(wxCommandEvent& event) {
985             if (IsBeingDeleted()) return;
986             m_mapView->flipObjects(Math::Direction_Up);
987         }
988 
OnUpdateUI(wxUpdateUIEvent & event)989         void MapFrame::OnUpdateUI(wxUpdateUIEvent& event) {
990             if (IsBeingDeleted()) return;
991 
992             const ActionManager& actionManager = ActionManager::instance();
993 
994             switch (event.GetId()) {
995                 case wxID_OPEN:
996                 case wxID_SAVE:
997                 case wxID_SAVEAS:
998                 case wxID_CLOSE:
999                 case CommandIds::Menu::FileOpenRecent:
1000                     event.Enable(true);
1001                     break;
1002                 case CommandIds::Menu::FileLoadPointFile:
1003                     event.Enable(canLoadPointFile());
1004                     break;
1005                 case CommandIds::Menu::FileUnloadPointFile:
1006                     event.Enable(canUnloadPointFile());
1007                     break;
1008                 case wxID_UNDO: {
1009                     const ActionMenuItem* item = actionManager.findMenuItem(wxID_UNDO);
1010                     assert(item != NULL);
1011                     if (canUndo()) {
1012                         event.Enable(true);
1013                         event.SetText(item->menuString(m_document->lastCommandName(), m_mapView->viewportHasFocus()));
1014                     } else {
1015                         event.Enable(false);
1016                         event.SetText(item->menuString("", m_mapView->viewportHasFocus()));
1017                     }
1018                     break;
1019                 }
1020                 case wxID_REDO: {
1021                     const ActionMenuItem* item = actionManager.findMenuItem(wxID_REDO);
1022                     if (canRedo()) {
1023                         event.Enable(true);
1024                         event.SetText(item->menuString(m_document->nextCommandName(), m_mapView->viewportHasFocus()));
1025                     } else {
1026                         event.Enable(false);
1027                         event.SetText(item->menuString("", m_mapView->viewportHasFocus()));
1028                     }
1029                     break;
1030                 }
1031                 case CommandIds::Menu::EditRepeat:
1032                 case CommandIds::Menu::EditClearRepeat:
1033                     event.Enable(true);
1034                     break;
1035                 case wxID_CUT:
1036                     event.Enable(canCut());
1037                     break;
1038                 case wxID_COPY:
1039                     event.Enable(canCopy());
1040                     break;
1041                 case wxID_PASTE:
1042                 case CommandIds::Menu::EditPasteAtOriginalPosition:
1043                     event.Enable(canPaste());
1044                     break;
1045                 case wxID_DUPLICATE:
1046                     event.Enable(canDuplicate());
1047                     break;
1048                 case wxID_DELETE:
1049                     event.Enable(canDelete());
1050                     break;
1051                 case CommandIds::Menu::EditSelectAll:
1052                     event.Enable(canSelect());
1053                     break;
1054                 case CommandIds::Menu::EditSelectSiblings:
1055                     event.Enable(canSelectSiblings());
1056                     break;
1057                 case CommandIds::Menu::EditSelectTouching:
1058                 case CommandIds::Menu::EditSelectInside:
1059                     event.Enable(canSelectByBrush());
1060                     break;
1061                 case CommandIds::Menu::EditSelectTall:
1062                     event.Enable(canSelectTall());
1063                     break;
1064                 case CommandIds::Menu::EditSelectByFilePosition:
1065                     event.Enable(canSelect());
1066                     break;
1067                 case CommandIds::Menu::EditSelectNone:
1068                     event.Enable(canDeselect());
1069                     break;
1070                 case CommandIds::Menu::EditGroupSelection:
1071                     event.Enable(canGroup());
1072                     break;
1073                 case CommandIds::Menu::EditUngroupSelection:
1074                     event.Enable(canUngroup());
1075                     break;
1076                 case CommandIds::Menu::EditDeactivateTool:
1077                     event.Check(!m_mapView->anyToolActive());
1078                     event.Enable(true);
1079                     break;
1080                 case CommandIds::Menu::EditToggleCreateComplexBrushTool:
1081                     event.Check(m_mapView->createComplexBrushToolActive());
1082                     event.Enable(m_mapView->canToggleCreateComplexBrushTool());
1083                     break;
1084                 case CommandIds::Menu::EditToggleClipTool:
1085                     event.Check(m_mapView->clipToolActive());
1086                     event.Enable(m_mapView->canToggleClipTool());
1087                     break;
1088                 case CommandIds::Menu::EditToggleRotateObjectsTool:
1089                     event.Check(m_mapView->rotateObjectsToolActive());
1090                     event.Enable(m_mapView->canToggleRotateObjectsTool());
1091                     break;
1092                 case CommandIds::Menu::EditToggleVertexTool:
1093                     event.Check(m_mapView->vertexToolActive());
1094                     event.Enable(m_mapView->canToggleVertexTool());
1095                     break;
1096                 case CommandIds::Menu::EditCsgConvexMerge:
1097                     event.Enable(canDoCsgConvexMerge());
1098                     break;
1099                 case CommandIds::Menu::EditCsgSubtract:
1100                     event.Enable(canDoCsgSubtract());
1101                     break;
1102                 case CommandIds::Menu::EditCsgIntersect:
1103                     event.Enable(canDoCsgIntersect());
1104                     break;
1105                 case CommandIds::Menu::EditSnapVerticesToInteger:
1106                 case CommandIds::Menu::EditSnapVerticesToGrid:
1107                     event.Enable(canSnapVertices());
1108                     break;
1109                 case CommandIds::Menu::EditReplaceTexture:
1110                     event.Enable(true);
1111                     break;
1112                 case CommandIds::Menu::EditToggleTextureLock:
1113                     event.Enable(true);
1114                     event.Check(m_document->textureLock());
1115                     break;
1116                 case CommandIds::Menu::ViewToggleShowGrid:
1117                     event.Enable(true);
1118                     event.Check(m_document->grid().visible());
1119                     break;
1120                 case CommandIds::Menu::ViewToggleSnapToGrid:
1121                     event.Enable(true);
1122                     event.Check(m_document->grid().snap());
1123                     break;
1124                 case CommandIds::Menu::ViewIncGridSize:
1125                     event.Enable(canIncGridSize());
1126                     break;
1127                 case CommandIds::Menu::ViewDecGridSize:
1128                     event.Enable(canDecGridSize());
1129                     break;
1130                 case CommandIds::Menu::ViewSetGridSize1:
1131                     event.Enable(true);
1132                     event.Check(m_document->grid().size() == 0);
1133                     break;
1134                 case CommandIds::Menu::ViewSetGridSize2:
1135                     event.Enable(true);
1136                     event.Check(m_document->grid().size() == 1);
1137                     break;
1138                 case CommandIds::Menu::ViewSetGridSize4:
1139                     event.Enable(true);
1140                     event.Check(m_document->grid().size() == 2);
1141                     break;
1142                 case CommandIds::Menu::ViewSetGridSize8:
1143                     event.Enable(true);
1144                     event.Check(m_document->grid().size() == 3);
1145                     break;
1146                 case CommandIds::Menu::ViewSetGridSize16:
1147                     event.Enable(true);
1148                     event.Check(m_document->grid().size() == 4);
1149                     break;
1150                 case CommandIds::Menu::ViewSetGridSize32:
1151                     event.Enable(true);
1152                     event.Check(m_document->grid().size() == 5);
1153                     break;
1154                 case CommandIds::Menu::ViewSetGridSize64:
1155                     event.Enable(true);
1156                     event.Check(m_document->grid().size() == 6);
1157                     break;
1158                 case CommandIds::Menu::ViewSetGridSize128:
1159                     event.Enable(true);
1160                     event.Check(m_document->grid().size() == 7);
1161                     break;
1162                 case CommandIds::Menu::ViewSetGridSize256:
1163                     event.Enable(true);
1164                     event.Check(m_document->grid().size() == 8);
1165                     break;
1166                 case CommandIds::Menu::ViewMoveCameraToNextPoint:
1167                     event.Enable(canMoveCameraToNextPoint());
1168                     break;
1169                 case CommandIds::Menu::ViewMoveCameraToPreviousPoint:
1170                     event.Enable(canMoveCameraToPreviousPoint());
1171                     break;
1172                 case CommandIds::Menu::ViewFocusCameraOnSelection:
1173                     event.Enable(canFocusCamera());
1174                     break;
1175                 case CommandIds::Menu::ViewMoveCameraToPosition:
1176                     event.Enable(true);
1177                     break;
1178                 case CommandIds::Menu::ViewHideSelection:
1179                     event.Enable(canHide());
1180                     break;
1181                 case CommandIds::Menu::ViewIsolateSelection:
1182                     event.Enable(canIsolate());
1183                     break;
1184                 case CommandIds::Menu::ViewUnhideAll:
1185                     event.Enable(true);
1186                     break;
1187                 case CommandIds::Menu::ViewSwitchToMapInspector:
1188                 case CommandIds::Menu::ViewSwitchToEntityInspector:
1189                 case CommandIds::Menu::ViewSwitchToFaceInspector:
1190                     event.Enable(true);
1191                     break;
1192                 case CommandIds::Menu::ViewToggleMaximizeCurrentView:
1193                     event.Enable(m_mapView->canMaximizeCurrentView());
1194                     event.Check(m_mapView->currentViewMaximized());
1195                     break;
1196                 case CommandIds::Menu::ViewToggleInfoPanel:
1197                     event.Enable(true);
1198                     event.Check(m_vSplitter->isMaximized(m_mapView));
1199                     break;
1200                 case CommandIds::Menu::ViewToggleInspector:
1201                     event.Enable(true);
1202                     event.Check(m_hSplitter->isMaximized(m_vSplitter));
1203                     break;
1204                 case CommandIds::Menu::DebugPrintVertices:
1205                 case CommandIds::Menu::DebugCreateBrush:
1206                 case CommandIds::Menu::DebugCopyJSShortcuts:
1207                     event.Enable(true);
1208                     break;
1209                 case CommandIds::Actions::FlipObjectsHorizontally:
1210                 case CommandIds::Actions::FlipObjectsVertically:
1211                     event.Enable(m_mapView->canFlipObjects());
1212                     break;
1213                 default:
1214                     if (event.GetId() >= CommandIds::Menu::FileRecentDocuments &&
1215                         event.GetId() < CommandIds::Menu::FileRecentDocuments + 10)
1216                         event.Enable(true);
1217                     else
1218                         event.Enable(false);
1219                     break;
1220             }
1221         }
1222 
OnToolBarSetGridSize(wxCommandEvent & event)1223         void MapFrame::OnToolBarSetGridSize(wxCommandEvent& event) {
1224             if (IsBeingDeleted()) return;
1225 
1226             const size_t size = static_cast<size_t>(event.GetSelection());
1227             assert(size < Grid::MaxSize);
1228             m_document->grid().setSize(size);
1229         }
1230 
canLoadPointFile() const1231         bool MapFrame::canLoadPointFile() const {
1232             return m_document->canLoadPointFile();
1233         }
1234 
canUnloadPointFile() const1235         bool MapFrame::canUnloadPointFile() const {
1236             return m_document->isPointFileLoaded();
1237         }
1238 
canUndo() const1239         bool MapFrame::canUndo() const {
1240             return m_document->canUndoLastCommand();
1241         }
1242 
canRedo() const1243         bool MapFrame::canRedo() const {
1244             return m_document->canRedoNextCommand();
1245         }
1246 
canCut() const1247         bool MapFrame::canCut() const {
1248             return canDelete();
1249         }
1250 
canCopy() const1251         bool MapFrame::canCopy() const {
1252             return m_document->hasSelectedNodes() || m_document->hasSelectedBrushFaces();
1253         }
1254 
canPaste() const1255         bool MapFrame::canPaste() const {
1256             OpenClipboard openClipboard;
1257             return wxTheClipboard->IsOpened() && wxTheClipboard->IsSupported(wxDF_TEXT);
1258         }
1259 
canDelete() const1260         bool MapFrame::canDelete() const {
1261             return m_document->hasSelectedNodes() && !m_mapView->anyToolActive();
1262         }
1263 
canDuplicate() const1264         bool MapFrame::canDuplicate() const {
1265             return m_document->hasSelectedNodes() && !m_mapView->clipToolActive() && !m_mapView->vertexToolActive();
1266         }
1267 
canSelectSiblings() const1268         bool MapFrame::canSelectSiblings() const {
1269             return canChangeSelection() && m_document->hasSelectedNodes();
1270         }
1271 
canSelectByBrush() const1272         bool MapFrame::canSelectByBrush() const {
1273             return canChangeSelection() && m_document->selectedNodes().hasOnlyBrushes();
1274         }
1275 
canSelectTall() const1276         bool MapFrame::canSelectTall() const {
1277             return canChangeSelection() && m_document->selectedNodes().hasOnlyBrushes() && m_mapView->canSelectTall();
1278         }
1279 
canSelect() const1280         bool MapFrame::canSelect() const {
1281             return canChangeSelection();
1282         }
1283 
canDeselect() const1284         bool MapFrame::canDeselect() const {
1285             return canChangeSelection() && m_document->hasSelectedNodes();
1286         }
1287 
canChangeSelection() const1288         bool MapFrame::canChangeSelection() const {
1289             return m_document->editorContext().canChangeSelection();
1290         }
1291 
canGroup() const1292         bool MapFrame::canGroup() const {
1293             return m_document->hasSelectedNodes();
1294         }
1295 
canUngroup() const1296         bool MapFrame::canUngroup() const {
1297             return m_document->selectedNodes().hasOnlyGroups();
1298         }
1299 
canHide() const1300         bool MapFrame::canHide() const {
1301             return m_document->hasSelectedNodes();
1302         }
1303 
canIsolate() const1304         bool MapFrame::canIsolate() const {
1305             return m_document->hasSelectedNodes();
1306         }
1307 
canDoCsgConvexMerge() const1308         bool MapFrame::canDoCsgConvexMerge() const {
1309             return (m_document->hasSelectedBrushFaces() && m_document->selectedBrushFaces().size() > 1) ||
1310                    (m_document->selectedNodes().hasOnlyBrushes() && m_document->selectedNodes().brushCount() > 1);
1311         }
1312 
canDoCsgSubtract() const1313         bool MapFrame::canDoCsgSubtract() const {
1314             return m_document->selectedNodes().hasOnlyBrushes() && m_document->selectedNodes().brushCount() > 1;
1315         }
1316 
canDoCsgIntersect() const1317         bool MapFrame::canDoCsgIntersect() const {
1318             return m_document->selectedNodes().hasOnlyBrushes() && m_document->selectedNodes().brushCount() > 1;
1319         }
1320 
canSnapVertices() const1321         bool MapFrame::canSnapVertices() const {
1322             return m_document->selectedNodes().hasOnlyBrushes();
1323         }
1324 
canDecGridSize() const1325         bool MapFrame::canDecGridSize() const {
1326             return m_document->grid().size() > 0;
1327         }
1328 
canIncGridSize() const1329         bool MapFrame::canIncGridSize() const {
1330             return m_document->grid().size() < Grid::MaxSize;
1331         }
1332 
canMoveCameraToNextPoint() const1333         bool MapFrame::canMoveCameraToNextPoint() const {
1334             return m_mapView->canMoveCameraToNextTracePoint();
1335         }
1336 
canMoveCameraToPreviousPoint() const1337         bool MapFrame::canMoveCameraToPreviousPoint() const {
1338             return m_mapView->canMoveCameraToPreviousTracePoint();
1339         }
1340 
canFocusCamera() const1341         bool MapFrame::canFocusCamera() const {
1342             return m_document->hasSelectedNodes();
1343         }
1344 
OnClose(wxCloseEvent & event)1345         void MapFrame::OnClose(wxCloseEvent& event) {
1346             if (IsBeingDeleted()) return;
1347 
1348             if (!IsBeingDeleted()) {
1349                 assert(m_frameManager != NULL);
1350                 if (event.CanVeto() && !confirmOrDiscardChanges())
1351                     event.Veto();
1352                 else
1353                     m_frameManager->removeAndDestroyFrame(this);
1354             }
1355         }
1356 
OnAutosaveTimer(wxTimerEvent & event)1357         void MapFrame::OnAutosaveTimer(wxTimerEvent& event) {
1358             if (IsBeingDeleted()) return;
1359 
1360             m_autosaver->triggerAutosave(logger());
1361         }
1362     }
1363 }
1364