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  * @author Maciej Suminski <maciej.suminski@cern.ch>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23  * or you may search the http://www.gnu.org website for the version 2 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
26  */
27 
28 #ifndef __TOOL_MANAGER_H
29 #define __TOOL_MANAGER_H
30 
31 #include <typeinfo>
32 #include <map>
33 #include <list>
34 #include <stack>
35 
36 #include <tool/tool_base.h>
37 #include <tool/tool_event.h>
38 #include <view/view_controls.h>
39 
40 class TOOLS_HOLDER;
41 class TOOL_ACTION;
42 class TOOL_BASE;
43 class ACTION_MANAGER;
44 class ACTION_MENU;
45 class APP_SETTINGS_BASE;
46 
47 
48 /**
49  * Master controller class:
50  * - registers editing tools
51  * - pumps UI events to tools requesting them
52  * - manages tool state machines (transitions and wait requests)
53  */
54 class TOOL_MANAGER
55 {
56 private:
57     struct TOOL_STATE;
58 
59 public:
60     TOOL_MANAGER();
61 
62     ~TOOL_MANAGER();
63 
64     // Helper typedefs
65     typedef std::map<TOOL_BASE*, TOOL_STATE*> TOOL_STATE_MAP;
66     typedef std::map<std::string, TOOL_STATE*> NAME_STATE_MAP;
67     typedef std::map<TOOL_ID, TOOL_STATE*> ID_STATE_MAP;
68     typedef std::list<TOOL_ID> ID_LIST;
69     typedef std::vector<TOOL_BASE*> TOOL_VEC;
70 
71     /**
72      * Generates a unique ID from for a tool with given name.
73      */
74     static TOOL_ID MakeToolId( const std::string& aToolName );
75 
76     /**
77      * Add a tool to the manager set and sets it up. Called once for each tool during
78      * application initialization.
79      *
80      * @param aTool: tool to be added. Ownership is transferred.
81      */
82     void RegisterTool( TOOL_BASE* aTool );
83 
84     /**
85      * Call a tool by sending a tool activation event to tool of given ID.
86      *
87      * @param aToolId is the ID number of the requested tool.
88      * @return True if the requested tool was invoked successfully.
89      */
90     bool InvokeTool( TOOL_ID aToolId );
91 
92     /**
93      * Call a tool by sending a tool activation event to tool of given name.
94      *
95      * @param aToolName is the name of the requested tool.
96      * @return True if the requested tool was invoked successfully.
97      */
98     bool InvokeTool( const std::string& aToolName );
99 
100     /**
101      * Shutdown all tools with a currently registered event loop in this tool manager
102      * by waking them up with a null event.
103      */
104     void ShutdownAllTools();
105 
106     /**
107      * Shutdown the specified tool by waking it up with a null event to terminate
108      * the processing loop.
109      *
110      * @param aTool is the tool to shutdown
111      */
112     void ShutdownTool( TOOL_BASE* aTool );
113 
114     /**
115      * Shutdown the specified tool by waking it up with a null event to terminate
116      * the processing loop.
117      *
118      * @param aToolId is the ID of the tool to shutdown
119      */
120     void ShutdownTool( TOOL_ID aToolId );
121 
122     /**
123      * Shutdown the specified tool by waking it up with a null event to terminate
124      * the processing loop.
125      *
126      * @param aToolName is name of the tool to shutdown
127      */
128     void ShutdownTool( const std::string& aToolName );
129 
130     /**
131      * Run the specified action.
132      *
133      * The common format for action names is "application.ToolName.Action".
134      *
135      * @param aActionName is the name of action to be invoked.
136      * @param aNow decides if the action has to be run immediately or after the current coroutine
137      *             is preemptied.
138      * @param aParam is an optional parameter that might be used by the invoked action. Its meaning
139      *               depends on the action.
140      * @return False if the action was not found.
141      */
142     template<typename T>
143     bool RunAction( const std::string& aActionName, bool aNow = false, T aParam = NULL )
144     {
145         return RunAction( aActionName, aNow, reinterpret_cast<void*>( aParam ) );
146     }
147 
148     bool RunAction( const std::string& aActionName, bool aNow, void* aParam );
149 
150     bool RunAction( const std::string& aActionName, bool aNow = false )
151     {
152         return RunAction( aActionName, aNow, (void*) NULL );
153     }
154 
155     /**
156      * Run the specified action.
157      *
158      * This function will only return if the action has been handled when the action is run
159      * immediately (aNow = true), otherwise it will always return false.
160      *
161      * @param aAction is the action to be invoked.
162      * @param aNow decides if the action has to be run immediately or after the current coroutine
163      *             is preemptied.
164      * @param aParam is an optional parameter that might be used by the invoked action. Its meaning
165      *               depends on the action.
166      * @return True if the action was handled immediately
167      */
168     template <typename T>
169     bool RunAction( const TOOL_ACTION& aAction, bool aNow = false, T aParam = NULL )
170     {
171         return RunAction( aAction, aNow, reinterpret_cast<void*>( aParam ) );
172     }
173 
174     bool RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam );
175 
176     bool RunAction( const TOOL_ACTION& aAction, bool aNow = false )
177     {
178         return RunAction( aAction, aNow, (void*) NULL );
179     }
180 
181     const std::map<std::string, TOOL_ACTION*>& GetActions() const;
182 
183     /**
184      * Send a cancel event to the tool currently at the top of the tool stack.
185      */
186     void CancelTool();
187 
188     /**
189      * "Prime" a tool by sending a cursor left-click event with the mouse position set
190      * to the passed in position.
191      *
192      * @param aPosition is the mouse position to use in the event
193      */
194     void PrimeTool( const VECTOR2D& aPosition );
195 
196     ///< @copydoc ACTION_MANAGER::GetHotKey()
197     int GetHotKey( const TOOL_ACTION& aAction ) const;
198 
GetActionManager()199     ACTION_MANAGER* GetActionManager() const { return m_actionMgr; }
200 
201     /**
202      * Search for a tool with given ID.
203      *
204      * @param aId is the ID number of the requested tool.
205      * @return Pointer to the requested tool or NULL in case of failure.
206      */
207     TOOL_BASE* FindTool( int aId ) const;
208 
209     /**
210      * Search for a tool with given name.
211      *
212      * @param aName is the name of the requested tool.
213      * @return Pointer to the requested tool or NULL in case of failure.
214      */
215     TOOL_BASE* FindTool( const std::string& aName ) const;
216 
217     /*
218      * Return the tool of given type or nullptr if there is no such tool registered.
219      */
220     template<typename T>
GetTool()221     T* GetTool()
222     {
223         std::map<const char*, TOOL_BASE*>::iterator tool = m_toolTypes.find( typeid( T ).name() );
224 
225         if( tool != m_toolTypes.end() )
226             return static_cast<T*>( tool->second );
227 
228         return nullptr;
229     }
230 
231     /**
232      * Deactivate the currently active tool.
233      */
234     void DeactivateTool();
235 
236 
237     /**
238      * Return true if a tool with given id is active (executing)
239      */
240      bool IsToolActive( TOOL_ID aId ) const;
241 
242 
243     /**
244      * Reset all tools (i.e. calls their Reset() method).
245      */
246     void ResetTools( TOOL_BASE::RESET_REASON aReason );
247 
248     /**
249      * Initializes all registered tools.
250      *
251      * If a tool fails during the initialization, it is deactivated and becomes unavailable
252      * for further use. Initialization should be done only once.
253      */
254     void InitTools();
255 
256     /**
257      * Propagate an event to tools that requested events of matching type(s).
258      *
259      * @param aEvent is the event to be processed.
260      * @return true if the event is a managed hotkey
261      */
262     bool ProcessEvent( const TOOL_EVENT& aEvent );
263 
264     /**
265      * Put an event to the event queue to be processed at the end of event processing cycle.
266      *
267      * @param aEvent is the event to be put into the queue.
268      */
269     void PostEvent( const TOOL_EVENT& aEvent );
270 
271     /**
272      * Set the work environment (model, view, view controls and the parent window).
273      *
274      * These are made available to the tool. Called by the parent frame when it is set up.
275      */
276     void SetEnvironment( EDA_ITEM* aModel, KIGFX::VIEW* aView,
277                          KIGFX::VIEW_CONTROLS* aViewControls, APP_SETTINGS_BASE* aSettings,
278                          TOOLS_HOLDER* aFrame );
279 
280     /*
281      * Accessors for the environment objects (view, model, etc.)
282      */
GetView()283     KIGFX::VIEW* GetView() const { return m_view; }
284 
GetViewControls()285     KIGFX::VIEW_CONTROLS* GetViewControls() const { return m_viewControls; }
286 
287     VECTOR2D GetMousePosition() const;
288     VECTOR2D GetCursorPosition() const;
289 
GetModel()290     EDA_ITEM* GetModel() const { return m_model; }
291 
GetSettings()292     APP_SETTINGS_BASE* GetSettings() const { return m_settings; }
293 
GetToolHolder()294     TOOLS_HOLDER* GetToolHolder() const { return m_frame; }
295 
296     /**
297      * Return id of the tool that is on the top of the active tools stack (was invoked the
298      * most recently).
299      *
300      * @return Id of the currently used tool.
301      */
GetCurrentToolId()302     inline int GetCurrentToolId() const
303     {
304         return m_activeTools.empty() ? -1 : m_activeTools.front();
305     }
306 
307     /**
308      * Return the tool that is on the top of the active tools stack (was invoked the most
309      * recently).
310      *
311      * @return Pointer to the currently used tool.
312      */
GetCurrentTool()313     inline TOOL_BASE* GetCurrentTool() const
314     {
315         return FindTool( GetCurrentToolId() );
316     }
317 
318     /**
319      * Return the #TOOL_STATE object representing the state of the active tool. If there are no
320      * tools active, it returns nullptr.
321      */
GetCurrentToolState()322     TOOL_STATE* GetCurrentToolState() const
323     {
324         auto it = m_toolIdIndex.find( GetCurrentToolId() );
325         return ( it != m_toolIdIndex.end() ) ? it->second : nullptr;
326     }
327 
328     /**
329      * Return priority of a given tool.
330      *
331      * Higher number means that the tool is closer to the beginning of the active tools
332      * queue (i.e. receives events earlier, tools with lower priority receive events later).
333      *
334      * @param aToolId is the id of queried tool.
335      * @return The priority of a given tool. If returned number is negative, then it means that
336      *         the tool id is invalid or the tool is not active.
337      */
338     int GetPriority( int aToolId ) const;
339 
340     /**
341      * Define a state transition.
342      *
343      * The events that cause a given handler method in the tool to be called. Called by
344      * TOOL_INTERACTIVE::Go(). May be called from a coroutine context.
345      */
346     void ScheduleNextState( TOOL_BASE* aTool, TOOL_STATE_FUNC& aHandler,
347                             const TOOL_EVENT_LIST& aConditions );
348 
349     /**
350      * Clear the state transition map for a tool.
351      *
352      * @param aTool is the tool that should have the transition map cleared.
353      */
354     void ClearTransitions( TOOL_BASE* aTool );
355 
356     void RunMainStack( TOOL_BASE* aTool, std::function<void()> aFunc );
357 
358     /**
359      * Update the status bar and synchronizes toolbars.
360      */
361     void UpdateUI( const TOOL_EVENT& aEvent );
362 
363     /**
364      * Pause execution of a given tool until one or more events matching aConditions arrives.
365      *
366      * The pause/resume operation is done through #COROUTINE object.  Called only from coroutines.
367      */
368     TOOL_EVENT* ScheduleWait( TOOL_BASE* aTool, const TOOL_EVENT_LIST& aConditions );
369 
370     /**
371      * Set behavior of the tool's context popup menu.
372      *
373      * @param aTool is the parent tool.
374      * @param aMenu is the menu structure, defined by the tool.
375      * @param aTrigger determines when the menu is activated:
376      *  CMENU_NOW: opens the menu right now
377      *  CMENU_BUTTON: opens the menu when RMB is pressed
378      *  CMENU_OFF: menu is disabled.
379      * May be called from a coroutine context.
380      */
381     void ScheduleContextMenu( TOOL_BASE* aTool, ACTION_MENU* aMenu, CONTEXT_MENU_TRIGGER aTrigger );
382 
383     /**
384      * Store information to the system clipboard.
385      *
386      * @param aText is the information to be stored, expected UTF8 encoding.  The text will be
387      *              stored as Unicode string (not stored as UTF8 string).
388      * @return False if error occurred.
389      */
390     bool SaveClipboard( const std::string& aTextUTF8 );
391 
392     /**
393      * Return the information currently stored in the system clipboard.
394      *
395      * If data stored in the clipboard is in non-text format, empty string is returned.
396      *
397      * @note The clipboard is expected containing Unicode chars, not only ASCII7 chars.
398      *       The returned string is UTF8 encoded
399      */
400     std::string GetClipboardUTF8() const;
401 
402     /**
403      * Return the view controls settings for the current tool or the general settings if there is
404      * no active tool.
405      */
406     const KIGFX::VC_SETTINGS& GetCurrentToolVC() const;
407 
408     /**
409      * True while processing a context menu.
410      */
IsContextMenuActive()411     bool IsContextMenuActive() const
412     {
413         return m_menuActive;
414     }
415 
416     /**
417      * Disable mouse warping after the current context menu is closed.
418      *
419      * This must be called before invoking each context menu.  It's a good idea to call this
420      * from non-modal dialogs (e.g. DRC window).
421      */
VetoContextMenuMouseWarp()422     void VetoContextMenuMouseWarp()
423     {
424         m_warpMouseAfterContextMenu = false;
425     }
426 
427     /**
428      * Handle context menu related events.
429      */
430     void DispatchContextMenu( const TOOL_EVENT& aEvent );
431 
432     /**
433      * Handle specific events, that are intended for TOOL_MANAGER rather than tools.
434      *
435      * @param aEvent is the event to be processed.
436      * @return true if the event was processed and should not go any further.
437      */
438     bool DispatchHotKey( const TOOL_EVENT& aEvent );
439 
GetMenuCursorPos()440     VECTOR2D GetMenuCursorPos() const
441     {
442         return m_menuCursor;
443     }
444 
445 private:
446     typedef std::pair<TOOL_EVENT_LIST, TOOL_STATE_FUNC> TRANSITION;
447 
448     /**
449      * Pass an event at first to the active tools, then to all others.
450      */
451     bool dispatchInternal( TOOL_EVENT& aEvent );
452 
453     /**
454      * Check if it is a valid activation event and invokes a proper tool.
455      *
456      * @param aEvent is an event to be tested.
457      * @return True if a tool was invoked, false otherwise.
458      */
459     bool dispatchActivation( const TOOL_EVENT& aEvent );
460 
461     /**
462      * Invoke a tool by sending a proper event (in contrary to runTool, which makes the tool run
463      * for real).
464      *
465      * @param aTool is the tool to be invoked.
466      */
467     bool invokeTool( TOOL_BASE* aTool );
468 
469     /**
470      * Make a tool active, so it can receive events and react to them.
471      *
472      * The activated tool is pushed on the active tools stack, so the last activated tool
473      * receives events first.
474      *
475      * @param aTool is the tool to be run.
476      */
477     bool runTool( TOOL_BASE* aTool );
478 
479     /**
480      * Deactivate a tool and does the necessary clean up.
481      *
482      * @param aState is the state variable of the tool to be stopped.
483      * @return m_activeTools iterator. If the tool has been completely deactivated, it points
484      *         to the next active tool on the list. Otherwise it is an iterator pointing to
485      *         \a aState.
486      */
487     ID_LIST::iterator finishTool( TOOL_STATE* aState );
488 
489     /**
490      * Return information about a tool registration status.
491      *
492      * @param aTool is the tool to be checked.
493      * @return true if the tool is in the registered tools list, false otherwise.
494      */
isRegistered(TOOL_BASE * aTool)495     bool isRegistered( TOOL_BASE* aTool ) const
496     {
497         return m_toolState.count( aTool ) > 0;
498     }
499 
500     /**
501      * Return information about a tool activation status.
502      *
503      * @param aTool is the tool to be checked.
504      * @return True if the tool is on the active tools stack, false otherwise.
505      */
506     bool isActive( TOOL_BASE* aTool ) const;
507 
508     /**
509      * Save the #VIEW_CONTROLS settings to the tool state object.
510      *
511      * If #VIEW_CONTROLS settings are affected by #TOOL_MANAGER, the original settings are saved.
512      */
513     void saveViewControls( TOOL_STATE* aState );
514 
515     /**
516      * Apply #VIEW_CONTROLS settings stored in a #TOOL_STATE object.
517      */
518     void applyViewControls( const TOOL_STATE* aState );
519 
520     /**
521      * Main function for event processing.
522      *
523      * @return true if a hotkey was handled.
524      */
525     bool processEvent( const TOOL_EVENT& aEvent );
526 
527     /**
528      * Save the previous active state and sets a new one.
529      *
530      * @param aState is the new active state. Might be null to indicate there is no new
531      *               active state.
532      */
533     void setActiveState( TOOL_STATE* aState );
534 
535     ///< List of tools in the order they were registered
536     TOOL_VEC m_toolOrder;
537 
538     ///< Index of registered tools current states, associated by tools' objects.
539     TOOL_STATE_MAP m_toolState;
540 
541     ///< Index of the registered tools current states, associated by tools' names.
542     NAME_STATE_MAP m_toolNameIndex;
543 
544     ///< Index of the registered tools current states, associated by tools' ID numbers.
545     ID_STATE_MAP m_toolIdIndex;
546 
547     ///< Index of the registered tools to easily lookup by their type.
548     std::map<const char*, TOOL_BASE*> m_toolTypes;
549 
550     ///< Stack of the active tools
551     ID_LIST m_activeTools;
552 
553     ///< Instance of ACTION_MANAGER that handles TOOL_ACTIONs
554     ACTION_MANAGER* m_actionMgr;
555 
556     ///< Original cursor position, if overridden by the context menu handler
557     std::map<TOOL_ID, OPT<VECTOR2D>> m_cursorSettings;
558 
559     EDA_ITEM*             m_model;
560     KIGFX::VIEW*          m_view;
561     KIGFX::VIEW_CONTROLS* m_viewControls;
562     TOOLS_HOLDER*         m_frame;
563     APP_SETTINGS_BASE*    m_settings;
564 
565     ///< Queue that stores events to be processed at the end of the event processing cycle.
566     std::list<TOOL_EVENT> m_eventQueue;
567 
568     ///< Right click context menu position.
569     VECTOR2D m_menuCursor;
570 
571     bool m_warpMouseAfterContextMenu;
572 
573     ///< Flag indicating whether a context menu is currently displayed.
574     bool m_menuActive;
575 
576     ///< Tool currently displaying a popup menu. It is negative when there is no menu displayed.
577     TOOL_ID m_menuOwner;
578 
579     ///< Pointer to the state object corresponding to the currently executed tool.
580     TOOL_STATE* m_activeState;
581 };
582 
583 #endif // __TOOL_MANAGER_H
584