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