You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
19 KiB

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