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 "MapViewBase.h"
21 
22 #include "Logger.h"
23 #include "PreferenceManager.h"
24 #include "Preferences.h"
25 #include "Assets/EntityDefinitionManager.h"
26 #include "Model/Brush.h"
27 #include "Model/BrushFace.h"
28 #include "Model/BrushGeometry.h"
29 #include "Model/EditorContext.h"
30 #include "Model/Entity.h"
31 #include "Model/EntityAttributes.h"
32 #include "Model/FindLayerVisitor.h"
33 #include "Model/Group.h"
34 #include "Model/Hit.h"
35 #include "Model/HitAdapter.h"
36 #include "Model/HitQuery.h"
37 #include "Model/Layer.h"
38 #include "Model/PointFile.h"
39 #include "Model/World.h"
40 #include "Renderer/Camera.h"
41 #include "Renderer/Compass.h"
42 #include "Renderer/FontDescriptor.h"
43 #include "Renderer/MapRenderer.h"
44 #include "Renderer/RenderBatch.h"
45 #include "Renderer/RenderContext.h"
46 #include "Renderer/RenderService.h"
47 #include "View/ActionManager.h"
48 #include "View/Animation.h"
49 #include "View/CameraAnimation.h"
50 #include "View/CommandIds.h"
51 #include "View/FlashSelectionAnimation.h"
52 #include "View/FlyModeHelper.h"
53 #include "View/Grid.h"
54 #include "View/MapDocument.h"
55 #include "View/MapViewConfig.h"
56 #include "View/MapViewToolBox.h"
57 #include "View/ToolBoxDropTarget.h"
58 #include "View/ViewUtils.h"
59 #include "View/wxUtils.h"
60 
61 namespace TrenchBroom {
62     namespace View {
63         const wxLongLong MapViewBase::DefaultCameraAnimationDuration = 250;
64 
MapViewBase(wxWindow * parent,Logger * logger,MapDocumentWPtr document,MapViewToolBox & toolBox,Renderer::MapRenderer & renderer,GLContextManager & contextManager)65         MapViewBase::MapViewBase(wxWindow* parent, Logger* logger, MapDocumentWPtr document, MapViewToolBox& toolBox, Renderer::MapRenderer& renderer, GLContextManager& contextManager) :
66         RenderView(parent, contextManager, buildAttribs()),
67         ToolBoxConnector(this),
68         m_logger(logger),
69         m_document(document),
70         m_toolBox(toolBox),
71         m_animationManager(new AnimationManager()),
72         m_renderer(renderer),
73         m_compass(NULL) {
74             setToolBox(toolBox);
75             toolBox.addWindow(this);
76             bindEvents();
77             bindObservers();
78             updateAcceleratorTable(HasFocus());
79         }
80 
setCompass(Renderer::Compass * compass)81         void MapViewBase::setCompass(Renderer::Compass* compass) {
82             m_compass = compass;
83         }
84 
~MapViewBase()85         MapViewBase::~MapViewBase() {
86             m_toolBox.removeWindow(this);
87             unbindObservers();
88             m_animationManager->Delete();
89             delete m_compass;
90         }
91 
bindObservers()92         void MapViewBase::bindObservers() {
93             MapDocumentSPtr document = lock(m_document);
94             document->nodesWereAddedNotifier.addObserver(this, &MapViewBase::nodesDidChange);
95             document->nodesWereRemovedNotifier.addObserver(this, &MapViewBase::nodesDidChange);
96             document->nodesDidChangeNotifier.addObserver(this, &MapViewBase::nodesDidChange);
97             document->nodeVisibilityDidChangeNotifier.addObserver(this, &MapViewBase::nodesDidChange);
98             document->nodeLockingDidChangeNotifier.addObserver(this, &MapViewBase::nodesDidChange);
99             document->commandDoneNotifier.addObserver(this, &MapViewBase::commandDone);
100             document->commandUndoneNotifier.addObserver(this, &MapViewBase::commandUndone);
101             document->selectionDidChangeNotifier.addObserver(this, &MapViewBase::selectionDidChange);
102             document->textureCollectionsDidChangeNotifier.addObserver(this, &MapViewBase::textureCollectionsDidChange);
103             document->entityDefinitionsDidChangeNotifier.addObserver(this, &MapViewBase::entityDefinitionsDidChange);
104             document->modsDidChangeNotifier.addObserver(this, &MapViewBase::modsDidChange);
105             document->editorContextDidChangeNotifier.addObserver(this, &MapViewBase::editorContextDidChange);
106             document->mapViewConfigDidChangeNotifier.addObserver(this, &MapViewBase::mapViewConfigDidChange);
107 			document->documentWasNewedNotifier.addObserver(this, &MapViewBase::documentDidChange);
108 			document->documentWasClearedNotifier.addObserver(this, &MapViewBase::documentDidChange);
109 			document->documentWasLoadedNotifier.addObserver(this, &MapViewBase::documentDidChange);
110 
111             Grid& grid = document->grid();
112             grid.gridDidChangeNotifier.addObserver(this, &MapViewBase::gridDidChange);
113 
114             m_toolBox.toolActivatedNotifier.addObserver(this, &MapViewBase::toolChanged);
115             m_toolBox.toolDeactivatedNotifier.addObserver(this, &MapViewBase::toolChanged);
116 
117             PreferenceManager& prefs = PreferenceManager::instance();
118             prefs.preferenceDidChangeNotifier.addObserver(this, &MapViewBase::preferenceDidChange);
119         }
120 
unbindObservers()121         void MapViewBase::unbindObservers() {
122             if (!expired(m_document)) {
123                 MapDocumentSPtr document = lock(m_document);
124                 document->nodesWereAddedNotifier.removeObserver(this, &MapViewBase::nodesDidChange);
125                 document->nodesWereRemovedNotifier.removeObserver(this, &MapViewBase::nodesDidChange);
126                 document->nodesDidChangeNotifier.removeObserver(this, &MapViewBase::nodesDidChange);
127                 document->nodeVisibilityDidChangeNotifier.removeObserver(this, &MapViewBase::nodesDidChange);
128                 document->nodeLockingDidChangeNotifier.removeObserver(this, &MapViewBase::nodesDidChange);
129                 document->commandDoneNotifier.removeObserver(this, &MapViewBase::commandDone);
130                 document->commandUndoneNotifier.removeObserver(this, &MapViewBase::commandUndone);
131                 document->selectionDidChangeNotifier.removeObserver(this, &MapViewBase::selectionDidChange);
132                 document->textureCollectionsDidChangeNotifier.removeObserver(this, &MapViewBase::textureCollectionsDidChange);
133                 document->entityDefinitionsDidChangeNotifier.removeObserver(this, &MapViewBase::entityDefinitionsDidChange);
134                 document->modsDidChangeNotifier.removeObserver(this, &MapViewBase::modsDidChange);
135                 document->editorContextDidChangeNotifier.removeObserver(this, &MapViewBase::editorContextDidChange);
136                 document->mapViewConfigDidChangeNotifier.removeObserver(this, &MapViewBase::mapViewConfigDidChange);
137 				document->documentWasNewedNotifier.removeObserver(this, &MapViewBase::documentDidChange);
138 				document->documentWasClearedNotifier.removeObserver(this, &MapViewBase::documentDidChange);
139 				document->documentWasLoadedNotifier.removeObserver(this, &MapViewBase::documentDidChange);
140 
141                 Grid& grid = document->grid();
142                 grid.gridDidChangeNotifier.removeObserver(this, &MapViewBase::gridDidChange);
143             }
144 
145             m_toolBox.toolActivatedNotifier.removeObserver(this, &MapViewBase::toolChanged);
146             m_toolBox.toolDeactivatedNotifier.removeObserver(this, &MapViewBase::toolChanged);
147 
148             PreferenceManager& prefs = PreferenceManager::instance();
149             prefs.preferenceDidChangeNotifier.removeObserver(this, &MapViewBase::preferenceDidChange);
150         }
151 
nodesDidChange(const Model::NodeList & nodes)152         void MapViewBase::nodesDidChange(const Model::NodeList& nodes) {
153             updatePickResult();
154             Refresh();
155         }
156 
toolChanged(Tool * tool)157         void MapViewBase::toolChanged(Tool* tool) {
158             updatePickResult();
159             updateAcceleratorTable(HasFocus());
160             Refresh();
161         }
162 
commandDone(Command::Ptr command)163         void MapViewBase::commandDone(Command::Ptr command) {
164             updatePickResult();
165             Refresh();
166         }
167 
commandUndone(UndoableCommand::Ptr command)168         void MapViewBase::commandUndone(UndoableCommand::Ptr command) {
169             updatePickResult();
170             Refresh();
171         }
172 
selectionDidChange(const Selection & selection)173         void MapViewBase::selectionDidChange(const Selection& selection) {
174             updateAcceleratorTable(HasFocus());
175         }
176 
textureCollectionsDidChange()177         void MapViewBase::textureCollectionsDidChange() {
178             Refresh();
179         }
180 
entityDefinitionsDidChange()181         void MapViewBase::entityDefinitionsDidChange() {
182             Refresh();
183         }
184 
modsDidChange()185         void MapViewBase::modsDidChange() {
186             Refresh();
187         }
188 
editorContextDidChange()189         void MapViewBase::editorContextDidChange() {
190             Refresh();
191         }
192 
mapViewConfigDidChange()193         void MapViewBase::mapViewConfigDidChange() {
194             Refresh();
195         }
196 
gridDidChange()197         void MapViewBase::gridDidChange() {
198             Refresh();
199         }
200 
preferenceDidChange(const IO::Path & path)201         void MapViewBase::preferenceDidChange(const IO::Path& path) {
202             Refresh();
203         }
204 
documentDidChange(MapDocument * document)205 		void MapViewBase::documentDidChange(MapDocument* document) {
206 			updatePickResult();
207 			Refresh();
208 		}
209 
bindEvents()210 		void MapViewBase::bindEvents() {
211             Bind(wxEVT_SET_FOCUS, &MapViewBase::OnSetFocus, this);
212             Bind(wxEVT_KILL_FOCUS, &MapViewBase::OnKillFocus, this);
213 
214             Bind(wxEVT_MENU, &MapViewBase::OnToggleClipSide,               this, CommandIds::Actions::ToggleClipSide);
215             Bind(wxEVT_MENU, &MapViewBase::OnPerformClip,                  this, CommandIds::Actions::PerformClip);
216             Bind(wxEVT_MENU, &MapViewBase::OnRemoveLastClipPoint,          this, CommandIds::Actions::RemoveLastClipPoint);
217 
218             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesForward,          this, CommandIds::Actions::MoveVerticesForward);
219             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesBackward,         this, CommandIds::Actions::MoveVerticesBackward);
220             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesLeft,             this, CommandIds::Actions::MoveVerticesLeft);
221             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesRight,            this, CommandIds::Actions::MoveVerticesRight);
222             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesUp,               this, CommandIds::Actions::MoveVerticesUp);
223             Bind(wxEVT_MENU, &MapViewBase::OnMoveVerticesDown,             this, CommandIds::Actions::MoveVerticesDown);
224 
225             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsForward,           this, CommandIds::Actions::MoveObjectsForward);
226             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsBackward,          this, CommandIds::Actions::MoveObjectsBackward);
227             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsLeft,              this, CommandIds::Actions::MoveObjectsLeft);
228             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsRight,             this, CommandIds::Actions::MoveObjectsRight);
229             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsUp,                this, CommandIds::Actions::MoveObjectsUp);
230             Bind(wxEVT_MENU, &MapViewBase::OnMoveObjectsDown,              this, CommandIds::Actions::MoveObjectsDown);
231 
232             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsForward,      this, CommandIds::Actions::DuplicateObjectsForward);
233             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsBackward,     this, CommandIds::Actions::DuplicateObjectsBackward);
234             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsLeft,         this, CommandIds::Actions::DuplicateObjectsLeft);
235             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsRight,        this, CommandIds::Actions::DuplicateObjectsRight);
236             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsUp,           this, CommandIds::Actions::DuplicateObjectsUp);
237             Bind(wxEVT_MENU, &MapViewBase::OnDuplicateObjectsDown,         this, CommandIds::Actions::DuplicateObjectsDown);
238 
239             Bind(wxEVT_MENU, &MapViewBase::OnRollObjectsCW,                this, CommandIds::Actions::RollObjectsCW);
240             Bind(wxEVT_MENU, &MapViewBase::OnRollObjectsCCW,               this, CommandIds::Actions::RollObjectsCCW);
241             Bind(wxEVT_MENU, &MapViewBase::OnPitchObjectsCW,               this, CommandIds::Actions::PitchObjectsCW);
242             Bind(wxEVT_MENU, &MapViewBase::OnPitchObjectsCCW,              this, CommandIds::Actions::PitchObjectsCCW);
243             Bind(wxEVT_MENU, &MapViewBase::OnYawObjectsCW,                 this, CommandIds::Actions::YawObjectsCW);
244             Bind(wxEVT_MENU, &MapViewBase::OnYawObjectsCCW,                this, CommandIds::Actions::YawObjectsCCW);
245 
246             Bind(wxEVT_MENU, &MapViewBase::OnFlipObjectsH,                 this, CommandIds::Actions::FlipObjectsHorizontally);
247             Bind(wxEVT_MENU, &MapViewBase::OnFlipObjectsV,                 this, CommandIds::Actions::FlipObjectsVertically);
248 
249             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterForward,    this, CommandIds::Actions::MoveRotationCenterForward);
250             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterBackward,   this, CommandIds::Actions::MoveRotationCenterBackward);
251             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterLeft,       this, CommandIds::Actions::MoveRotationCenterLeft);
252             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterRight,      this, CommandIds::Actions::MoveRotationCenterRight);
253             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterUp,         this, CommandIds::Actions::MoveRotationCenterUp);
254             Bind(wxEVT_MENU, &MapViewBase::OnMoveRotationCenterDown,       this, CommandIds::Actions::MoveRotationCenterDown);
255 
256             Bind(wxEVT_MENU, &MapViewBase::OnCancel,                       this, CommandIds::Actions::Cancel);
257             Bind(wxEVT_MENU, &MapViewBase::OnDeactivateTool,               this, CommandIds::Actions::DeactivateTool);
258 
259             Bind(wxEVT_MENU, &MapViewBase::OnGroupSelectedObjects,         this, CommandIds::MapViewPopupMenu::GroupObjects);
260             Bind(wxEVT_MENU, &MapViewBase::OnUngroupSelectedObjects,       this, CommandIds::MapViewPopupMenu::UngroupObjects);
261             Bind(wxEVT_MENU, &MapViewBase::OnRenameGroups,                 this, CommandIds::MapViewPopupMenu::RenameGroups);
262             Bind(wxEVT_MENU, &MapViewBase::OnAddObjectsToGroup,            this, CommandIds::MapViewPopupMenu::AddObjectsToGroup);
263             Bind(wxEVT_MENU, &MapViewBase::OnRemoveObjectsFromGroup,       this, CommandIds::MapViewPopupMenu::RemoveObjectsFromGroup);
264             Bind(wxEVT_MENU, &MapViewBase::OnMoveBrushesTo,                this, CommandIds::MapViewPopupMenu::MoveBrushesToEntity);
265             Bind(wxEVT_MENU, &MapViewBase::OnMoveBrushesTo,                this, CommandIds::MapViewPopupMenu::MoveBrushesToWorld);
266             Bind(wxEVT_MENU, &MapViewBase::OnCreatePointEntity,            this, CommandIds::MapViewPopupMenu::LowestPointEntityItem, CommandIds::MapViewPopupMenu::HighestPointEntityItem);
267             Bind(wxEVT_MENU, &MapViewBase::OnCreateBrushEntity,            this, CommandIds::MapViewPopupMenu::LowestBrushEntityItem, CommandIds::MapViewPopupMenu::HighestBrushEntityItem);
268 
269             Bind(wxEVT_UPDATE_UI, &MapViewBase::OnUpdatePopupMenuItem,     this, CommandIds::MapViewPopupMenu::GroupObjects);
270             Bind(wxEVT_UPDATE_UI, &MapViewBase::OnUpdatePopupMenuItem,     this, CommandIds::MapViewPopupMenu::UngroupObjects);
271             Bind(wxEVT_UPDATE_UI, &MapViewBase::OnUpdatePopupMenuItem,     this, CommandIds::MapViewPopupMenu::RenameGroups);
272             Bind(wxEVT_UPDATE_UI, &MapViewBase::OnUpdatePopupMenuItem,     this, CommandIds::MapViewPopupMenu::LowestPointEntityItem, CommandIds::MapViewPopupMenu::HighestPointEntityItem);
273             Bind(wxEVT_UPDATE_UI, &MapViewBase::OnUpdatePopupMenuItem,     this, CommandIds::MapViewPopupMenu::LowestBrushEntityItem, CommandIds::MapViewPopupMenu::HighestBrushEntityItem);
274 
275             wxFrame* frame = findFrame(this);
276             frame->Bind(wxEVT_ACTIVATE, &MapViewBase::OnActivateFrame, this);
277         }
278 
OnMoveObjectsForward(wxCommandEvent & event)279         void MapViewBase::OnMoveObjectsForward(wxCommandEvent& event) {
280             if (IsBeingDeleted()) return;
281 
282             moveObjects(Math::Direction_Forward);
283         }
284 
OnMoveObjectsBackward(wxCommandEvent & event)285         void MapViewBase::OnMoveObjectsBackward(wxCommandEvent& event) {
286             if (IsBeingDeleted()) return;
287 
288             moveObjects(Math::Direction_Backward);
289         }
290 
OnMoveObjectsLeft(wxCommandEvent & event)291         void MapViewBase::OnMoveObjectsLeft(wxCommandEvent& event) {
292             if (IsBeingDeleted()) return;
293 
294             moveObjects(Math::Direction_Left);
295         }
296 
OnMoveObjectsRight(wxCommandEvent & event)297         void MapViewBase::OnMoveObjectsRight(wxCommandEvent& event) {
298             if (IsBeingDeleted()) return;
299 
300             moveObjects(Math::Direction_Right);
301         }
302 
OnMoveObjectsUp(wxCommandEvent & event)303         void MapViewBase::OnMoveObjectsUp(wxCommandEvent& event) {
304             if (IsBeingDeleted()) return;
305 
306             moveObjects(Math::Direction_Up);
307         }
308 
OnMoveObjectsDown(wxCommandEvent & event)309         void MapViewBase::OnMoveObjectsDown(wxCommandEvent& event) {
310             if (IsBeingDeleted()) return;
311 
312             moveObjects(Math::Direction_Down);
313         }
314 
OnDuplicateObjectsForward(wxCommandEvent & event)315         void MapViewBase::OnDuplicateObjectsForward(wxCommandEvent& event) {
316             if (IsBeingDeleted()) return;
317 
318             duplicateAndMoveObjects(Math::Direction_Forward);
319         }
320 
OnDuplicateObjectsBackward(wxCommandEvent & event)321         void MapViewBase::OnDuplicateObjectsBackward(wxCommandEvent& event) {
322             if (IsBeingDeleted()) return;
323 
324             duplicateAndMoveObjects(Math::Direction_Backward);
325         }
326 
OnDuplicateObjectsLeft(wxCommandEvent & event)327         void MapViewBase::OnDuplicateObjectsLeft(wxCommandEvent& event) {
328             if (IsBeingDeleted()) return;
329 
330             duplicateAndMoveObjects(Math::Direction_Left);
331         }
332 
OnDuplicateObjectsRight(wxCommandEvent & event)333         void MapViewBase::OnDuplicateObjectsRight(wxCommandEvent& event) {
334             if (IsBeingDeleted()) return;
335 
336             duplicateAndMoveObjects(Math::Direction_Right);
337         }
338 
OnDuplicateObjectsUp(wxCommandEvent & event)339         void MapViewBase::OnDuplicateObjectsUp(wxCommandEvent& event) {
340             if (IsBeingDeleted()) return;
341 
342             duplicateAndMoveObjects(Math::Direction_Up);
343         }
344 
OnDuplicateObjectsDown(wxCommandEvent & event)345         void MapViewBase::OnDuplicateObjectsDown(wxCommandEvent& event) {
346             if (IsBeingDeleted()) return;
347 
348             duplicateAndMoveObjects(Math::Direction_Down);
349         }
350 
OnRollObjectsCW(wxCommandEvent & event)351         void MapViewBase::OnRollObjectsCW(wxCommandEvent& event) {
352             if (IsBeingDeleted()) return;
353 
354             rotateObjects(Math::RotationAxis_Roll, true);
355         }
356 
OnRollObjectsCCW(wxCommandEvent & event)357         void MapViewBase::OnRollObjectsCCW(wxCommandEvent& event) {
358             if (IsBeingDeleted()) return;
359 
360             rotateObjects(Math::RotationAxis_Roll, false);
361         }
362 
OnPitchObjectsCW(wxCommandEvent & event)363         void MapViewBase::OnPitchObjectsCW(wxCommandEvent& event) {
364             if (IsBeingDeleted()) return;
365 
366             rotateObjects(Math::RotationAxis_Pitch, true);
367         }
368 
OnPitchObjectsCCW(wxCommandEvent & event)369         void MapViewBase::OnPitchObjectsCCW(wxCommandEvent& event) {
370             if (IsBeingDeleted()) return;
371 
372             rotateObjects(Math::RotationAxis_Pitch, false);
373         }
374 
OnYawObjectsCW(wxCommandEvent & event)375         void MapViewBase::OnYawObjectsCW(wxCommandEvent& event) {
376             if (IsBeingDeleted()) return;
377 
378             rotateObjects(Math::RotationAxis_Yaw, true);
379         }
380 
OnYawObjectsCCW(wxCommandEvent & event)381         void MapViewBase::OnYawObjectsCCW(wxCommandEvent& event) {
382             if (IsBeingDeleted()) return;
383 
384             rotateObjects(Math::RotationAxis_Yaw, false);
385         }
386 
OnFlipObjectsH(wxCommandEvent & event)387         void MapViewBase::OnFlipObjectsH(wxCommandEvent& event) {
388             if (IsBeingDeleted()) return;
389 
390             flipObjects(Math::Direction_Left);
391         }
392 
OnFlipObjectsV(wxCommandEvent & event)393         void MapViewBase::OnFlipObjectsV(wxCommandEvent& event) {
394             if (IsBeingDeleted()) return;
395 
396             flipObjects(Math::Direction_Up);
397         }
398 
duplicateAndMoveObjects(const Math::Direction direction)399         void MapViewBase::duplicateAndMoveObjects(const Math::Direction direction) {
400             Transaction transaction(m_document);
401             duplicateObjects();
402             moveObjects(direction);
403         }
404 
duplicateObjects()405         void MapViewBase::duplicateObjects() {
406             MapDocumentSPtr document = lock(m_document);
407             if (!document->hasSelectedNodes())
408                 return;
409 
410             document->duplicateObjects();
411         }
412 
moveObjects(const Math::Direction direction)413         void MapViewBase::moveObjects(const Math::Direction direction) {
414             MapDocumentSPtr document = lock(m_document);
415             if (!document->hasSelectedNodes())
416                 return;
417 
418             const Grid& grid = document->grid();
419             const Vec3 delta = moveDirection(direction) * static_cast<FloatType>(grid.actualSize());
420             document->translateObjects(delta);
421         }
422 
moveDirection(const Math::Direction direction) const423         Vec3 MapViewBase::moveDirection(const Math::Direction direction) const {
424             return doGetMoveDirection(direction);
425         }
426 
rotateObjects(const Math::RotationAxis axisSpec,const bool clockwise)427         void MapViewBase::rotateObjects(const Math::RotationAxis axisSpec, const bool clockwise) {
428             MapDocumentSPtr document = lock(m_document);
429             if (!document->hasSelectedNodes())
430                 return;
431 
432             const Vec3 axis = rotationAxis(axisSpec, clockwise);
433             const double angle = m_toolBox.rotateObjectsToolActive() ? std::abs(m_toolBox.rotateToolAngle()) : Math::C::piOverTwo();
434 
435             const Grid& grid = document->grid();
436             const Vec3 center = m_toolBox.rotateObjectsToolActive() ? m_toolBox.rotateToolCenter() : grid.referencePoint(document->selectionBounds());
437 
438             document->rotateObjects(center, axis, angle);
439         }
440 
rotationAxis(const Math::RotationAxis axisSpec,const bool clockwise) const441         Vec3 MapViewBase::rotationAxis(const Math::RotationAxis axisSpec, const bool clockwise) const {
442             Vec3 axis;
443             switch (axisSpec) {
444                 case Math::RotationAxis_Roll:
445                     axis = -moveDirection(Math::Direction_Forward);
446                     break;
447                 case Math::RotationAxis_Pitch:
448                     axis = moveDirection(Math::Direction_Right);
449                     break;
450                 case Math::RotationAxis_Yaw:
451                     axis = moveDirection(Math::Direction_Up);
452                     break;
453                     switchDefault()
454             }
455 
456             if (clockwise)
457                 axis = -axis;
458             return axis;
459         }
460 
OnToggleRotateObjectsTool(wxCommandEvent & event)461         void MapViewBase::OnToggleRotateObjectsTool(wxCommandEvent& event) {
462             if (IsBeingDeleted()) return;
463 
464             m_toolBox.toggleRotateObjectsTool();
465         }
466 
OnMoveRotationCenterForward(wxCommandEvent & event)467         void MapViewBase::OnMoveRotationCenterForward(wxCommandEvent& event) {
468             if (IsBeingDeleted()) return;
469 
470             moveRotationCenter(Math::Direction_Forward);
471         }
472 
OnMoveRotationCenterBackward(wxCommandEvent & event)473         void MapViewBase::OnMoveRotationCenterBackward(wxCommandEvent& event) {
474             if (IsBeingDeleted()) return;
475 
476             moveRotationCenter(Math::Direction_Backward);
477         }
478 
OnMoveRotationCenterLeft(wxCommandEvent & event)479         void MapViewBase::OnMoveRotationCenterLeft(wxCommandEvent& event) {
480             if (IsBeingDeleted()) return;
481 
482             moveRotationCenter(Math::Direction_Left);
483         }
484 
OnMoveRotationCenterRight(wxCommandEvent & event)485         void MapViewBase::OnMoveRotationCenterRight(wxCommandEvent& event) {
486             if (IsBeingDeleted()) return;
487 
488             moveRotationCenter(Math::Direction_Right);
489         }
490 
OnMoveRotationCenterUp(wxCommandEvent & event)491         void MapViewBase::OnMoveRotationCenterUp(wxCommandEvent& event) {
492             if (IsBeingDeleted()) return;
493 
494             moveRotationCenter(Math::Direction_Up);
495         }
496 
OnMoveRotationCenterDown(wxCommandEvent & event)497         void MapViewBase::OnMoveRotationCenterDown(wxCommandEvent& event) {
498             if (IsBeingDeleted()) return;
499 
500             moveRotationCenter(Math::Direction_Down);
501         }
502 
moveRotationCenter(const Math::Direction direction)503         void MapViewBase::moveRotationCenter(const Math::Direction direction) {
504             MapDocumentSPtr document = lock(m_document);
505             const Grid& grid = document->grid();
506             const Vec3 delta = moveDirection(direction) * static_cast<FloatType>(grid.actualSize());
507             m_toolBox.moveRotationCenter(delta);
508             Refresh();
509         }
510 
OnToggleClipSide(wxCommandEvent & event)511         void MapViewBase::OnToggleClipSide(wxCommandEvent& event) {
512             if (IsBeingDeleted()) return;
513 
514             m_toolBox.toggleClipSide();
515         }
516 
OnPerformClip(wxCommandEvent & event)517         void MapViewBase::OnPerformClip(wxCommandEvent& event) {
518             if (IsBeingDeleted()) return;
519 
520             m_toolBox.performClip();
521         }
522 
OnRemoveLastClipPoint(wxCommandEvent & event)523         void MapViewBase::OnRemoveLastClipPoint(wxCommandEvent& event) {
524             if (IsBeingDeleted()) return;
525 
526             m_toolBox.removeLastClipPoint();
527         }
528 
OnMoveVerticesForward(wxCommandEvent & event)529         void MapViewBase::OnMoveVerticesForward(wxCommandEvent& event) {
530             if (IsBeingDeleted()) return;
531 
532             moveVertices(Math::Direction_Forward);
533         }
534 
OnMoveVerticesBackward(wxCommandEvent & event)535         void MapViewBase::OnMoveVerticesBackward(wxCommandEvent& event) {
536             if (IsBeingDeleted()) return;
537 
538             moveVertices(Math::Direction_Backward);
539         }
540 
OnMoveVerticesLeft(wxCommandEvent & event)541         void MapViewBase::OnMoveVerticesLeft(wxCommandEvent& event) {
542             if (IsBeingDeleted()) return;
543 
544             moveVertices(Math::Direction_Left);
545         }
546 
OnMoveVerticesRight(wxCommandEvent & event)547         void MapViewBase::OnMoveVerticesRight(wxCommandEvent& event) {
548             if (IsBeingDeleted()) return;
549 
550             moveVertices(Math::Direction_Right);
551         }
552 
OnMoveVerticesUp(wxCommandEvent & event)553         void MapViewBase::OnMoveVerticesUp(wxCommandEvent& event) {
554             if (IsBeingDeleted()) return;
555 
556             moveVertices(Math::Direction_Up);
557         }
558 
OnMoveVerticesDown(wxCommandEvent & event)559         void MapViewBase::OnMoveVerticesDown(wxCommandEvent& event) {
560             if (IsBeingDeleted()) return;
561 
562             moveVertices(Math::Direction_Down);
563         }
564 
moveVertices(const Math::Direction direction)565         void MapViewBase::moveVertices(const Math::Direction direction) {
566             MapDocumentSPtr document = lock(m_document);
567             const Grid& grid = document->grid();
568             const Vec3 delta = moveDirection(direction) * static_cast<FloatType>(grid.actualSize());
569             m_toolBox.moveVertices(delta);
570         }
571 
OnCancel(wxCommandEvent & event)572         void MapViewBase::OnCancel(wxCommandEvent& event) {
573             if (IsBeingDeleted()) return;
574 
575             if (MapViewBase::cancel())
576                 return;
577             if (ToolBoxConnector::cancel())
578                 return;
579 
580             MapDocumentSPtr document = lock(m_document);
581             if (document->hasSelection()) {
582                 document->deselectAll();
583             } else if (document->currentGroup() != NULL) {
584                 document->closeGroup();
585             }
586         }
587 
cancel()588         bool MapViewBase::cancel() {
589             return doCancel();
590         }
591 
OnDeactivateTool(wxCommandEvent & event)592         void MapViewBase::OnDeactivateTool(wxCommandEvent& event) {
593             m_toolBox.deactivateAllTools();
594         }
595 
OnGroupSelectedObjects(wxCommandEvent & event)596         void MapViewBase::OnGroupSelectedObjects(wxCommandEvent& event) {
597             if (IsBeingDeleted()) return;
598 
599             MapDocumentSPtr document = lock(m_document);
600             if (document->hasSelectedNodes()) {
601                 const String name = queryGroupName(this);
602                 if (!name.empty())
603                     document->groupSelection(name);
604             }
605         }
606 
OnUngroupSelectedObjects(wxCommandEvent & event)607         void MapViewBase::OnUngroupSelectedObjects(wxCommandEvent& event) {
608             if (IsBeingDeleted()) return;
609 
610             MapDocumentSPtr document = lock(m_document);
611             if (document->hasSelectedNodes() && document->selectedNodes().hasOnlyGroups())
612                 document->ungroupSelection();
613         }
614 
OnRenameGroups(wxCommandEvent & event)615         void MapViewBase::OnRenameGroups(wxCommandEvent& event) {
616             if (IsBeingDeleted()) return;
617 
618             MapDocumentSPtr document = lock(m_document);
619             assert(document->selectedNodes().hasOnlyGroups());
620             const String name = queryGroupName(this);
621             if (!name.empty())
622                 document->renameGroups(name);
623         }
624 
OnCreatePointEntity(wxCommandEvent & event)625         void MapViewBase::OnCreatePointEntity(wxCommandEvent& event) {
626             if (IsBeingDeleted()) return;
627 
628             MapDocumentSPtr document = lock(m_document);
629             const size_t index = static_cast<size_t>(event.GetId() - CommandIds::MapViewPopupMenu::LowestPointEntityItem);
630             const Assets::EntityDefinition* definition = findEntityDefinition(Assets::EntityDefinition::Type_PointEntity, index);
631             assert(definition != NULL);
632             assert(definition->type() == Assets::EntityDefinition::Type_PointEntity);
633             createPointEntity(static_cast<const Assets::PointEntityDefinition*>(definition));
634         }
635 
OnCreateBrushEntity(wxCommandEvent & event)636         void MapViewBase::OnCreateBrushEntity(wxCommandEvent& event) {
637             if (IsBeingDeleted()) return;
638 
639             MapDocumentSPtr document = lock(m_document);
640             const size_t index = static_cast<size_t>(event.GetId() - CommandIds::MapViewPopupMenu::LowestBrushEntityItem);
641             const Assets::EntityDefinition* definition = findEntityDefinition(Assets::EntityDefinition::Type_BrushEntity, index);
642             assert(definition != NULL);
643             assert(definition->type() == Assets::EntityDefinition::Type_BrushEntity);
644             createBrushEntity(static_cast<const Assets::BrushEntityDefinition*>(definition));
645         }
646 
findEntityDefinition(const Assets::EntityDefinition::Type type,const size_t index) const647         Assets::EntityDefinition* MapViewBase::findEntityDefinition(const Assets::EntityDefinition::Type type, const size_t index) const {
648             size_t count = 0;
649             const Assets::EntityDefinitionGroup::List& groups = lock(m_document)->entityDefinitionManager().groups();
650             Assets::EntityDefinitionGroup::List::const_iterator groupIt, groupEnd;
651             for (groupIt = groups.begin(), groupEnd = groups.end(); groupIt != groupEnd; ++groupIt) {
652                 const Assets::EntityDefinitionGroup& group = *groupIt;
653                 const Assets::EntityDefinitionList definitions = group.definitions(type, Assets::EntityDefinition::Name);
654                 if (index < count + definitions.size())
655                     return definitions[index - count];
656                 count += definitions.size();
657             }
658             return NULL;
659         }
660 
createPointEntity(const Assets::PointEntityDefinition * definition)661         void MapViewBase::createPointEntity(const Assets::PointEntityDefinition* definition) {
662             assert(definition != NULL);
663 
664             MapDocumentSPtr document = lock(m_document);
665             Model::Entity* entity = document->world()->createEntity();
666             entity->addOrUpdateAttribute(Model::AttributeNames::Classname, definition->name());
667 
668             StringStream name;
669             name << "Create " << definition->name();
670 
671             const Vec3 delta = doComputePointEntityPosition(definition->bounds());
672 
673             const Transaction transaction(document, name.str());
674             document->deselectAll();
675             document->addNode(entity, document->currentParent());
676             document->select(entity);
677             document->translateObjects(delta);
678         }
679 
createBrushEntity(const Assets::BrushEntityDefinition * definition)680         void MapViewBase::createBrushEntity(const Assets::BrushEntityDefinition* definition) {
681             assert(definition != NULL);
682 
683             MapDocumentSPtr document = lock(m_document);
684 
685             const Model::BrushList brushes = document->selectedNodes().brushes();
686             assert(!brushes.empty());
687 
688             // if all brushes belong to the same entity, and that entity is not worldspawn, copy its properties
689             Model::BrushList::const_iterator it = brushes.begin();
690             Model::BrushList::const_iterator end = brushes.end();
691             Model::AttributableNode* entityTemplate = (*it++)->entity();
692             while (it != end && entityTemplate != NULL)
693                 if ((*it++)->parent() != entityTemplate)
694                     entityTemplate = NULL;
695 
696             Model::Entity* entity = document->world()->createEntity();
697             if (entityTemplate != NULL && entityTemplate != document->world())
698                 entity->setAttributes(entityTemplate->attributes());
699             entity->addOrUpdateAttribute(Model::AttributeNames::Classname, definition->name());
700 
701             StringStream name;
702             name << "Create " << definition->name();
703 
704             const Model::NodeList nodes(brushes.begin(), brushes.end());
705 
706             const Transaction transaction(document, name.str());
707             document->deselectAll();
708             document->addNode(entity, document->currentParent());
709             document->reparentNodes(entity, nodes);
710             document->select(nodes);
711         }
712 
canCreateBrushEntity()713         bool MapViewBase::canCreateBrushEntity() {
714             MapDocumentSPtr document = lock(m_document);
715             return document->selectedNodes().hasOnlyBrushes();
716         }
717 
OnSetFocus(wxFocusEvent & event)718         void MapViewBase::OnSetFocus(wxFocusEvent& event) {
719             if (IsBeingDeleted()) return;
720 
721             updateAcceleratorTable(true);
722             event.Skip();
723         }
724 
OnKillFocus(wxFocusEvent & event)725         void MapViewBase::OnKillFocus(wxFocusEvent& event) {
726             if (IsBeingDeleted()) return;
727 
728             updateAcceleratorTable(false);
729             event.Skip();
730         }
731 
OnActivateFrame(wxActivateEvent & event)732         void MapViewBase::OnActivateFrame(wxActivateEvent& event) {
733             if (IsBeingDeleted()) return;
734 
735             if (event.GetActive())
736                 updateLastActivation();
737             event.Skip();
738         }
739 
updateAcceleratorTable()740         void MapViewBase::updateAcceleratorTable() {
741             updateAcceleratorTable(HasFocus());
742         }
743 
updateAcceleratorTable(const bool hasFocus)744         void MapViewBase::updateAcceleratorTable(const bool hasFocus) {
745             if (hasFocus) {
746                 const wxAcceleratorTable acceleratorTable = doCreateAccelerationTable(actionContext());
747                 SetAcceleratorTable(acceleratorTable);
748             } else {
749                 SetAcceleratorTable(wxNullAcceleratorTable);
750             }
751         }
752 
actionContext() const753         ActionContext MapViewBase::actionContext() const {
754             const ActionContext derivedContext = doGetActionContext();
755             if (derivedContext != ActionContext_Default)
756                 return derivedContext;
757 
758             if (m_toolBox.createComplexBrushToolActive())
759                 return ActionContext_CreateComplexBrushTool;
760             if (m_toolBox.clipToolActive())
761                 return ActionContext_ClipTool;
762             if (m_toolBox.vertexToolActive())
763                 return ActionContext_VertexTool;
764             if (m_toolBox.rotateObjectsToolActive())
765                 return ActionContext_RotateTool;
766 
767             MapDocumentSPtr document = lock(m_document);
768             if (document->hasSelectedNodes())
769                 return ActionContext_NodeSelection;
770             if (document->hasSelectedBrushFaces())
771                 return ActionContext_FaceSelection;
772             return ActionContext_Default;
773         }
774 
doFlashSelection()775         void MapViewBase::doFlashSelection() {
776             FlashSelectionAnimation* animation = new FlashSelectionAnimation(m_renderer, *this, 180);
777             m_animationManager->runAnimation(animation, true);
778         }
779 
doGetIsCurrent() const780         bool MapViewBase::doGetIsCurrent() const {
781             return HasFocus();
782         }
783 
doSetToolBoxDropTarget()784         void MapViewBase::doSetToolBoxDropTarget() {
785             SetDropTarget(new ToolBoxDropTarget(this));
786         }
787 
doClearDropTarget()788         void MapViewBase::doClearDropTarget() {
789             SetDropTarget(NULL);
790         }
791 
doCanFlipObjects() const792         bool MapViewBase::doCanFlipObjects() const {
793             MapDocumentSPtr document = lock(m_document);
794             return !m_toolBox.anyToolActive() && document->hasSelectedNodes();
795         }
796 
doFlipObjects(const Math::Direction direction)797         void MapViewBase::doFlipObjects(const Math::Direction direction) {
798             MapDocumentSPtr document = lock(m_document);
799             if (!document->hasSelectedNodes())
800                 return;
801 
802             const Grid& grid = document->grid();
803             const Vec3 center = grid.referencePoint(document->selectionBounds());
804             const Math::Axis::Type axis = moveDirection(direction).firstComponent();
805 
806             document->flipObjects(center, axis);
807         }
808 
doInitializeGL(const bool firstInitialization)809         void MapViewBase::doInitializeGL(const bool firstInitialization) {
810             if (firstInitialization) {
811                 const wxString vendor   = wxString::FromUTF8(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
812                 const wxString renderer = wxString::FromUTF8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
813                 const wxString version  = wxString::FromUTF8(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
814 
815                 m_logger->info(wxString::Format(L"Renderer info: %s version %s from %s", renderer, version, vendor));
816                 m_logger->info("Depth buffer bits: %d", depthBits());
817 
818                 if (multisample())
819                     m_logger->info("Multisampling enabled");
820                 else
821                     m_logger->info("Multisampling disabled");
822             }
823         }
824 
doShouldRenderFocusIndicator() const825         bool MapViewBase::doShouldRenderFocusIndicator() const {
826             return true;
827         }
828 
doRender()829         void MapViewBase::doRender() {
830             const IO::Path& fontPath = pref(Preferences::RendererFontPath());
831             const size_t fontSize = static_cast<size_t>(pref(Preferences::RendererFontSize));
832             const Renderer::FontDescriptor fontDescriptor(fontPath, fontSize);
833 
834             Renderer::RenderContext renderContext = createRenderContext();
835 
836             setupGL(renderContext);
837             setRenderOptions(renderContext);
838 
839             Renderer::RenderBatch renderBatch(vertexVbo(), indexVbo());
840 
841             doRenderGrid(renderContext, renderBatch);
842             doRenderMap(m_renderer, renderContext, renderBatch);
843             doRenderTools(m_toolBox, renderContext, renderBatch);
844             doRenderExtras(renderContext, renderBatch);
845             renderCoordinateSystem(renderContext, renderBatch);
846             renderPointFile(renderContext, renderBatch);
847             renderCompass(renderBatch);
848 
849             renderBatch.render(renderContext);
850         }
851 
createRenderContext()852         Renderer::RenderContext MapViewBase::createRenderContext() {
853             MapDocumentSPtr document = lock(m_document);
854             const MapViewConfig& mapViewConfig = document->mapViewConfig();
855             const Grid& grid = document->grid();
856 
857             Renderer::RenderContext renderContext = doCreateRenderContext();
858             renderContext.setShowTextures(mapViewConfig.showTextures());
859             renderContext.setShowFaces(mapViewConfig.showFaces());
860             renderContext.setShowEdges(mapViewConfig.showEdges());
861             renderContext.setShadeFaces(mapViewConfig.shadeFaces());
862             renderContext.setShowPointEntities(mapViewConfig.showPointEntities());
863             renderContext.setShowPointEntityModels(mapViewConfig.showPointEntityModels());
864             renderContext.setShowEntityClassnames(mapViewConfig.showEntityClassnames());
865             renderContext.setShowEntityBounds(mapViewConfig.showEntityBounds());
866             renderContext.setShowFog(mapViewConfig.showFog());
867             renderContext.setShowGrid(grid.visible());
868             renderContext.setGridSize(grid.actualSize());
869             return renderContext;
870         }
871 
setupGL(Renderer::RenderContext & context)872         void MapViewBase::setupGL(Renderer::RenderContext& context) {
873             const Renderer::Camera::Viewport& viewport = context.camera().unzoomedViewport();
874             glAssert(glViewport(viewport.x, viewport.y, viewport.width, viewport.height));
875 
876             glAssert(glEnable(GL_MULTISAMPLE));
877             glAssert(glEnable(GL_BLEND));
878             glAssert(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
879             glAssert(glShadeModel(GL_SMOOTH));
880         }
881 
renderCoordinateSystem(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)882         void MapViewBase::renderCoordinateSystem(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
883             if (pref(Preferences::ShowAxes)) {
884                 MapDocumentSPtr document = lock(m_document);
885                 const BBox3& worldBounds = document->worldBounds();
886 
887                 Renderer::RenderService renderService(renderContext, renderBatch);
888                 renderService.renderCoordinateSystem(worldBounds);
889             }
890         }
891 
renderPointFile(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)892         void MapViewBase::renderPointFile(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {
893             MapDocumentSPtr document = lock(m_document);
894             Model::PointFile* pointFile = document->pointFile();
895             if (pointFile != NULL) {
896                 Renderer::RenderService renderService(renderContext, renderBatch);
897                 renderService.setForegroundColor(pref(Preferences::PointFileColor));
898                 renderService.renderLineStrip(pointFile->points());
899             }
900         }
901 
renderCompass(Renderer::RenderBatch & renderBatch)902         void MapViewBase::renderCompass(Renderer::RenderBatch& renderBatch) {
903             if (m_compass != NULL)
904                 m_compass->render(renderBatch);
905         }
906 
doShowPopupMenu()907         void MapViewBase::doShowPopupMenu() {
908             MapDocumentSPtr document = lock(m_document);
909             const Model::NodeList& nodes = document->selectedNodes().nodes();
910             Model::Node* newBrushParent = findNewParentEntityForBrushes(nodes);
911             Model::Node* currentGroup = document->editorContext().currentGroup();
912             Model::Node* newGroup = findNewGroupForObjects(nodes);
913 
914             wxMenu menu;
915             menu.SetEventHandler(this);
916             menu.Append(CommandIds::MapViewPopupMenu::GroupObjects, "Group");
917             menu.Append(CommandIds::MapViewPopupMenu::UngroupObjects, "Ungroup");
918             menu.Append(CommandIds::MapViewPopupMenu::RenameGroups, "Rename");
919 
920             if (newGroup != NULL && newGroup != currentGroup) {
921                 menu.Append(CommandIds::MapViewPopupMenu::AddObjectsToGroup, "Add Objects to Group " + newGroup->name());
922             } else if (currentGroup != NULL) {
923                 menu.Append(CommandIds::MapViewPopupMenu::RemoveObjectsFromGroup, "Remove Objects from Group " + currentGroup->name());
924             }
925             menu.AppendSeparator();
926 
927             menu.AppendSubMenu(makeEntityGroupsMenu(Assets::EntityDefinition::Type_PointEntity, CommandIds::MapViewPopupMenu::LowestPointEntityItem), "Create Point Entity");
928             menu.AppendSubMenu(makeEntityGroupsMenu(Assets::EntityDefinition::Type_BrushEntity, CommandIds::MapViewPopupMenu::LowestBrushEntityItem), "Create Brush Entity");
929 
930             if (newBrushParent == document->currentLayer()) {
931                 menu.Append(CommandIds::MapViewPopupMenu::MoveBrushesToWorld, "Move Brushes to World");
932             } else {
933                 menu.Append(CommandIds::MapViewPopupMenu::MoveBrushesToEntity, "Move Brushes to Entity " + newBrushParent->name());
934             }
935 
936             menu.UpdateUI(this);
937             PopupMenu(&menu);
938 
939             doAfterPopupMenu();
940         }
941 
makeEntityGroupsMenu(const Assets::EntityDefinition::Type type,int id)942         wxMenu* MapViewBase::makeEntityGroupsMenu(const Assets::EntityDefinition::Type type, int id) {
943             wxMenu* menu = new wxMenu();
944 
945             MapDocumentSPtr document = lock(m_document);
946             const Assets::EntityDefinitionGroup::List& groups = document->entityDefinitionManager().groups();
947             Assets::EntityDefinitionGroup::List::const_iterator groupIt, groupEnd;
948             for (groupIt = groups.begin(), groupEnd = groups.end(); groupIt != groupEnd; ++groupIt) {
949                 const Assets::EntityDefinitionGroup& group = *groupIt;
950                 const Assets::EntityDefinitionList definitions = group.definitions(type, Assets::EntityDefinition::Name);
951 
952                 Assets::EntityDefinitionList filteredDefinitions;
953                 filteredDefinitions.reserve(definitions.size());
954 
955                 Assets::EntityDefinitionList::const_iterator dIt, dEnd;
956                 for (dIt = definitions.begin(), dEnd = definitions.end(); dIt != dEnd; ++dIt) {
957                     Assets::EntityDefinition* definition = *dIt;
958                     if (definition->name() != Model::AttributeValues::WorldspawnClassname)
959                         filteredDefinitions.push_back(definition);
960                 }
961 
962                 if (!filteredDefinitions.empty()) {
963                     const String groupName = group.displayName();
964                     wxMenu* groupMenu = new wxMenu();
965                     groupMenu->SetEventHandler(this);
966 
967                     for (dIt = filteredDefinitions.begin(), dEnd = filteredDefinitions.end(); dIt != dEnd; ++dIt) {
968                         const Assets::EntityDefinition* definition = *dIt;
969                         groupMenu->Append(id++, definition->shortName());
970                     }
971 
972                     menu->AppendSubMenu(groupMenu, groupName);
973                 }
974             }
975 
976             return menu;
977         }
978 
OnAddObjectsToGroup(wxCommandEvent & event)979         void MapViewBase::OnAddObjectsToGroup(wxCommandEvent& event) {
980             MapDocumentSPtr document = lock(m_document);
981             const Model::NodeList nodes = document->selectedNodes().nodes();
982             Model::Node* newGroup = findNewGroupForObjects(nodes);
983             assert(newGroup != NULL);
984 
985             Transaction transaction(document, "Add Objects to Group");
986             reparentNodes(nodes, newGroup);
987             document->deselectAll();
988             document->select(newGroup);
989         }
990 
OnRemoveObjectsFromGroup(wxCommandEvent & event)991         void MapViewBase::OnRemoveObjectsFromGroup(wxCommandEvent& event) {
992             MapDocumentSPtr document = lock(m_document);
993             const Model::NodeList nodes = document->selectedNodes().nodes();
994             Model::Node* currentGroup = document->editorContext().currentGroup();
995             assert(currentGroup != NULL);
996 
997             Transaction transaction(document, "Remove Objects from Group");
998             if (currentGroup->childCount() == nodes.size())
999                 document->closeGroup();
1000             reparentNodes(nodes, document->currentLayer());
1001             document->deselectAll();
1002             document->select(nodes);
1003         }
1004 
findNewGroupForObjects(const Model::NodeList & nodes) const1005         Model::Node* MapViewBase::findNewGroupForObjects(const Model::NodeList& nodes) const {
1006             Model::Node* newGroup = NULL;
1007 
1008             MapDocumentSPtr document = lock(m_document);
1009             const Model::Hit& hit = pickResult().query().pickable().type(Model::Group::GroupHit).occluded().first();
1010             if (hit.isMatch())
1011                 newGroup = Model::hitToNode(hit);
1012 
1013             if (newGroup != NULL && canReparentNodes(nodes, newGroup))
1014                 return newGroup;
1015             return NULL;
1016         }
1017 
OnMoveBrushesTo(wxCommandEvent & event)1018         void MapViewBase::OnMoveBrushesTo(wxCommandEvent& event) {
1019             if (IsBeingDeleted()) return;
1020 
1021             MapDocumentSPtr document = lock(m_document);
1022             const Model::NodeList& nodes = document->selectedNodes().nodes();
1023             Model::Node* newParent = findNewParentEntityForBrushes(nodes);
1024             assert(newParent != NULL);
1025             reparentNodes(nodes, newParent);
1026         }
1027 
findNewParentEntityForBrushes(const Model::NodeList & nodes) const1028         Model::Node* MapViewBase::findNewParentEntityForBrushes(const Model::NodeList& nodes) const {
1029             Model::Node* newParent = NULL;
1030 
1031             MapDocumentSPtr document = lock(m_document);
1032             const Model::Hit& hit = pickResult().query().pickable().type(Model::Brush::BrushHit).occluded().first();
1033             if (hit.isMatch()) {
1034                 const Model::Brush* brush = Model::hitToBrush(hit);
1035                 newParent = brush->entity();
1036             }
1037 
1038             if (newParent != NULL && newParent != document->world() && canReparentNodes(nodes, newParent))
1039                 return newParent;
1040             return document->currentLayer();
1041         }
1042 
canReparentNodes(const Model::NodeList & nodes,const Model::Node * newParent) const1043         bool MapViewBase::canReparentNodes(const Model::NodeList& nodes, const Model::Node* newParent) const {
1044             Model::NodeList::const_iterator it, end;
1045             for (it = nodes.begin(), end = nodes.end(); it != end; ++it) {
1046                 const Model::Node* node = *it;
1047                 if (newParent != node && node->parent() != newParent && !newParent->isDescendantOf(node))
1048                     return true;
1049             }
1050             return false;
1051         }
1052 
reparentNodes(const Model::NodeList & nodes,Model::Node * newParent)1053         void MapViewBase::reparentNodes(const Model::NodeList& nodes, Model::Node* newParent) {
1054             assert(newParent != NULL);
1055 
1056             const Model::NodeList reparentableNodes = collectReparentableNodes(nodes, newParent);
1057             assert(!reparentableNodes.empty());
1058 
1059             MapDocumentSPtr document = lock(m_document);
1060 
1061             StringStream name;
1062             name << "Move " << (reparentableNodes.size() == 1 ? "Object" : "Objects") << " to " << newParent->name();
1063 
1064             const Transaction transaction(document, name.str());
1065             document->deselectAll();
1066             document->reparentNodes(newParent, reparentableNodes);
1067             document->select(reparentableNodes);
1068         }
1069 
collectReparentableNodes(const Model::NodeList & nodes,const Model::Node * newParent) const1070         Model::NodeList MapViewBase::collectReparentableNodes(const Model::NodeList& nodes, const Model::Node* newParent) const {
1071             Model::NodeList result;
1072             Model::NodeList::const_iterator it, end;
1073             for (it = nodes.begin(), end = nodes.end(); it != end; ++it) {
1074                 Model::Node* node = *it;
1075                 if (newParent != node && node->parent() != newParent && !newParent->isDescendantOf(node))
1076                     result.push_back(node);
1077             }
1078             return result;
1079         }
1080 
OnUpdatePopupMenuItem(wxUpdateUIEvent & event)1081         void MapViewBase::OnUpdatePopupMenuItem(wxUpdateUIEvent& event) {
1082             if (IsBeingDeleted()) return;
1083 
1084             switch (event.GetId()) {
1085                 case CommandIds::MapViewPopupMenu::GroupObjects:
1086                     updateGroupObjectsMenuItem(event);
1087                     break;
1088                 case CommandIds::MapViewPopupMenu::UngroupObjects:
1089                     updateUngroupObjectsMenuItem(event);
1090                     break;
1091                 case CommandIds::MapViewPopupMenu::RenameGroups:
1092                     updateRenameGroupsMenuItem(event);
1093                     break;
1094                 default:
1095                     if (event.GetId() >= CommandIds::MapViewPopupMenu::LowestBrushEntityItem &&
1096                         event.GetId() <= CommandIds::MapViewPopupMenu::HighestBrushEntityItem) {
1097                         event.Enable(canCreateBrushEntity());
1098                     } else {
1099                         event.Enable(true);
1100                     }
1101                     break;
1102             }
1103         }
1104 
updateGroupObjectsMenuItem(wxUpdateUIEvent & event) const1105         void MapViewBase::updateGroupObjectsMenuItem(wxUpdateUIEvent& event) const {
1106             MapDocumentSPtr document = lock(m_document);
1107             event.Enable(!document->selectedNodes().empty());
1108         }
1109 
updateUngroupObjectsMenuItem(wxUpdateUIEvent & event) const1110         void MapViewBase::updateUngroupObjectsMenuItem(wxUpdateUIEvent& event) const {
1111             MapDocumentSPtr document = lock(m_document);
1112             event.Enable(document->selectedNodes().hasOnlyGroups());
1113         }
1114 
updateRenameGroupsMenuItem(wxUpdateUIEvent & event) const1115         void MapViewBase::updateRenameGroupsMenuItem(wxUpdateUIEvent& event) const {
1116             MapDocumentSPtr document = lock(m_document);
1117             event.Enable(document->selectedNodes().hasOnlyGroups());
1118         }
1119 
doRenderExtras(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)1120         void MapViewBase::doRenderExtras(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) {}
1121 
doAfterPopupMenu()1122         void MapViewBase::doAfterPopupMenu() {}
1123     }
1124 }
1125