1 /* 2 * This program source code file is part of KiCad, a free EDA CAD application. 3 * 4 * Copyright (C) 2013 CERN 5 * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. 6 * 7 * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 2 12 * of the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, you may find one here: 21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 22 * or you may search the http://www.gnu.org website for the version 2 license, 23 * or you may write to the Free Software Foundation, Inc., 24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 25 */ 26 27 #ifndef __TOOL_EVENT_H 28 #define __TOOL_EVENT_H 29 30 #include <cstdio> 31 #include <deque> 32 #include <iterator> 33 34 #include <math/vector2d.h> 35 #include <core/optional.h> 36 37 #include <tool/tool_action.h> 38 39 class TOOL_ACTION; 40 class TOOL_MANAGER; 41 class TOOL_BASE; 42 43 /** 44 * Internal (GUI-independent) event definitions. 45 */ 46 enum TOOL_EVENT_CATEGORY 47 { 48 TC_NONE = 0x00, 49 TC_MOUSE = 0x01, 50 TC_KEYBOARD = 0x02, 51 TC_COMMAND = 0x04, 52 TC_MESSAGE = 0x08, 53 TC_VIEW = 0x10, 54 TC_ANY = 0xffffffff 55 }; 56 57 enum TOOL_ACTIONS 58 { 59 // UI input events 60 TA_NONE = 0x0000, 61 TA_MOUSE_CLICK = 0x0001, 62 TA_MOUSE_DBLCLICK = 0x0002, 63 TA_MOUSE_UP = 0x0004, 64 TA_MOUSE_DOWN = 0x0008, 65 TA_MOUSE_DRAG = 0x0010, 66 TA_MOUSE_MOTION = 0x0020, 67 TA_MOUSE_WHEEL = 0x0040, 68 TA_MOUSE = 0x007f, 69 70 TA_KEY_PRESSED = 0x0080, 71 TA_KEYBOARD = TA_KEY_PRESSED, 72 73 // View related events 74 TA_VIEW_REFRESH = 0x0100, 75 TA_VIEW_ZOOM = 0x0200, 76 TA_VIEW_PAN = 0x0400, 77 TA_VIEW_DIRTY = 0x0800, 78 TA_VIEW = 0x0f00, 79 80 TA_CHANGE_LAYER = 0x1000, 81 82 // Tool cancel event. Issued automagically when the user hits escape or selects End Tool from 83 // the context menu. 84 TA_CANCEL_TOOL = 0x2000, 85 86 // Context menu update. Issued whenever context menu is open and the user hovers the mouse 87 // over one of choices. Used in dynamic highlighting in disambiguation menu 88 TA_CHOICE_MENU_UPDATE = 0x4000, 89 90 // Context menu choice. Sent if the user picked something from the context menu or 91 // closed it without selecting anything. 92 TA_CHOICE_MENU_CHOICE = 0x8000, 93 94 // Context menu is closed, no matter whether anything has been chosen or not. 95 TA_CHOICE_MENU_CLOSED = 0x10000, 96 97 TA_CHOICE_MENU = TA_CHOICE_MENU_UPDATE | TA_CHOICE_MENU_CHOICE | TA_CHOICE_MENU_CLOSED, 98 99 // This event is sent *before* undo/redo command is performed. 100 TA_UNDO_REDO_PRE = 0x20000, 101 102 // This event is sent *after* undo/redo command is performed. 103 TA_UNDO_REDO_POST = 0x40000, 104 105 // Tool action (allows one to control tools). 106 TA_ACTION = 0x80000, 107 108 // Tool activation event. 109 TA_ACTIVATE = 0x100000, 110 111 // Tool re-activation event for tools already on the stack 112 TA_REACTIVATE = 0x200000, 113 114 // Model has changed (partial update). 115 TA_MODEL_CHANGE = 0x400000, 116 117 // Tool priming event (a special mouse click) 118 TA_PRIME = 0x800001, 119 120 TA_ANY = 0xffffffff 121 }; 122 123 enum TOOL_MOUSE_BUTTONS 124 { 125 BUT_NONE = 0x0, 126 BUT_LEFT = 0x1, 127 BUT_RIGHT = 0x2, 128 BUT_MIDDLE = 0x4, 129 BUT_BUTTON_MASK = BUT_LEFT | BUT_RIGHT | BUT_MIDDLE, 130 BUT_ANY = 0xffffffff 131 }; 132 133 enum TOOL_MODIFIERS 134 { 135 MD_SHIFT = 0x1000, 136 MD_CTRL = 0x2000, 137 MD_ALT = 0x4000, 138 MD_MODIFIER_MASK = MD_SHIFT | MD_CTRL | MD_ALT, 139 }; 140 141 /// Defines when a context menu is opened. 142 enum CONTEXT_MENU_TRIGGER 143 { 144 CMENU_BUTTON = 0, // On the right button 145 CMENU_NOW, // Right now (after TOOL_INTERACTIVE::SetContextMenu) 146 CMENU_OFF // Never 147 }; 148 149 /** 150 * Generic, UI-independent tool event. 151 */ 152 class TOOL_EVENT 153 { 154 public: 155 /** 156 * Return information about event in form of a human-readable string. 157 * 158 * @return Event information. 159 */ 160 const std::string Format() const; 161 162 TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory = TC_NONE, TOOL_ACTIONS aAction = TA_NONE, 163 TOOL_ACTION_SCOPE aScope = AS_GLOBAL, void* aParameter = nullptr ) : m_category(aCategory)164 m_category( aCategory ), 165 m_actions( aAction ), 166 m_scope( aScope ), 167 m_mouseButtons( 0 ), 168 m_keyCode( 0 ), 169 m_modifiers( 0 ), 170 m_param( aParameter ), 171 m_firstResponder( nullptr ) 172 { 173 init(); 174 } 175 176 TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory, TOOL_ACTIONS aAction, int aExtraParam, 177 TOOL_ACTION_SCOPE aScope = AS_GLOBAL, void* aParameter = nullptr ) : m_category(aCategory)178 m_category( aCategory ), 179 m_actions( aAction ), 180 m_scope( aScope ), 181 m_mouseButtons( 0 ), 182 m_keyCode( 0 ), 183 m_modifiers( 0 ), 184 m_param( aParameter ), 185 m_firstResponder( nullptr ) 186 { 187 if( aCategory == TC_MOUSE ) 188 { 189 setMouseButtons( aExtraParam & BUT_BUTTON_MASK ); 190 } 191 else if( aCategory == TC_KEYBOARD ) 192 { 193 m_keyCode = aExtraParam & ~MD_MODIFIER_MASK; // Filter out modifiers 194 } 195 else if( aCategory == TC_COMMAND ) 196 { 197 m_commandId = aExtraParam; 198 } 199 200 if( aCategory & ( TC_MOUSE | TC_KEYBOARD ) ) 201 { 202 m_modifiers = aExtraParam & MD_MODIFIER_MASK; 203 } 204 205 init(); 206 } 207 208 TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory, TOOL_ACTIONS aAction, 209 const std::string& aExtraParam, TOOL_ACTION_SCOPE aScope = AS_GLOBAL, 210 void* aParameter = nullptr ) : m_category(aCategory)211 m_category( aCategory ), 212 m_actions( aAction ), 213 m_scope( aScope ), 214 m_mouseButtons( 0 ), 215 m_keyCode( 0 ), 216 m_modifiers( 0 ), 217 m_param( aParameter ), 218 m_firstResponder( nullptr ) 219 { 220 if( aCategory == TC_COMMAND || aCategory == TC_MESSAGE ) 221 m_commandStr = aExtraParam; 222 223 init(); 224 } 225 226 ///< Returns the category (eg. mouse/keyboard/action) of an event.. Category()227 TOOL_EVENT_CATEGORY Category() const { return m_category; } 228 229 ///< Returns more specific information about the type of an event. Action()230 TOOL_ACTIONS Action() const { return m_actions; } 231 232 ///< These give a tool a method of informing the TOOL_MANAGER that a particular event should 233 ///< be passed on to subsequent tools on the stack. Defaults to true for TC_MESSAGES; false 234 ///< for everything else. PassEvent()235 bool PassEvent() const { return m_passEvent; } 236 void SetPassEvent( bool aPass = true ) { m_passEvent = aPass; } 237 238 ///< Returns if it this event has a valid position (true for mouse events and context-menu 239 ///< or hotkey-based command events) HasPosition()240 bool HasPosition() const { return m_hasPosition; } SetHasPosition(bool aHasPosition)241 void SetHasPosition( bool aHasPosition ) { m_hasPosition = aHasPosition; } 242 243 ///< Returns if the action associated with this event should be treated as immediate regardless 244 ///< of the current immediate action settings. ForceImmediate()245 bool ForceImmediate() const { return m_forceImmediate; } 246 void SetForceImmediate( bool aForceImmediate = true ) { m_forceImmediate = aForceImmediate; } 247 FirstResponder()248 TOOL_BASE* FirstResponder() const { return m_firstResponder; } SetFirstResponder(TOOL_BASE * aTool)249 void SetFirstResponder( TOOL_BASE* aTool ) { m_firstResponder = aTool; } 250 251 ///< Controls whether the tool is first being pushed to the stack or being reactivated after a pause IsReactivate()252 bool IsReactivate() const { return m_reactivate; } 253 void SetReactivate( bool aReactivate = true ) { m_reactivate = aReactivate; } 254 255 ///< Returns information about difference between current mouse cursor position and the place 256 ///< where dragging has started. Delta()257 const VECTOR2D Delta() const 258 { 259 return returnCheckedPosition( m_mouseDelta ); 260 } 261 262 ///< Returns mouse cursor position in world coordinates. Position()263 const VECTOR2D Position() const 264 { 265 return returnCheckedPosition( m_mousePos ); 266 } 267 268 ///< Returns the point where dragging has started. DragOrigin()269 const VECTOR2D DragOrigin() const 270 { 271 return returnCheckedPosition( m_mouseDragOrigin ); 272 } 273 274 ///< Returns information about mouse buttons state. Buttons()275 int Buttons() const 276 { 277 assert( m_category == TC_MOUSE ); // this should be used only with mouse events 278 return m_mouseButtons; 279 } 280 281 bool IsClick( int aButtonMask = BUT_ANY ) const; 282 283 bool IsDblClick( int aButtonMask = BUT_ANY ) const; 284 285 bool IsDrag( int aButtonMask = BUT_ANY ) const 286 { 287 return m_actions == TA_MOUSE_DRAG && ( m_mouseButtons & aButtonMask ) == m_mouseButtons; 288 } 289 290 bool IsMouseDown( int aButtonMask = BUT_ANY ) const 291 { 292 return m_actions == TA_MOUSE_DOWN && ( m_mouseButtons & aButtonMask ) == m_mouseButtons; 293 } 294 295 bool IsMouseUp( int aButtonMask = BUT_ANY ) const 296 { 297 return m_actions == TA_MOUSE_UP && ( m_mouseButtons & aButtonMask ) == m_mouseButtons; 298 } 299 IsMotion()300 bool IsMotion() const 301 { 302 return m_actions == TA_MOUSE_MOTION; 303 } 304 IsMouseAction()305 bool IsMouseAction() const 306 { 307 return ( m_actions & TA_MOUSE ); 308 } 309 IsCancel()310 bool IsCancel() const 311 { 312 return m_actions == TA_CANCEL_TOOL; 313 } 314 IsActivate()315 bool IsActivate() const 316 { 317 return m_actions == TA_ACTIVATE; 318 } 319 IsUndoRedo()320 bool IsUndoRedo() const 321 { 322 return m_actions & ( TA_UNDO_REDO_PRE | TA_UNDO_REDO_POST ); 323 } 324 IsChoiceMenu()325 bool IsChoiceMenu() const 326 { 327 return m_actions & TA_CHOICE_MENU; 328 } 329 IsPrime()330 bool IsPrime() const 331 { 332 return m_actions == TA_PRIME; 333 } 334 335 ///< Returns information about key modifiers state (Ctrl, Alt, etc.) 336 int Modifier( int aMask = MD_MODIFIER_MASK ) const 337 { 338 return m_modifiers & aMask; 339 } 340 DisableGridSnapping()341 bool DisableGridSnapping() const 342 { 343 return Modifier( MD_CTRL ); 344 } 345 KeyCode()346 int KeyCode() const 347 { 348 return m_keyCode; 349 } 350 IsKeyPressed()351 bool IsKeyPressed() const 352 { 353 return m_actions == TA_KEY_PRESSED; 354 } 355 356 /** 357 * Test whether two events match in terms of category & action or command. 358 * 359 * @param aEvent is the event to test against. 360 * @return True if two events match, false otherwise. 361 */ Matches(const TOOL_EVENT & aEvent)362 bool Matches( const TOOL_EVENT& aEvent ) const 363 { 364 if( !( m_category & aEvent.m_category ) ) 365 return false; 366 367 if( m_category == TC_COMMAND || m_category == TC_MESSAGE ) 368 { 369 if( (bool) m_commandStr && (bool) aEvent.m_commandStr ) 370 return *m_commandStr == *aEvent.m_commandStr; 371 372 if( (bool) m_commandId && (bool) aEvent.m_commandId ) 373 return *m_commandId == *aEvent.m_commandId; 374 } 375 376 // BUGFIX: TA_ANY should match EVERYTHING, even TA_NONE (for TC_MESSAGE) 377 if( m_actions == TA_ANY && aEvent.m_actions == TA_NONE && aEvent.m_category == TC_MESSAGE ) 378 return true; 379 380 // BUGFIX: This check must happen after the TC_COMMAND check because otherwise events of 381 // the form { TC_COMMAND, TA_NONE } will be incorrectly skipped 382 if( !( m_actions & aEvent.m_actions ) ) 383 return false; 384 385 return true; 386 } 387 388 /** 389 * Test if the event contains an action issued upon activation of the given #TOOL_ACTION. 390 * 391 * @param aAction is the TOOL_ACTION to be checked against. 392 * @return True if it matches the given TOOL_ACTION. 393 */ 394 bool IsAction( const TOOL_ACTION* aAction ) const; 395 396 /** 397 * Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc 398 * key, click cancel, start different tool). 399 */ 400 bool IsCancelInteractive() const; 401 402 /** 403 * Indicate an selection-changed notification event. 404 */ 405 bool IsSelectionEvent() const; 406 407 /** 408 * Indicate if the event is from one of the point editors. 409 * 410 * Usually used to allow the point editor to activate itself without de-activating the 411 * current drawing tool. 412 */ 413 bool IsPointEditor() const; 414 415 /** 416 * Indicate if the event is from one of the move tools. 417 * 418 * Usually used to allow move to be done without de-activating the current drawing tool. 419 */ 420 bool IsMoveTool() const; 421 422 /** 423 * Indicate if the event is from the simulator. 424 */ 425 bool IsSimulator() const; 426 427 /** 428 * Return a non-standard parameter assigned to the event. Its meaning depends on the 429 * target tool. 430 */ 431 template<typename T> Parameter()432 inline T Parameter() const 433 { 434 // Exhibit #798 on why I love to hate C++ 435 // - reinterpret_cast needs to be used for pointers 436 // - static_cast must be used for enums 437 // - templates can't usefully distinguish between pointer and non-pointer types 438 // Fortunately good old C's cast can be a reinterpret_cast or a static_cast, and 439 // C99 gave us intptr_t which is guaranteed to be round-trippable with a pointer. 440 return (T) reinterpret_cast<intptr_t>( m_param ); 441 } 442 443 /** 444 * Set a non-standard parameter assigned to the event. Its meaning depends on the 445 * target tool. 446 * 447 * @param aParam is the new parameter. 448 */ 449 template<typename T> SetParameter(T aParam)450 void SetParameter(T aParam) 451 { 452 m_param = reinterpret_cast<void*>( aParam ); 453 } 454 GetCommandId()455 OPT<int> GetCommandId() const 456 { 457 return m_commandId; 458 } 459 GetCommandStr()460 OPT<std::string> GetCommandStr() const 461 { 462 return m_commandStr; 463 } 464 SetMousePosition(const VECTOR2D & aP)465 void SetMousePosition( const VECTOR2D& aP ) 466 { 467 m_mousePos = aP; 468 } 469 470 private: 471 friend class TOOL_DISPATCHER; 472 473 void init(); 474 setMouseDragOrigin(const VECTOR2D & aP)475 void setMouseDragOrigin( const VECTOR2D& aP ) 476 { 477 m_mouseDragOrigin = aP; 478 } 479 setMouseDelta(const VECTOR2D & aP)480 void setMouseDelta( const VECTOR2D& aP ) 481 { 482 m_mouseDelta = aP; 483 } 484 setMouseButtons(int aButtons)485 void setMouseButtons( int aButtons ) 486 { 487 assert( ( aButtons & ~BUT_BUTTON_MASK ) == 0 ); 488 m_mouseButtons = aButtons; 489 } 490 setModifiers(int aMods)491 void setModifiers( int aMods ) 492 { 493 assert( ( aMods & ~MD_MODIFIER_MASK ) == 0 ); 494 m_modifiers = aMods; 495 } 496 497 /** 498 * Ensure that the event is a type that has a position before returning a 499 * position, otherwise return a null-constructed position. 500 * 501 * Used to defend the position accessors from runtime access when the event 502 * does not have a valid position. 503 * 504 * @param aPos the position to return if the event is valid 505 * @return the checked position 506 */ 507 VECTOR2D returnCheckedPosition( const VECTOR2D& aPos ) const; 508 509 TOOL_EVENT_CATEGORY m_category; 510 TOOL_ACTIONS m_actions; 511 TOOL_ACTION_SCOPE m_scope; 512 bool m_passEvent; 513 bool m_hasPosition; 514 bool m_forceImmediate; 515 516 ///< True when the tool is being re-activated from the stack 517 bool m_reactivate; 518 519 ///< Difference between mouse cursor position and 520 ///< the point where dragging event has started 521 VECTOR2D m_mouseDelta; 522 523 ///< Current mouse cursor position 524 VECTOR2D m_mousePos; 525 526 ///< Point where dragging has started 527 VECTOR2D m_mouseDragOrigin; 528 529 ///< State of mouse buttons 530 int m_mouseButtons; 531 532 ///< Stores code of pressed/released key 533 int m_keyCode; 534 535 ///< State of key modifiers (Ctrl/Alt/etc.) 536 int m_modifiers; 537 538 ///< Generic parameter used for passing non-standard data. 539 void* m_param; 540 541 ///< The first tool to receive the event 542 TOOL_BASE* m_firstResponder; 543 544 OPT<int> m_commandId; 545 OPT<std::string> m_commandStr; 546 }; 547 548 typedef OPT<TOOL_EVENT> OPT_TOOL_EVENT; 549 550 /** 551 * A list of TOOL_EVENTs, with overloaded || operators allowing for concatenating TOOL_EVENTs 552 * with little code. 553 */ 554 class TOOL_EVENT_LIST 555 { 556 public: 557 typedef TOOL_EVENT value_type; 558 typedef std::deque<TOOL_EVENT>::iterator iterator; 559 typedef std::deque<TOOL_EVENT>::const_iterator const_iterator; 560 561 ///< Default constructor. Creates an empty list. TOOL_EVENT_LIST()562 TOOL_EVENT_LIST() 563 {} 564 565 ///< Constructor for a list containing only one TOOL_EVENT. TOOL_EVENT_LIST(const TOOL_EVENT & aSingleEvent)566 TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent ) 567 { 568 m_events.push_back( aSingleEvent ); 569 } 570 571 ///< Copy an existing TOOL_EVENT_LIST TOOL_EVENT_LIST(const TOOL_EVENT_LIST & aEventList)572 TOOL_EVENT_LIST( const TOOL_EVENT_LIST& aEventList ) 573 { 574 m_events.clear(); 575 576 for( const TOOL_EVENT& event : aEventList.m_events ) 577 m_events.push_back( event ); 578 } 579 580 /** 581 * Function Format() 582 * Returns information about event in form of a human-readable string. 583 * 584 * @return Event information. 585 */ 586 const std::string Format() const; 587 Matches(const TOOL_EVENT & aEvent)588 OPT<const TOOL_EVENT&> Matches( const TOOL_EVENT& aEvent ) const 589 { 590 for( const TOOL_EVENT& event : m_events ) 591 { 592 if( event.Matches( aEvent ) ) 593 return event; 594 } 595 596 return OPT<const TOOL_EVENT&>(); 597 } 598 599 /** 600 * Add a tool event to the list. 601 * 602 * @param aEvent is the tool event to be added. 603 */ Add(const TOOL_EVENT & aEvent)604 void Add( const TOOL_EVENT& aEvent ) 605 { 606 m_events.push_back( aEvent ); 607 } 608 begin()609 iterator begin() 610 { 611 return m_events.begin(); 612 } 613 end()614 iterator end() 615 { 616 return m_events.end(); 617 } 618 cbegin()619 const_iterator cbegin() const 620 { 621 return m_events.begin(); 622 } 623 cend()624 const_iterator cend() const 625 { 626 return m_events.end(); 627 } 628 size()629 int size() const 630 { 631 return m_events.size(); 632 } 633 clear()634 void clear() 635 { 636 m_events.clear(); 637 } 638 639 TOOL_EVENT_LIST& operator=( const TOOL_EVENT_LIST& aEventList ) 640 { 641 m_events.clear(); 642 643 for( const TOOL_EVENT& event : aEventList.m_events ) 644 m_events.push_back( event ); 645 646 return *this; 647 } 648 649 TOOL_EVENT_LIST& operator=( const TOOL_EVENT& aEvent ) 650 { 651 m_events.clear(); 652 m_events.push_back( aEvent ); 653 return *this; 654 } 655 656 TOOL_EVENT_LIST& operator||( const TOOL_EVENT& aEvent ) 657 { 658 Add( aEvent ); 659 return *this; 660 } 661 662 TOOL_EVENT_LIST& operator||( const TOOL_EVENT_LIST& aEvent ) 663 { 664 std::copy( aEvent.m_events.begin(), aEvent.m_events.end(), std::back_inserter( m_events ) ); 665 return *this; 666 } 667 668 private: 669 std::deque<TOOL_EVENT> m_events; 670 }; 671 672 673 inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEventA, const TOOL_EVENT& aEventB ) 674 { 675 TOOL_EVENT_LIST l; 676 677 l.Add( aEventA ); 678 l.Add( aEventB ); 679 680 return l; 681 } 682 683 684 inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEvent, 685 const TOOL_EVENT_LIST& aEventList ) 686 { 687 TOOL_EVENT_LIST l( aEventList ); 688 689 l.Add( aEvent ); 690 return l; 691 } 692 693 694 #endif 695