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 "ToolBoxConnector.h" 21 22 #include "View/ToolBox.h" 23 #include "View/ToolChain.h" 24 25 namespace TrenchBroom { 26 namespace View { ToolBoxConnector(wxWindow * window)27 ToolBoxConnector::ToolBoxConnector(wxWindow* window) : 28 m_window(window), 29 m_toolBox(NULL), 30 m_toolChain(new ToolChain()), 31 m_ignoreNextDrag(false) { 32 assert(m_window != NULL); 33 bindEvents(); 34 } 35 ~ToolBoxConnector()36 ToolBoxConnector::~ToolBoxConnector() { 37 unbindEvents(); 38 delete m_toolChain; 39 } 40 pickRay() const41 const Ray3& ToolBoxConnector::pickRay() const { 42 return m_inputState.pickRay(); 43 } 44 pickResult() const45 const Model::PickResult& ToolBoxConnector::pickResult() const { 46 return m_inputState.pickResult(); 47 } 48 updatePickResult()49 void ToolBoxConnector::updatePickResult() { 50 assert(m_toolBox != NULL); 51 52 m_inputState.setPickRequest(doGetPickRequest(m_inputState.mouseX(), m_inputState.mouseY())); 53 Model::PickResult pickResult = doPick(m_inputState.pickRay()); 54 m_toolBox->pick(m_toolChain, m_inputState, pickResult); 55 m_inputState.setPickResult(pickResult); 56 } 57 updateLastActivation()58 void ToolBoxConnector::updateLastActivation() { 59 assert(m_toolBox != NULL); 60 m_toolBox->updateLastActivation(); 61 } 62 setToolBox(ToolBox & toolBox)63 void ToolBoxConnector::setToolBox(ToolBox& toolBox) { 64 assert(m_toolBox == NULL); 65 m_toolBox = &toolBox; 66 } 67 addTool(ToolController * tool)68 void ToolBoxConnector::addTool(ToolController* tool) { 69 m_toolChain->append(tool); 70 } 71 dragEnter(const wxCoord x,const wxCoord y,const String & text)72 bool ToolBoxConnector::dragEnter(const wxCoord x, const wxCoord y, const String& text) { 73 assert(m_toolBox != NULL); 74 75 mouseMoved(wxPoint(x, y)); 76 updatePickResult(); 77 78 const bool result = m_toolBox->dragEnter(m_toolChain, m_inputState, text); 79 m_window->Refresh(); 80 return result; 81 } 82 dragMove(const wxCoord x,const wxCoord y,const String & text)83 bool ToolBoxConnector::dragMove(const wxCoord x, const wxCoord y, const String& text) { 84 assert(m_toolBox != NULL); 85 86 mouseMoved(wxPoint(x, y)); 87 updatePickResult(); 88 89 const bool result = m_toolBox->dragMove(m_toolChain, m_inputState, text); 90 m_window->Refresh(); 91 return result; 92 } 93 dragLeave()94 void ToolBoxConnector::dragLeave() { 95 assert(m_toolBox != NULL); 96 97 m_toolBox->dragLeave(m_toolChain, m_inputState); 98 m_window->Refresh(); 99 } 100 dragDrop(const wxCoord x,const wxCoord y,const String & text)101 bool ToolBoxConnector::dragDrop(const wxCoord x, const wxCoord y, const String& text) { 102 assert(m_toolBox != NULL); 103 104 updatePickResult(); 105 106 const bool result = m_toolBox->dragDrop(m_toolChain, m_inputState, text); 107 m_window->Refresh(); 108 if (result) 109 m_window->SetFocus(); 110 return result; 111 } 112 cancel()113 bool ToolBoxConnector::cancel() { 114 assert(m_toolBox != NULL); 115 const bool result = m_toolBox->cancel(m_toolChain); 116 m_inputState.setAnyToolDragging(false); 117 return result; 118 } 119 setRenderOptions(Renderer::RenderContext & renderContext)120 void ToolBoxConnector::setRenderOptions(Renderer::RenderContext& renderContext) { 121 assert(m_toolBox != NULL); 122 m_toolBox->setRenderOptions(m_toolChain, m_inputState, renderContext); 123 } 124 renderTools(Renderer::RenderContext & renderContext,Renderer::RenderBatch & renderBatch)125 void ToolBoxConnector::renderTools(Renderer::RenderContext& renderContext, Renderer::RenderBatch& renderBatch) { 126 assert(m_toolBox != NULL); 127 m_toolBox->renderTools(m_toolChain, m_inputState, renderContext, renderBatch); 128 } 129 bindEvents()130 void ToolBoxConnector::bindEvents() { 131 m_window->Bind(wxEVT_KEY_DOWN, &ToolBoxConnector::OnKey, this); 132 m_window->Bind(wxEVT_KEY_UP, &ToolBoxConnector::OnKey, this); 133 m_window->Bind(wxEVT_LEFT_DOWN, &ToolBoxConnector::OnMouseButton, this); 134 m_window->Bind(wxEVT_LEFT_UP, &ToolBoxConnector::OnMouseButton, this); 135 m_window->Bind(wxEVT_LEFT_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 136 m_window->Bind(wxEVT_RIGHT_DOWN, &ToolBoxConnector::OnMouseButton, this); 137 m_window->Bind(wxEVT_RIGHT_UP, &ToolBoxConnector::OnMouseButton, this); 138 m_window->Bind(wxEVT_RIGHT_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 139 m_window->Bind(wxEVT_MIDDLE_DOWN, &ToolBoxConnector::OnMouseButton, this); 140 m_window->Bind(wxEVT_MIDDLE_UP, &ToolBoxConnector::OnMouseButton, this); 141 m_window->Bind(wxEVT_MIDDLE_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 142 m_window->Bind(wxEVT_AUX1_DOWN, &ToolBoxConnector::OnMouseButton, this); 143 m_window->Bind(wxEVT_AUX1_UP, &ToolBoxConnector::OnMouseButton, this); 144 m_window->Bind(wxEVT_AUX1_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 145 m_window->Bind(wxEVT_AUX2_DOWN, &ToolBoxConnector::OnMouseButton, this); 146 m_window->Bind(wxEVT_AUX2_UP, &ToolBoxConnector::OnMouseButton, this); 147 m_window->Bind(wxEVT_AUX2_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 148 m_window->Bind(wxEVT_MOTION, &ToolBoxConnector::OnMouseMotion, this); 149 m_window->Bind(wxEVT_MOUSEWHEEL, &ToolBoxConnector::OnMouseWheel, this); 150 m_window->Bind(wxEVT_MOUSE_CAPTURE_LOST, &ToolBoxConnector::OnMouseCaptureLost, this); 151 m_window->Bind(wxEVT_SET_FOCUS, &ToolBoxConnector::OnSetFocus, this); 152 m_window->Bind(wxEVT_KILL_FOCUS, &ToolBoxConnector::OnKillFocus, this); 153 } 154 unbindEvents()155 void ToolBoxConnector::unbindEvents() { 156 m_window->Unbind(wxEVT_KEY_DOWN, &ToolBoxConnector::OnKey, this); 157 m_window->Unbind(wxEVT_KEY_UP, &ToolBoxConnector::OnKey, this); 158 m_window->Unbind(wxEVT_LEFT_DOWN, &ToolBoxConnector::OnMouseButton, this); 159 m_window->Unbind(wxEVT_LEFT_UP, &ToolBoxConnector::OnMouseButton, this); 160 m_window->Unbind(wxEVT_LEFT_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 161 m_window->Unbind(wxEVT_RIGHT_DOWN, &ToolBoxConnector::OnMouseButton, this); 162 m_window->Unbind(wxEVT_RIGHT_UP, &ToolBoxConnector::OnMouseButton, this); 163 m_window->Unbind(wxEVT_RIGHT_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 164 m_window->Unbind(wxEVT_MIDDLE_DOWN, &ToolBoxConnector::OnMouseButton, this); 165 m_window->Unbind(wxEVT_MIDDLE_UP, &ToolBoxConnector::OnMouseButton, this); 166 m_window->Unbind(wxEVT_MIDDLE_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 167 m_window->Unbind(wxEVT_AUX1_DOWN, &ToolBoxConnector::OnMouseButton, this); 168 m_window->Unbind(wxEVT_AUX1_UP, &ToolBoxConnector::OnMouseButton, this); 169 m_window->Unbind(wxEVT_AUX1_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 170 m_window->Unbind(wxEVT_AUX2_DOWN, &ToolBoxConnector::OnMouseButton, this); 171 m_window->Unbind(wxEVT_AUX2_UP, &ToolBoxConnector::OnMouseButton, this); 172 m_window->Unbind(wxEVT_AUX2_DCLICK, &ToolBoxConnector::OnMouseDoubleClick, this); 173 m_window->Unbind(wxEVT_MOTION, &ToolBoxConnector::OnMouseMotion, this); 174 m_window->Unbind(wxEVT_MOUSEWHEEL, &ToolBoxConnector::OnMouseWheel, this); 175 m_window->Unbind(wxEVT_MOUSE_CAPTURE_LOST, &ToolBoxConnector::OnMouseCaptureLost, this); 176 m_window->Unbind(wxEVT_SET_FOCUS, &ToolBoxConnector::OnSetFocus, this); 177 m_window->Unbind(wxEVT_KILL_FOCUS, &ToolBoxConnector::OnKillFocus, this); 178 } 179 OnKey(wxKeyEvent & event)180 void ToolBoxConnector::OnKey(wxKeyEvent& event) { 181 if (m_window->IsBeingDeleted()) return; 182 183 assert(m_toolBox != NULL); 184 185 event.Skip(); 186 updateModifierKeys(); 187 m_window->Refresh(); 188 } 189 OnMouseButton(wxMouseEvent & event)190 void ToolBoxConnector::OnMouseButton(wxMouseEvent& event) { 191 if (m_window->IsBeingDeleted()) return; 192 193 assert(m_toolBox != NULL); 194 195 event.Skip(); 196 197 const MouseButtonState button = mouseButton(event); 198 if (m_toolBox->ignoreNextClick() && button == MouseButtons::MBLeft) { 199 if (event.ButtonUp()) 200 m_toolBox->clearIgnoreNextClick(); 201 return; 202 } 203 204 m_window->SetFocus(); 205 if (event.ButtonUp()) 206 m_toolBox->clearIgnoreNextClick(); 207 208 updateModifierKeys(); 209 if (event.ButtonDown()) { 210 captureMouse(); 211 m_clickTime = wxGetLocalTimeMillis(); 212 m_clickPos = event.GetPosition(); 213 m_inputState.mouseDown(button); 214 m_toolBox->mouseDown(m_toolChain, m_inputState); 215 } else { 216 if (m_toolBox->dragging()) { 217 const wxLongLong clickInterval = wxGetLocalTimeMillis() - m_clickTime; 218 if (clickInterval <= 100) { 219 m_toolBox->cancelMouseDrag(); 220 m_toolBox->mouseUp(m_toolChain, m_inputState); 221 m_toolBox->mouseClick(m_toolChain, m_inputState); 222 } else { 223 m_toolBox->endMouseDrag(m_inputState); 224 m_toolBox->mouseUp(m_toolChain, m_inputState); 225 } 226 m_inputState.mouseUp(button); 227 m_inputState.setAnyToolDragging(false); 228 releaseMouse(); 229 } else if (!m_ignoreNextDrag) { 230 m_toolBox->mouseUp(m_toolChain, m_inputState); 231 const bool handled = isWithinClickDistance(event.GetPosition()) && m_toolBox->mouseClick(m_toolChain, m_inputState); 232 m_inputState.mouseUp(button); 233 releaseMouse(); 234 235 if (button == MouseButtons::MBRight && !handled) { 236 // We miss mouse events when a popup menu is already open, so we must make sure that the input 237 // state is up to date. 238 mouseMoved(event.GetPosition()); 239 updatePickResult(); 240 showPopupMenu(); 241 } 242 } else { 243 m_toolBox->mouseUp(m_toolChain, m_inputState); 244 m_inputState.mouseUp(button); 245 releaseMouse(); 246 } 247 } 248 249 updatePickResult(); 250 m_ignoreNextDrag = false; 251 252 m_window->Refresh(); 253 } 254 OnMouseDoubleClick(wxMouseEvent & event)255 void ToolBoxConnector::OnMouseDoubleClick(wxMouseEvent& event) { 256 if (m_window->IsBeingDeleted()) return; 257 258 assert(m_toolBox != NULL); 259 260 event.Skip(); 261 262 const MouseButtonState button = mouseButton(event); 263 updateModifierKeys(); 264 265 m_clickPos = event.GetPosition(); 266 m_inputState.mouseDown(button); 267 m_toolBox->mouseDoubleClick(m_toolChain, m_inputState); 268 m_inputState.mouseUp(button); 269 270 updatePickResult(); 271 272 m_window->Refresh(); 273 } 274 OnMouseMotion(wxMouseEvent & event)275 void ToolBoxConnector::OnMouseMotion(wxMouseEvent& event) { 276 if (m_window->IsBeingDeleted()) return; 277 278 assert(m_toolBox != NULL); 279 280 event.Skip(); 281 282 updateModifierKeys(); 283 if (m_toolBox->dragging()) { 284 mouseMoved(event.GetPosition()); 285 updatePickResult(); 286 if (!m_toolBox->mouseDrag(m_inputState)) { 287 m_toolBox->endMouseDrag(m_inputState); 288 m_inputState.setAnyToolDragging(false); 289 m_ignoreNextDrag = true; 290 } 291 } else if (!m_ignoreNextDrag) { 292 if (m_inputState.mouseButtons() != MouseButtons::MBNone) { 293 if (!isWithinClickDistance(event.GetPosition())) { 294 const bool dragStarted = m_toolBox->startMouseDrag(m_toolChain, m_inputState); 295 if (dragStarted) 296 m_ignoreNextDrag = true; 297 mouseMoved(event.GetPosition()); 298 updatePickResult(); 299 if (dragStarted) { 300 m_inputState.setAnyToolDragging(true); 301 m_toolBox->mouseDrag(m_inputState); 302 } 303 } 304 } else { 305 mouseMoved(event.GetPosition()); 306 updatePickResult(); 307 m_toolBox->mouseMove(m_toolChain, m_inputState); 308 } 309 } 310 311 m_window->Refresh(); 312 m_window->Update(); // neccessary for smooth rendering on Windows 313 } 314 OnMouseWheel(wxMouseEvent & event)315 void ToolBoxConnector::OnMouseWheel(wxMouseEvent& event) { 316 if (m_window->IsBeingDeleted()) return; 317 318 assert(m_toolBox != NULL); 319 320 event.Skip(); 321 322 updateModifierKeys(); 323 const float delta = static_cast<float>(event.GetWheelRotation()) / event.GetWheelDelta() * event.GetLinesPerAction(); 324 if (event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL) 325 m_inputState.scroll(delta, 0.0f); 326 else if (event.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL) 327 m_inputState.scroll(0.0f, delta); 328 m_toolBox->mouseScroll(m_toolChain, m_inputState); 329 330 updatePickResult(); 331 m_window->Refresh(); 332 } 333 334 OnMouseCaptureLost(wxMouseCaptureLostEvent & event)335 void ToolBoxConnector::OnMouseCaptureLost(wxMouseCaptureLostEvent& event) { 336 if (m_window->IsBeingDeleted()) return; 337 338 assert(m_toolBox != NULL); 339 340 event.Skip(); 341 342 cancelDrag(); 343 m_window->Refresh(); 344 } 345 OnSetFocus(wxFocusEvent & event)346 void ToolBoxConnector::OnSetFocus(wxFocusEvent& event) { 347 if (m_window->IsBeingDeleted()) return; 348 349 assert(m_toolBox != NULL); 350 351 event.Skip(); 352 updateModifierKeys(); 353 m_window->Refresh(); 354 355 mouseMoved(m_window->ScreenToClient(wxGetMousePosition())); 356 } 357 OnKillFocus(wxFocusEvent & event)358 void ToolBoxConnector::OnKillFocus(wxFocusEvent& event) { 359 if (m_window->IsBeingDeleted()) return; 360 361 assert(m_toolBox != NULL); 362 363 event.Skip(); 364 365 cancelDrag(); 366 releaseMouse(); 367 updateModifierKeys(); 368 m_window->Refresh(); 369 } 370 isWithinClickDistance(const wxPoint & pos) const371 bool ToolBoxConnector::isWithinClickDistance(const wxPoint& pos) const { 372 return (std::abs(pos.x - m_clickPos.x) <= 1 && 373 std::abs(pos.y - m_clickPos.y) <= 1); 374 } 375 captureMouse()376 void ToolBoxConnector::captureMouse() { 377 if (!m_window->HasCapture() && !m_toolBox->dragging()) 378 m_window->CaptureMouse(); 379 } 380 releaseMouse()381 void ToolBoxConnector::releaseMouse() { 382 if (m_window->HasCapture() && !m_toolBox->dragging()) 383 m_window->ReleaseMouse(); 384 } 385 386 cancelDrag()387 void ToolBoxConnector::cancelDrag() { 388 if (m_toolBox->dragging()) { 389 m_toolBox->cancelMouseDrag(); 390 m_inputState.setAnyToolDragging(false); 391 m_inputState.clearMouseButtons(); 392 } 393 } 394 modifierKeys()395 ModifierKeyState ToolBoxConnector::modifierKeys() { 396 const wxMouseState mouseState = wxGetMouseState(); 397 398 ModifierKeyState state = ModifierKeys::MKNone; 399 if (mouseState.CmdDown()) 400 state |= ModifierKeys::MKCtrlCmd; 401 if (mouseState.ShiftDown()) 402 state |= ModifierKeys::MKShift; 403 if (mouseState.AltDown()) 404 state |= ModifierKeys::MKAlt; 405 return state; 406 } 407 setModifierKeys()408 bool ToolBoxConnector::setModifierKeys() { 409 const ModifierKeyState keys = modifierKeys(); 410 if (keys != m_inputState.modifierKeys()) { 411 m_inputState.setModifierKeys(keys); 412 return true; 413 } 414 return false; 415 } 416 clearModifierKeys()417 bool ToolBoxConnector::clearModifierKeys() { 418 if (m_inputState.modifierKeys() != ModifierKeys::MKNone) { 419 m_inputState.setModifierKeys(ModifierKeys::MKNone); 420 return true; 421 } 422 return false; 423 } 424 updateModifierKeys()425 void ToolBoxConnector::updateModifierKeys() { 426 if (setModifierKeys()) { 427 updatePickResult(); 428 m_toolBox->modifierKeyChange(m_toolChain, m_inputState); 429 } 430 } 431 mouseButton(wxMouseEvent & event)432 MouseButtonState ToolBoxConnector::mouseButton(wxMouseEvent& event) { 433 switch (event.GetButton()) { 434 case wxMOUSE_BTN_LEFT: 435 return MouseButtons::MBLeft; 436 case wxMOUSE_BTN_MIDDLE: 437 return MouseButtons::MBMiddle; 438 case wxMOUSE_BTN_RIGHT: 439 return MouseButtons::MBRight; 440 default: 441 return MouseButtons::MBNone; 442 } 443 } 444 mouseMoved(const wxPoint & position)445 void ToolBoxConnector::mouseMoved(const wxPoint& position) { 446 const wxPoint delta = position - m_lastMousePos; 447 m_inputState.mouseMove(position.x, position.y, delta.x, delta.y); 448 m_lastMousePos = position; 449 } 450 showPopupMenu()451 void ToolBoxConnector::showPopupMenu() { 452 doShowPopupMenu(); 453 updateModifierKeys(); 454 } 455 doShowPopupMenu()456 void ToolBoxConnector::doShowPopupMenu() {} 457 } 458 } 459