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