KiCad PCB EDA Suite
tool_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2018 CERN
5 * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
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#include <core/kicad_algo.h>
28#include <optional>
29#include <map>
30#include <stack>
31#include <trace_helpers.h>
32
33#include <wx/event.h>
34#include <wx/clipbrd.h>
35#include <wx/app.h>
36
37#include <view/view.h>
38#include <eda_base_frame.h>
39#include <tool/tool_base.h>
41#include <tool/tool_manager.h>
42#include <tool/action_menu.h>
43#include <tool/coroutine.h>
44#include <tool/action_manager.h>
45
47
50{
52 theTool( aTool )
53 {
54 clear();
55 }
56
57 TOOL_STATE( const TOOL_STATE& aState )
58 {
59 theTool = aState.theTool;
60 idle = aState.idle;
61 shutdown = aState.shutdown;
62 pendingWait = aState.pendingWait;
64 contextMenu = aState.contextMenu;
66 cofunc = aState.cofunc;
68 wakeupEvent = aState.wakeupEvent;
69 waitEvents = aState.waitEvents;
70 transitions = aState.transitions;
71 vcSettings = aState.vcSettings;
72 // do not copy stateStack
73 }
74
76 {
77 if( !stateStack.empty() )
78 wxFAIL;
79 }
80
83
85 bool idle;
86
89
93
96
99
102
105
108
111
114
117 std::vector<TRANSITION> transitions;
118
121
123 {
124 theTool = aState.theTool;
125 idle = aState.idle;
126 shutdown = aState.shutdown;
127 pendingWait = aState.pendingWait;
129 contextMenu = aState.contextMenu;
131 cofunc = aState.cofunc;
132 initialEvent = aState.initialEvent;
133 wakeupEvent = aState.wakeupEvent;
134 waitEvents = aState.waitEvents;
135 transitions = aState.transitions;
136 vcSettings = aState.vcSettings;
137 // do not copy stateStack
138 return *this;
139 }
140
141 bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
142 {
143 return aRhs.theTool == theTool;
144 }
145
146 bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
147 {
148 return aRhs.theTool != theTool;
149 }
150
155 void Push()
156 {
157 auto state = std::make_unique<TOOL_STATE>( *this );
158 stateStack.push( std::move( state ) );
159 clear();
160 }
161
168 bool Pop()
169 {
170 delete cofunc;
171
172 if( !stateStack.empty() )
173 {
174 *this = *stateStack.top().get();
175 stateStack.pop();
176 return true;
177 }
178 else
179 {
180 cofunc = nullptr;
181 return false;
182 }
183 }
184
185private:
187 std::stack<std::unique_ptr<TOOL_STATE>> stateStack;
188
190 void clear()
191 {
192 idle = true;
193 shutdown = false;
194 pendingWait = false;
195 pendingContextMenu = false;
196 cofunc = nullptr;
197 contextMenu = nullptr;
200 transitions.clear();
201 }
202};
203
204
206 m_model( nullptr ),
207 m_view( nullptr ),
208 m_viewControls( nullptr ),
209 m_frame( nullptr ),
210 m_settings( nullptr ),
211 m_warpMouseAfterContextMenu( true ),
212 m_menuActive( false ),
213 m_menuOwner( -1 ),
214 m_activeState( nullptr ),
215 m_shuttingDown( false )
216{
217 m_actionMgr = new ACTION_MANAGER( this );
218}
219
220
222{
223 std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end;
224
225 for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it )
226 {
227 delete it->second->cofunc; // delete cofunction
228 delete it->second; // delete TOOL_STATE
229 delete it->first; // delete the tool itself
230 }
231
232 delete m_actionMgr;
233}
234
235
237{
238 wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(),
239 wxT( "Adding two tools with the same name may result in unexpected behavior.") );
240 wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(),
241 wxT( "Adding two tools with the same ID may result in unexpected behavior.") );
242 wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(),
243 wxT( "Adding two tools of the same type may result in unexpected behavior.") );
244
245 m_toolOrder.push_back( aTool );
246
247 TOOL_STATE* st = new TOOL_STATE( aTool );
248
249 m_toolState[aTool] = st;
250 m_toolNameIndex[aTool->GetName()] = st;
251 m_toolIdIndex[aTool->GetId()] = st;
252 m_toolTypes[typeid( *aTool ).name()] = st->theTool;
253
254 aTool->attachManager( this );
255}
256
257
259{
260 TOOL_BASE* tool = FindTool( aToolId );
261
262 if( tool && tool->GetType() == INTERACTIVE )
263 return invokeTool( tool );
264
265 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::InvokeTool - no tool with ID %d",
266 aToolId );
267
268 return false; // there is no tool with the given id
269}
270
271
272bool TOOL_MANAGER::InvokeTool( const std::string& aToolName )
273{
274 TOOL_BASE* tool = FindTool( aToolName );
275
276 if( tool && tool->GetType() == INTERACTIVE )
277 return invokeTool( tool );
278
279 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::InvokeTool - no tool with name %s",
280 aToolName );
281
282 return false; // there is no tool with the given name
283}
284
285
286bool TOOL_MANAGER::RunAction( const std::string& aActionName, bool aNow, void* aParam )
287{
288 TOOL_ACTION* action = m_actionMgr->FindAction( aActionName );
289
290 if( !action )
291 {
292 wxASSERT_MSG( false, wxString::Format( "Could not find action %s.", aActionName ) );
293 return false;
294 }
295
296 RunAction( *action, aNow, aParam );
297
298 return false;
299}
300
301
303{
304 if( m_viewControls )
306 else
307 return wxGetMousePosition();
308}
309
310
312{
313 if( m_viewControls )
315 else
316 return wxGetMousePosition();
317}
318
319
320bool TOOL_MANAGER::RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam )
321{
322 if( m_shuttingDown )
323 return true;
324
325 bool handled = false;
326 TOOL_EVENT event = aAction.MakeEvent();
327
328 if( event.Category() == TC_COMMAND )
330
331 // Allow to override the action parameter
332 if( aParam )
333 event.SetParameter( aParam );
334
335 if( aNow )
336 {
337 TOOL_STATE* current = m_activeState;
338 handled = processEvent( event );
339 setActiveState( current );
340 UpdateUI( event );
341 }
342 else
343 {
344 PostEvent( event );
345 }
346
347 return handled;
348}
349
350
352{
354
355 processEvent( evt );
356}
357
358
359void TOOL_MANAGER::PrimeTool( const VECTOR2D& aPosition )
360{
361 int modifiers = 0;
362
363 /*
364 * Don't include any modifiers. They're part of the hotkey, not part of the resulting
365 * click.
366 *
367 * modifiers |= wxGetKeyState( WXK_SHIFT ) ? MD_SHIFT : 0;
368 * modifiers |= wxGetKeyState( WXK_CONTROL ) ? MD_CTRL : 0;
369 * modifiers |= wxGetKeyState( WXK_ALT ) ? MD_ALT : 0;
370 */
371
372 TOOL_EVENT evt( TC_MOUSE, TA_PRIME, BUT_LEFT | modifiers );
373 evt.SetMousePosition( aPosition );
374
375 PostEvent( evt );
376}
377
378
380{
381 // Horrific hack, but it's a crash bug. Don't let inter-frame commands stack up
382 // waiting to be processed.
383 if( aEvent.IsSimulator() && m_eventQueue.size() > 0 && m_eventQueue.back().IsSimulator() )
384 m_eventQueue.pop_back();
385
386 m_eventQueue.push_back( aEvent );
387}
388
389
390int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
391{
392 return m_actionMgr->GetHotKey( aAction );
393}
394
395
397{
398 wxASSERT( aTool != nullptr );
399
400 TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() );
402 processEvent( evt );
403
404 if( TOOL_STATE* active = GetCurrentToolState() )
405 setActiveState( active );
406
407 return true;
408}
409
410
412{
413 wxASSERT( aTool != nullptr );
414
415 if( !isRegistered( aTool ) )
416 {
417 wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) );
418 return false;
419 }
420
421 TOOL_ID id = aTool->GetId();
422
423 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::runTool - running tool %s",
424 aTool->GetName() );
425
426 if( aTool->GetType() == INTERACTIVE )
427 static_cast<TOOL_INTERACTIVE*>( aTool )->resetTransitions();
428
429 // If the tool is already active, bring it to the top of the active tools stack
430 if( isActive( aTool ) && m_activeTools.size() > 1 )
431 {
432 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), id );
433
434 if( it != m_activeTools.end() )
435 {
436 if( it != m_activeTools.begin() )
437 {
438 m_activeTools.erase( it );
439 m_activeTools.push_front( id );
440 }
441
442 return false;
443 }
444 }
445
448
449 // Add the tool on the front of the processing queue (it gets events first)
450 m_activeTools.push_front( id );
451
452 return true;
453}
454
455
457{
458 m_shuttingDown = true;
459
460 // Create a temporary list of tools to iterate over since when the tools shutdown
461 // they remove themselves from the list automatically (invalidating the iterator)
462 ID_LIST tmpList = m_activeTools;
463
464 // Make sure each tool knows that it is shutting down, so that loops get shut down
465 // at the dispatcher
466 for( auto id : tmpList )
467 {
468 if( m_toolIdIndex.count( id ) == 0 )
469 continue;
470
471 m_toolIdIndex[id]->shutdown = true;
472 }
473
474 for( auto id : tmpList )
475 {
476 ShutdownTool( id );
477 }
478}
479
480
482{
483 TOOL_BASE* tool = FindTool( aToolId );
484
485 if( tool && tool->GetType() == INTERACTIVE )
486 ShutdownTool( tool );
487
488 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::ShutdownTool - no tool with ID %d",
489 aToolId );
490}
491
492
493void TOOL_MANAGER::ShutdownTool( const std::string& aToolName )
494{
495 TOOL_BASE* tool = FindTool( aToolName );
496
497 if( tool && tool->GetType() == INTERACTIVE )
498 ShutdownTool( tool );
499
500 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::ShutdownTool - no tool with name %s",
501 aToolName );
502}
503
504
506{
507 wxASSERT( aTool != nullptr );
508
509 TOOL_ID id = aTool->GetId();
510
511 if( isActive( aTool ) )
512 {
513 TOOL_MANAGER::ID_LIST::iterator it = std::find( m_activeTools.begin(),
514 m_activeTools.end(), id );
515
516 TOOL_STATE* st = m_toolIdIndex[*it];
517
518 // the tool state handler is waiting for events (i.e. called Wait() method)
519 if( st && st->pendingWait )
520 {
521 // Wake up the tool and tell it to shutdown
522 st->shutdown = true;
523 st->pendingWait = false;
524 st->waitEvents.clear();
525
526 if( st->cofunc )
527 {
528 wxLogTrace( kicadTraceToolStack,
529 "TOOL_MANAGER::ShutdownTool - Shutting down tool %s",
530 st->theTool->GetName() );
531
532 setActiveState( st );
533 bool end = !st->cofunc->Resume();
534
535 if( end )
536 finishTool( st );
537 }
538 }
539 }
540}
541
542
544{
545 std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
546
547 if( it != m_toolIdIndex.end() )
548 return it->second->theTool;
549
550 return nullptr;
551}
552
553
554TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const
555{
556 std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName );
557
558 if( it != m_toolNameIndex.end() )
559 return it->second->theTool;
560
561 return nullptr;
562}
563
564
566{
567 // Deactivate the active tool, but do not run anything new
569 processEvent( evt );
570}
571
572
574{
576
577 for( auto& state : m_toolState )
578 {
579 TOOL_BASE* tool = state.first;
580 setActiveState( state.second );
581 tool->Reset( aReason );
582
583 if( tool->GetType() == INTERACTIVE )
584 static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
585 }
586}
587
588
590{
591 for( auto it = m_toolOrder.begin(); it != m_toolOrder.end(); /* iter inside */ )
592 {
593 TOOL_BASE* tool = *it;
594 wxASSERT( m_toolState.count( tool ) );
595 TOOL_STATE* state = m_toolState[tool];
596 setActiveState( state );
597 ++it; // keep the iterator valid if the element is going to be erased
598
599 if( !tool->Init() )
600 {
601 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER initialization of tool '%s' failed",
602 tool->GetName() );
603
604 // Unregister the tool
605 setActiveState( nullptr );
606 m_toolState.erase( tool );
607 m_toolNameIndex.erase( tool->GetName() );
608 m_toolIdIndex.erase( tool->GetId() );
609 m_toolTypes.erase( typeid( *tool ).name() );
610
611 delete state;
612 delete tool;
613 }
614 }
615
616 m_actionMgr->UpdateHotKeys( true );
617
619}
620
621
622int TOOL_MANAGER::GetPriority( int aToolId ) const
623{
624 int priority = 0;
625
626 for( TOOL_ID tool : m_activeTools )
627 {
628 if( tool == aToolId )
629 return priority;
630
631 ++priority;
632 }
633
634 return -1;
635}
636
637
639 const TOOL_EVENT_LIST& aConditions )
640{
641 TOOL_STATE* st = m_toolState[aTool];
642
643 st->transitions.emplace_back( TRANSITION( aConditions, aHandler ) );
644}
645
646
648{
649 m_toolState[aTool]->transitions.clear();
650}
651
652
653void TOOL_MANAGER::RunMainStack( TOOL_BASE* aTool, std::function<void()> aFunc )
654{
655 TOOL_STATE* st = m_toolState[aTool];
656 setActiveState( st );
657 st->cofunc->RunMainStack( std::move( aFunc ) );
658}
659
660
662{
663 TOOL_STATE* st = m_toolState[aTool];
664
665 wxASSERT( !st->pendingWait ); // everything collapses on two KiYield() in a row
666
667 // indicate to the manager that we are going to sleep and we shall be
668 // woken up when an event matching aConditions arrive
669 st->pendingWait = true;
670 st->waitEvents = aConditions;
671
672 // switch context back to event dispatcher loop
673 st->cofunc->KiYield();
674
675 // If the tool should shutdown, it gets a null event to break the loop
676 if( st->shutdown )
677 return nullptr;
678 else
679 return &st->wakeupEvent;
680}
681
682
684{
685 bool handled = false;
686
687 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::dispatchInternal - received event: %s",
688 aEvent.Format() );
689
690 auto it = m_activeTools.begin();
691
692 // iterate over active tool stack
693 while( it != m_activeTools.end() )
694 {
695 TOOL_STATE* st = m_toolIdIndex[*it];
696 bool increment = true;
697
698 // forward context menu events to the tool that created the menu
699 if( aEvent.IsChoiceMenu() )
700 {
701 if( *it != m_menuOwner )
702 {
703 ++it;
704 continue;
705 }
706 }
707
708 // If we're pendingWait then we had better have a cofunc to process the wait.
709 wxASSERT( !st || !st->pendingWait || st->cofunc );
710
711 // the tool state handler is waiting for events (i.e. called Wait() method)
712 if( st && st->cofunc && st->pendingWait && st->waitEvents.Matches( aEvent ) )
713 {
714 if( !aEvent.FirstResponder() )
715 aEvent.SetFirstResponder( st->theTool );
716
717 // got matching event? clear wait list and wake up the coroutine
718 st->wakeupEvent = aEvent;
719 st->pendingWait = false;
720 st->waitEvents.clear();
721
722 wxLogTrace( kicadTraceToolStack,
723 "TOOL_MANAGER::dispatchInternal - Waking tool %s for event: %s",
724 st->theTool->GetName(), aEvent.Format() );
725
726 setActiveState( st );
727 bool end = !st->cofunc->Resume();
728
729 if( end )
730 {
731 it = finishTool( st );
732 increment = false;
733 }
734
735 // If the tool did not request the event be passed to other tools, we're done
736 if( !st->wakeupEvent.PassEvent() )
737 {
738 wxLogTrace( kicadTraceToolStack,
739 "TOOL_MANAGER::dispatchInternal - tool %s stopped passing event: %s",
740 st->theTool->GetName(), aEvent.Format() );
741
742 return true;
743 }
744 }
745
746 if( increment )
747 ++it;
748 }
749
750 for( const auto& state : m_toolState )
751 {
752 TOOL_STATE* st = state.second;
753 bool finished = false;
754
755 // no state handler in progress - check if there are any transitions (defined by
756 // Go() method that match the event.
757 if( !st->transitions.empty() )
758 {
759 for( const TRANSITION& tr : st->transitions )
760 {
761 if( tr.first.Matches( aEvent ) )
762 {
763 auto func_copy = tr.second;
764
765 if( !aEvent.FirstResponder() )
766 aEvent.SetFirstResponder( st->theTool );
767
768 // if there is already a context, then push it on the stack
769 // and transfer the previous view control settings to the new context
770 if( st->cofunc )
771 {
772 auto vc = st->vcSettings;
773 st->Push();
774 st->vcSettings = vc;
775 }
776
777 st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( std::move( func_copy ) );
778
779 // as the state changes, the transition table has to be set up again
780 st->transitions.clear();
781
782 wxLogTrace( kicadTraceToolStack,
783 "TOOL_MANAGER::dispatchInternal - Running tool %s for event: %s",
784 st->theTool->GetName(), aEvent.Format() );
785
786 // got match? Run the handler.
787 setActiveState( st );
788 st->idle = false;
789 st->initialEvent = aEvent;
790 st->cofunc->Call( st->initialEvent );
791 handled = true;
792
793 if( !st->cofunc->Running() )
794 finishTool( st ); // The coroutine has finished immediately?
795
796 // if it is a message, continue processing
797 finished = !( aEvent.Category() == TC_MESSAGE );
798
799 // there is no point in further checking, as transitions got cleared
800 break;
801 }
802 }
803 }
804
805 if( finished )
806 break; // only the first tool gets the event
807 }
808
809 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::dispatchInternal - %s handle event: %s",
810 ( handled ? "Did" : "Did not" ), aEvent.Format() );
811
812 return handled;
813}
814
815
817{
818 if( aEvent.Action() == TA_KEY_PRESSED )
819 return m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() );
820
821 return false;
822}
823
824
826{
827 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::dispatchActivation - Received event: %s",
828 aEvent.Format() );
829
830 if( aEvent.IsActivate() )
831 {
832 auto tool = m_toolNameIndex.find( aEvent.getCommandStr() );
833
834 if( tool != m_toolNameIndex.end() )
835 {
836 wxLogTrace( kicadTraceToolStack,
837 "TOOL_MANAGER::dispatchActivation - Running tool %s for event: %s",
838 tool->second->theTool->GetName(), aEvent.Format() );
839
840 runTool( tool->second->theTool );
841 return true;
842 }
843 }
844
845 return false;
846}
847
849{
850 for( TOOL_ID toolId : m_activeTools )
851 {
852 TOOL_STATE* st = m_toolIdIndex[toolId];
853
854 // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
855 // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
856 if( st->contextMenuTrigger == CMENU_OFF )
857 continue;
858
859 if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
860 break;
861
862 if( st->cofunc )
863 {
864 st->pendingWait = true;
866 }
867
868 // Store the menu pointer in case it is changed by the TOOL when handling menu events
869 ACTION_MENU* m = st->contextMenu;
870
871 if( st->contextMenuTrigger == CMENU_NOW )
873
874 // Store the cursor position, so the tools could execute actions
875 // using the point where the user has invoked a context menu
876 if( m_viewControls )
878
879 // Save all tools cursor settings, as they will be overridden
880 for( const std::pair<const TOOL_ID, TOOL_STATE*>& idState : m_toolIdIndex )
881 {
882 TOOL_STATE* s = idState.second;
883 const auto& vc = s->vcSettings;
884
885 if( vc.m_forceCursorPosition )
886 m_cursorSettings[idState.first] = vc.m_forcedPosition;
887 else
888 m_cursorSettings[idState.first] = std::nullopt;
889 }
890
891 if( m_viewControls )
893
894 // Display a copy of menu
895 std::unique_ptr<ACTION_MENU> menu( m->Clone() );
896
897 m_menuOwner = toolId;
898 m_menuActive = true;
899
900 if( wxWindow* frame = dynamic_cast<wxWindow*>( m_frame ) )
901 frame->PopupMenu( menu.get() );
902
903 // If a menu is canceled then notify tool
904 if( menu->GetSelected() < 0 )
905 {
907 evt.SetHasPosition( false );
908 evt.SetParameter( m );
909 dispatchInternal( evt );
910 }
911
912 // Restore setting in case it was vetoed
914
915 // Notify the tools that menu has been closed
917 evt.SetHasPosition( false );
918 evt.SetParameter( m );
919 dispatchInternal( evt );
920
921 m_menuActive = false;
922 m_menuOwner = -1;
923
924 // Restore cursor settings
925 for( const std::pair<const TOOL_ID, std::optional<VECTOR2D>>& cursorSetting : m_cursorSettings )
926 {
927 auto it = m_toolIdIndex.find( cursorSetting.first );
928 wxASSERT( it != m_toolIdIndex.end() );
929
930 if( it == m_toolIdIndex.end() )
931 continue;
932
933 KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
934 vc.m_forceCursorPosition = (bool) cursorSetting.second;
935 vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
936 }
937
938 m_cursorSettings.clear();
939 break;
940 }
941}
942
943
944TOOL_MANAGER::ID_LIST::iterator TOOL_MANAGER::finishTool( TOOL_STATE* aState )
945{
946 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), aState->theTool->GetId() );
947
948 if( !aState->Pop() )
949 {
950 // Deactivate the tool if there are no other contexts saved on the stack
951 if( it != m_activeTools.end() )
952 it = m_activeTools.erase( it );
953
954 aState->idle = true;
955 }
956
957 if( aState == m_activeState )
958 setActiveState( nullptr );
959
960 // Set transitions to be ready for future TOOL_EVENTs
961 TOOL_BASE* tool = aState->theTool;
962
963 if( tool->GetType() == INTERACTIVE )
964 static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
965
966 return it;
967}
968
969
971{
972 // Once the tool manager is shutting down, don't start
973 // activating more tools
974 if( m_shuttingDown )
975 return true;
976
977 bool handled = processEvent( aEvent );
978
979 TOOL_STATE* activeTool = GetCurrentToolState();
980
981 if( activeTool )
982 setActiveState( activeTool );
983
984 if( m_view && m_view->IsDirty() )
985 {
986 if( GetToolHolder() )
988
989#if defined( __WXMAC__ )
990 wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu
991#endif
992 }
993
994 UpdateUI( aEvent );
995
996 return handled;
997}
998
999
1001 CONTEXT_MENU_TRIGGER aTrigger )
1002{
1003 TOOL_STATE* st = m_toolState[aTool];
1004
1005 st->contextMenu = aMenu;
1006 st->contextMenuTrigger = aTrigger;
1007}
1008
1009
1010bool TOOL_MANAGER::SaveClipboard( const std::string& aTextUTF8 )
1011{
1012 wxLogNull doNotLog; // disable logging of failed clipboard actions
1013
1014 if( wxTheClipboard->Open() )
1015 {
1016 // Store the UTF8 string as Unicode string in clipboard:
1017 wxTheClipboard->SetData( new wxTextDataObject( wxString( aTextUTF8.c_str(),
1018 wxConvUTF8 ) ) );
1019
1020 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
1021 wxTheClipboard->Close();
1022
1023 return true;
1024 }
1025
1026 return false;
1027}
1028
1029
1031{
1032 std::string result;
1033
1034 wxLogNull doNotLog; // disable logging of failed clipboard actions
1035
1036 if( wxTheClipboard->Open() )
1037 {
1038 if( wxTheClipboard->IsSupported( wxDF_TEXT )
1039 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
1040 {
1041 wxTextDataObject data;
1042 wxTheClipboard->GetData( data );
1043
1044 // The clipboard is expected containing a Unicode string, so return it
1045 // as UTF8 string
1046 result = data.GetText().utf8_str();
1047 }
1048
1049 wxTheClipboard->Close();
1050 }
1051
1052 return result;
1053}
1054
1055
1057{
1058 if( TOOL_STATE* active = GetCurrentToolState() )
1059 return active->vcSettings;
1060
1061 return m_viewControls->GetSettings();
1062}
1063
1064
1065TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName )
1066{
1067 static int currentId;
1068
1069 return currentId++;
1070}
1071
1072
1074 KIGFX::VIEW_CONTROLS* aViewControls,
1075 APP_SETTINGS_BASE* aSettings, TOOLS_HOLDER* aFrame )
1076{
1077 m_model = aModel;
1078 m_view = aView;
1079 m_viewControls = aViewControls;
1080 m_frame = aFrame;
1081 m_settings = aSettings;
1082}
1083
1084
1086{
1087 if( !isRegistered( aTool ) )
1088 return false;
1089
1090 // Just check if the tool is on the active tools stack
1091 return alg::contains( m_activeTools, aTool->GetId() );
1092}
1093
1094
1096{
1098
1099 if( m_menuActive )
1100 {
1101 // Context menu is active, so the cursor settings are overridden (see DispatchContextMenu())
1102 auto it = m_cursorSettings.find( aState->theTool->GetId() );
1103
1104 if( it != m_cursorSettings.end() )
1105 {
1107
1108 // Tool has overridden the cursor position, so store the new settings
1110 {
1111 if( !curr.m_forceCursorPosition )
1112 it->second = std::nullopt;
1113 else
1114 it->second = curr.m_forcedPosition;
1115 }
1116 else
1117 {
1118 std::optional<VECTOR2D> cursor = it->second;
1119
1120 if( cursor )
1121 {
1122 aState->vcSettings.m_forceCursorPosition = true;
1124 }
1125 else
1126 {
1127 aState->vcSettings.m_forceCursorPosition = false;
1128 }
1129 }
1130 }
1131 }
1132}
1133
1134
1136{
1138}
1139
1140
1142{
1143 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::processEvent - %s", aEvent.Format() );
1144
1145 // First try to dispatch the action associated with the event if it is a key press event
1146 bool handled = DispatchHotKey( aEvent );
1147
1148 if( !handled )
1149 {
1150 TOOL_EVENT mod_event( aEvent );
1151
1152 // Only immediate actions get the position. Otherwise clear for tool activation
1153 if( GetToolHolder() && !GetToolHolder()->GetDoImmediateActions() )
1154 {
1155 // An tool-selection-event has no position
1156 if( !mod_event.getCommandStr().empty()
1157 && mod_event.getCommandStr() != GetToolHolder()->CurrentToolName()
1158 && !mod_event.ForceImmediate() )
1159 {
1160 mod_event.SetHasPosition( false );
1161 }
1162 }
1163
1164 // If the event is not handled through a hotkey activation, pass it to the currently
1165 // running tool loops
1166 handled |= dispatchInternal( mod_event );
1167 handled |= dispatchActivation( mod_event );
1168
1169 // Open the context menu if requested by a tool
1170 DispatchContextMenu( mod_event );
1171
1172 // Dispatch any remaining events in the event queue
1173 while( !m_eventQueue.empty() )
1174 {
1175 TOOL_EVENT event = m_eventQueue.front();
1176 m_eventQueue.pop_front();
1177 processEvent( event );
1178 }
1179 }
1180
1181 wxLogTrace( kicadTraceToolStack, "TOOL_MANAGER::processEvent - %s handle event: %s",
1182 ( handled ? "Did" : "Did not" ), aEvent.Format() );
1183
1184 return handled;
1185}
1186
1187
1189{
1192
1193 m_activeState = aState;
1194
1196 applyViewControls( aState );
1197}
1198
1199
1201{
1202 auto it = m_toolIdIndex.find( aId );
1203 return !it->second->idle;
1204}
1205
1206
1208{
1209 EDA_BASE_FRAME* frame = dynamic_cast<EDA_BASE_FRAME*>( GetToolHolder() );
1210
1211 if( frame )
1212 frame->UpdateStatusBar();
1213}
Manage TOOL_ACTION objects.
bool RunHotKey(int aHotKey) const
Run an action associated with a hotkey (if there is one available).
TOOL_ACTION * FindAction(const std::string &aActionName) const
Find an action with a given name (if there is one available).
int GetHotKey(const TOOL_ACTION &aAction) const
Return the hot key associated with a given action or 0 if there is none.
void UpdateHotKeys(bool aFullUpdate)
Optionally read the hotkey config files and then rebuilds the internal hotkey maps.
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
ACTION_MENU * Clone() const
Create a deep, recursive copy of this ACTION_MENU.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:110
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:264
void KiYield()
Stop execution of the coroutine and returns control to the caller.
Definition: coroutine.h:227
bool Resume()
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:310
bool Running() const
Definition: coroutine.h:359
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
Definition: coroutine.h:250
The base frame for deriving all KiCad main window classes.
virtual void UpdateStatusBar()
Update the status bar information.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
void ApplySettings(const VC_SETTINGS &aSettings)
Load new settings from program common settings.
const VC_SETTINGS & GetSettings() const
Apply VIEW_CONTROLS settings from an object.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:69
bool IsDirty() const
Return true if any of the VIEW layers needs to be refreshened.
Definition: view.h:589
virtual void RefreshCanvas()
Definition: tools_holder.h:165
Represent a single user action.
Definition: tool_action.h:68
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Definition: tool_action.cpp:72
Base abstract interface for all kinds of tools.
Definition: tool_base.h:66
virtual void Reset(RESET_REASON aReason)=0
Bring the tool to a known, initial state.
virtual bool Init()
Init() is called once upon a registration of the tool.
Definition: tool_base.h:89
const std::string & GetName() const
Return the name of the tool.
Definition: tool_base.h:133
TOOL_TYPE GetType() const
Return the type of the tool.
Definition: tool_base.h:108
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ RUN
Tool is invoked after being inactive.
Definition: tool_base.h:79
TOOL_ID GetId() const
Return the unique identifier of the tool.
Definition: tool_base.h:120
void attachManager(TOOL_MANAGER *aManager)
Set the TOOL_MANAGER the tool will belong to.
Definition: tool_base.cpp:60
A list of TOOL_EVENTs, with overloaded || operators allowing for concatenating TOOL_EVENTs with littl...
Definition: tool_event.h:564
OPT_TOOL_EVENT Matches(const TOOL_EVENT &aEvent) const
Definition: tool_event.h:597
Generic, UI-independent tool event.
Definition: tool_event.h:156
bool PassEvent() const
Definition: tool_event.h:238
TOOL_ACTIONS Action() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition: tool_event.h:233
void SetMousePosition(const VECTOR2D &aP)
Definition: tool_event.h:470
int KeyCode() const
Definition: tool_event.h:349
TOOL_BASE * FirstResponder() const
Definition: tool_event.h:251
bool IsActivate() const
Definition: tool_event.h:318
void SetFirstResponder(TOOL_BASE *aTool)
Controls whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition: tool_event.h:252
bool IsSimulator() const
Indicate if the event is from the simulator.
Definition: tool_event.cpp:248
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition: tool_event.h:460
bool ForceImmediate() const
Definition: tool_event.h:248
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:200
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:230
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:339
void SetHasPosition(bool aHasPosition)
Returns if the action associated with this event should be treated as immediate regardless of the cur...
Definition: tool_event.h:244
const std::string & getCommandStr() const
Definition: tool_event.h:482
bool IsChoiceMenu() const
Definition: tool_event.h:328
const std::string Format() const
Return information about event in form of a human-readable string.
Definition: tool_event.cpp:94
void applyViewControls(const TOOL_STATE *aState)
Apply #VIEW_CONTROLS settings stored in a #TOOL_STATE object.
int GetPriority(int aToolId) const
Return priority of a given tool.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
TOOL_STATE * m_activeState
True if the tool manager is shutting down (don't process additional events)
Definition: tool_manager.h:582
void UpdateUI(const TOOL_EVENT &aEvent)
Update the status bar and synchronizes toolbars.
std::map< TOOL_ID, std::optional< VECTOR2D > > m_cursorSettings
Definition: tool_manager.h:559
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
void ScheduleNextState(TOOL_BASE *aTool, TOOL_STATE_FUNC &aHandler, const TOOL_EVENT_LIST &aConditions)
Define a state transition.
std::map< const char *, TOOL_BASE * > m_toolTypes
Stack of the active tools.
Definition: tool_manager.h:550
bool isRegistered(TOOL_BASE *aTool) const
Return information about a tool registration status.
Definition: tool_manager.h:497
APP_SETTINGS_BASE * m_settings
Queue that stores events to be processed at the end of the event processing cycle.
Definition: tool_manager.h:565
TOOL_STATE * GetCurrentToolState() const
Return the #TOOL_STATE object representing the state of the active tool.
Definition: tool_manager.h:324
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
VECTOR2D GetCursorPosition() const
std::list< TOOL_EVENT > m_eventQueue
Right click context menu position.
Definition: tool_manager.h:568
std::pair< TOOL_EVENT_LIST, TOOL_STATE_FUNC > TRANSITION
Definition: tool_manager.h:448
void setActiveState(TOOL_STATE *aState)
Save the previous active state and sets a new one.
void DeactivateTool()
Deactivate the currently active tool.
bool dispatchInternal(TOOL_EVENT &aEvent)
Pass an event at first to the active tools, then to all others.
void PrimeTool(const VECTOR2D &aPosition)
"Prime" a tool by sending a cursor left-click event with the mouse position set to the passed in posi...
bool runTool(TOOL_BASE *aTool)
Make a tool active, so it can receive events and react to them.
bool InvokeTool(TOOL_ID aToolId)
Call a tool by sending a tool activation event to tool of given ID.
NAME_STATE_MAP m_toolNameIndex
Index of the registered tools current states, associated by tools' ID numbers.
Definition: tool_manager.h:544
TOOLS_HOLDER * m_frame
Definition: tool_manager.h:564
std::string GetClipboardUTF8() const
Return the information currently stored in the system clipboard.
void CancelTool()
Send a cancel event to the tool currently at the top of the tool stack.
bool m_warpMouseAfterContextMenu
Flag indicating whether a context menu is currently displayed.
Definition: tool_manager.h:573
KIGFX::VIEW_CONTROLS * m_viewControls
Definition: tool_manager.h:563
ACTION_MANAGER * m_actionMgr
Original cursor position, if overridden by the context menu handler.
Definition: tool_manager.h:556
const KIGFX::VC_SETTINGS & GetCurrentToolVC() const
Return the view controls settings for the current tool or the general settings if there is no active ...
void RunMainStack(TOOL_BASE *aTool, std::function< void()> aFunc)
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:296
VECTOR2D GetMousePosition() const
bool processEvent(const TOOL_EVENT &aEvent)
Main function for event processing.
std::list< TOOL_ID > ID_LIST
Definition: tool_manager.h:68
ID_LIST::iterator finishTool(TOOL_STATE *aState)
Deactivate a tool and does the necessary clean up.
bool dispatchActivation(const TOOL_EVENT &aEvent)
Check if it is a valid activation event and invokes a proper tool.
ID_STATE_MAP m_toolIdIndex
Index of the registered tools to easily lookup by their type.
Definition: tool_manager.h:547
TOOL_ID m_menuOwner
Pointer to the state object corresponding to the currently executed tool.
Definition: tool_manager.h:579
KIGFX::VIEW * m_view
Definition: tool_manager.h:562
void ClearTransitions(TOOL_BASE *aTool)
Clear the state transition map for a tool.
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
bool m_shuttingDown
Definition: tool_manager.h:585
void saveViewControls(TOOL_STATE *aState)
Save the #VIEW_CONTROLS settings to the tool state object.
int GetHotKey(const TOOL_ACTION &aAction) const
bool m_menuActive
Tool currently displaying a popup menu. It is negative when there is no menu displayed.
Definition: tool_manager.h:576
TOOL_STATE_MAP m_toolState
Index of the registered tools current states, associated by tools' names.
Definition: tool_manager.h:541
void ShutdownTool(TOOL_BASE *aTool)
Shutdown the specified tool by waking it up with a null event to terminate the processing loop.
ID_LIST m_activeTools
Instance of ACTION_MANAGER that handles TOOL_ACTIONs.
Definition: tool_manager.h:553
void ResetTools(TOOL_BASE::RESET_REASON aReason)
Reset all tools (i.e.
EDA_ITEM * m_model
Definition: tool_manager.h:561
bool DispatchHotKey(const TOOL_EVENT &aEvent)
Handle specific events, that are intended for TOOL_MANAGER rather than tools.
static TOOL_ID MakeToolId(const std::string &aToolName)
Generates a unique ID from for a tool with given name.
bool invokeTool(TOOL_BASE *aTool)
Invoke a tool by sending a proper event (in contrary to runTool, which makes the tool run for real).
void DispatchContextMenu(const TOOL_EVENT &aEvent)
Handle context menu related events.
std::vector< TOOL_BASE * > m_toolOrder
Index of registered tools current states, associated by tools' objects.
Definition: tool_manager.h:538
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
void ScheduleContextMenu(TOOL_BASE *aTool, ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger)
Set behavior of the tool's context popup menu.
TOOL_EVENT * ScheduleWait(TOOL_BASE *aTool, const TOOL_EVENT_LIST &aConditions)
Pause execution of a given tool until one or more events matching aConditions arrives.
bool IsToolActive(TOOL_ID aId) const
Return true if a tool with given id is active (executing)
bool isActive(TOOL_BASE *aTool) const
Return information about a tool activation status.
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
void InitTools()
Initializes all registered tools.
VECTOR2D m_menuCursor
Definition: tool_manager.h:571
void ShutdownAllTools()
Shutdown all tools with a currently registered event loop in this tool manager by waking them up with...
Base window classes and related definitions.
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Structure to keep VIEW_CONTROLS settings for easy store/restore operations.
Definition: view_controls.h:42
VECTOR2D m_forcedPosition
Is the forced cursor position enabled.
Definition: view_controls.h:55
void Reset()
Flag determining the cursor visibility.
bool m_forceCursorPosition
Should the cursor be locked within the parent window area.
Definition: view_controls.h:58
Struct describing the current execution state of a TOOL.
bool operator!=(const TOOL_MANAGER::TOOL_STATE &aRhs) const
bool Pop()
Restore state of the tool from stack.
bool pendingWait
Flag defining if the tool is waiting for any event (i.e.
bool pendingContextMenu
Is there a context menu being displayed.
void Push()
Store the current state of the tool on stack.
CONTEXT_MENU_TRIGGER contextMenuTrigger
Defines when the context menu is opened.
TOOL_EVENT initialEvent
The first event that triggered activation of the tool.
KIGFX::VC_SETTINGS vcSettings
VIEW_CONTROLS settings to preserve settings when the tools are switched.
TOOL_STATE(const TOOL_STATE &aState)
TOOL_STATE(TOOL_BASE *aTool)
ACTION_MENU * contextMenu
Context menu currently used by the tool.
bool idle
Is the tool active (pending execution) or disabled at the moment.
std::vector< TRANSITION > transitions
List of possible transitions (ie.
TOOL_EVENT_LIST waitEvents
List of events the tool is currently waiting for.
COROUTINE< int, const TOOL_EVENT & > * cofunc
Tool execution context.
bool shutdown
Should the tool shutdown during next execution.
std::stack< std::unique_ptr< TOOL_STATE > > stateStack
< Stack preserving previous states of a TOOL.
TOOL_BASE * theTool
The tool itself.
TOOL_EVENT wakeupEvent
The event that triggered the execution/wakeup of the tool after Wait() call.
TOOL_STATE & operator=(const TOOL_STATE &aState)
bool operator==(const TOOL_MANAGER::TOOL_STATE &aRhs) const
std::function< int(const TOOL_EVENT &)> TOOL_STATE_FUNC
Definition: tool_base.h:58
int TOOL_ID
Unique identifier for tools.
Definition: tool_base.h:56
@ INTERACTIVE
Tool that interacts with the user.
Definition: tool_base.h:49
CONTEXT_MENU_TRIGGER
Defines when a context menu is opened.
Definition: tool_event.h:146
@ CMENU_NOW
Definition: tool_event.h:148
@ CMENU_OFF
Definition: tool_event.h:149
@ CMENU_BUTTON
Definition: tool_event.h:147
@ TA_ANY
Definition: tool_event.h:121
@ TA_CHOICE_MENU_CHOICE
Definition: tool_event.h:93
@ TA_ACTIVATE
Definition: tool_event.h:110
@ TA_CHOICE_MENU_CLOSED
Definition: tool_event.h:96
@ TA_PRIME
Definition: tool_event.h:119
@ TA_KEY_PRESSED
Definition: tool_event.h:71
@ TA_CANCEL_TOOL
Definition: tool_event.h:85
@ TC_ANY
Definition: tool_event.h:55
@ TC_COMMAND
Definition: tool_event.h:52
@ TC_MOUSE
Definition: tool_event.h:50
@ TC_MESSAGE
Definition: tool_event.h:53
@ BUT_LEFT
Definition: tool_event.h:127
@ BUT_RIGHT
Definition: tool_event.h:128
wxLogTrace helper definitions.