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-2023 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 <math/vector2wx.h>
38
39#include <view/view.h>
40#include <eda_base_frame.h>
41#include <tool/tool_base.h>
43#include <tool/tool_manager.h>
44#include <tool/action_menu.h>
45#include <tool/coroutine.h>
46#include <tool/action_manager.h>
47
49
52{
54 theTool( aTool )
55 {
56 clear();
57 }
58
59 TOOL_STATE( const TOOL_STATE& aState )
60 {
61 theTool = aState.theTool;
62 idle = aState.idle;
63 shutdown = aState.shutdown;
64 pendingWait = aState.pendingWait;
66 contextMenu = aState.contextMenu;
68 cofunc = aState.cofunc;
70 wakeupEvent = aState.wakeupEvent;
71 waitEvents = aState.waitEvents;
72 transitions = aState.transitions;
73 vcSettings = aState.vcSettings;
74 // do not copy stateStack
75 }
76
78 {
79 if( !stateStack.empty() )
80 wxFAIL;
81 }
82
85
87 bool idle;
88
91
95
98
101
104
107
110
113
116
119 std::vector<TRANSITION> transitions;
120
123
125 {
126 theTool = aState.theTool;
127 idle = aState.idle;
128 shutdown = aState.shutdown;
129 pendingWait = aState.pendingWait;
131 contextMenu = aState.contextMenu;
133 cofunc = aState.cofunc;
134 initialEvent = aState.initialEvent;
135 wakeupEvent = aState.wakeupEvent;
136 waitEvents = aState.waitEvents;
137 transitions = aState.transitions;
138 vcSettings = aState.vcSettings;
139 // do not copy stateStack
140 return *this;
141 }
142
143 bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
144 {
145 return aRhs.theTool == theTool;
146 }
147
148 bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
149 {
150 return aRhs.theTool != theTool;
151 }
152
157 void Push()
158 {
159 auto state = std::make_unique<TOOL_STATE>( *this );
160 stateStack.push( std::move( state ) );
161 clear();
162 }
163
170 bool Pop()
171 {
172 delete cofunc;
173
174 if( !stateStack.empty() )
175 {
176 *this = *stateStack.top().get();
177 stateStack.pop();
178 return true;
179 }
180 else
181 {
182 cofunc = nullptr;
183 return false;
184 }
185 }
186
187private:
189 std::stack<std::unique_ptr<TOOL_STATE>> stateStack;
190
192 void clear()
193 {
194 idle = true;
195 shutdown = false;
196 pendingWait = false;
197 pendingContextMenu = false;
198 cofunc = nullptr;
199 contextMenu = nullptr;
202 transitions.clear();
203 }
204};
205
206
208 m_model( nullptr ),
209 m_view( nullptr ),
210 m_viewControls( nullptr ),
211 m_frame( nullptr ),
212 m_settings( nullptr ),
213 m_warpMouseAfterContextMenu( true ),
214 m_menuActive( false ),
215 m_menuOwner( -1 ),
216 m_activeState( nullptr ),
217 m_shuttingDown( false )
218{
219 m_actionMgr = new ACTION_MANAGER( this );
220}
221
222
224{
225 std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end;
226
227 for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it )
228 {
229 delete it->second->cofunc; // delete cofunction
230 delete it->second; // delete TOOL_STATE
231 delete it->first; // delete the tool itself
232 }
233
234 delete m_actionMgr;
235}
236
237
239{
240 wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(),
241 wxT( "Adding two tools with the same name may result in unexpected behavior.") );
242 wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(),
243 wxT( "Adding two tools with the same ID may result in unexpected behavior.") );
244 wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(),
245 wxT( "Adding two tools of the same type may result in unexpected behavior.") );
246
247 m_toolOrder.push_back( aTool );
248
249 TOOL_STATE* st = new TOOL_STATE( aTool );
250
251 m_toolState[aTool] = st;
252 m_toolNameIndex[aTool->GetName()] = st;
253 m_toolIdIndex[aTool->GetId()] = st;
254 m_toolTypes[typeid( *aTool ).name()] = st->theTool;
255
256 aTool->attachManager( this );
257}
258
259
261{
262 TOOL_BASE* tool = FindTool( aToolId );
263
264 if( tool && tool->GetType() == INTERACTIVE )
265 return invokeTool( tool );
266
267 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::InvokeTool - no tool with ID %d" ),
268 aToolId );
269
270 return false; // there is no tool with the given id
271}
272
273
274bool TOOL_MANAGER::InvokeTool( const std::string& aToolName )
275{
276 TOOL_BASE* tool = FindTool( aToolName );
277
278 if( tool && tool->GetType() == INTERACTIVE )
279 return invokeTool( tool );
280
281 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::InvokeTool - no tool with name %s" ),
282 aToolName );
283
284 return false; // there is no tool with the given name
285}
286
287
288bool TOOL_MANAGER::RunAction( const std::string& aActionName, bool aNow, void* aParam )
289{
290 TOOL_ACTION* action = m_actionMgr->FindAction( aActionName );
291
292 if( !action )
293 {
294 wxASSERT_MSG( false, wxString::Format( "Could not find action %s.", aActionName ) );
295 return false;
296 }
297
298 RunAction( *action, aNow, aParam );
299
300 return false;
301}
302
303
305{
306 if( m_viewControls )
308 else
309 return ToVECTOR2D( wxGetMousePosition() );
310}
311
312
314{
315 if( m_viewControls )
317 else
318 return ToVECTOR2D( wxGetMousePosition() );
319}
320
321
322bool TOOL_MANAGER::RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam )
323{
324 if( m_shuttingDown )
325 return true;
326
327 bool handled = false;
328 TOOL_EVENT event = aAction.MakeEvent();
329
330 if( event.Category() == TC_COMMAND )
332
333 // Allow to override the action parameter
334 if( aParam )
335 event.SetParameter( aParam );
336
337 if( aNow )
338 {
339 TOOL_STATE* current = m_activeState;
340 handled = processEvent( event );
341 setActiveState( current );
342 UpdateUI( event );
343 }
344 else
345 {
346 PostEvent( event );
347 }
348
349 return handled;
350}
351
352
354{
356
357 processEvent( evt );
358}
359
360
361void TOOL_MANAGER::PrimeTool( const VECTOR2D& aPosition )
362{
363 int modifiers = 0;
364
365 /*
366 * Don't include any modifiers. They're part of the hotkey, not part of the resulting
367 * click.
368 *
369 * modifiers |= wxGetKeyState( WXK_SHIFT ) ? MD_SHIFT : 0;
370 * modifiers |= wxGetKeyState( WXK_CONTROL ) ? MD_CTRL : 0;
371 * modifiers |= wxGetKeyState( WXK_ALT ) ? MD_ALT : 0;
372 */
373
374 TOOL_EVENT evt( TC_MOUSE, TA_PRIME, BUT_LEFT | modifiers );
375 evt.SetMousePosition( aPosition );
376
377 PostEvent( evt );
378}
379
380
382{
383 // Horrific hack, but it's a crash bug. Don't let inter-frame commands stack up
384 // waiting to be processed.
385 if( aEvent.IsSimulator() && m_eventQueue.size() > 0 && m_eventQueue.back().IsSimulator() )
386 m_eventQueue.pop_back();
387
388 m_eventQueue.push_back( aEvent );
389}
390
391
392int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
393{
394 return m_actionMgr->GetHotKey( aAction );
395}
396
397
399{
400 wxASSERT( aTool != nullptr );
401
402 TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() );
404 processEvent( evt );
405
406 if( TOOL_STATE* active = GetCurrentToolState() )
407 setActiveState( active );
408
409 return true;
410}
411
412
414{
415 wxASSERT( aTool != nullptr );
416
417 if( !isRegistered( aTool ) )
418 {
419 wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) );
420 return false;
421 }
422
423 TOOL_ID id = aTool->GetId();
424
425 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::runTool - running tool %s" ),
426 aTool->GetName() );
427
428 if( aTool->GetType() == INTERACTIVE )
429 static_cast<TOOL_INTERACTIVE*>( aTool )->resetTransitions();
430
431 // If the tool is already active, bring it to the top of the active tools stack
432 if( isActive( aTool ) && m_activeTools.size() > 1 )
433 {
434 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), id );
435
436 if( it != m_activeTools.end() )
437 {
438 if( it != m_activeTools.begin() )
439 {
440 m_activeTools.erase( it );
441 m_activeTools.push_front( id );
442 }
443
444 return false;
445 }
446 }
447
450
451 // Add the tool on the front of the processing queue (it gets events first)
452 m_activeTools.push_front( id );
453
454 return true;
455}
456
457
459{
460 m_shuttingDown = true;
461
462 // Create a temporary list of tools to iterate over since when the tools shutdown
463 // they remove themselves from the list automatically (invalidating the iterator)
464 ID_LIST tmpList = m_activeTools;
465
466 // Make sure each tool knows that it is shutting down, so that loops get shut down
467 // at the dispatcher
468 for( auto id : tmpList )
469 {
470 if( m_toolIdIndex.count( id ) == 0 )
471 continue;
472
473 m_toolIdIndex[id]->shutdown = true;
474 }
475
476 for( auto id : tmpList )
477 {
478 ShutdownTool( id );
479 }
480}
481
482
484{
485 TOOL_BASE* tool = FindTool( aToolId );
486
487 if( tool && tool->GetType() == INTERACTIVE )
488 ShutdownTool( tool );
489
490 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::ShutdownTool - no tool with ID %d" ),
491 aToolId );
492}
493
494
495void TOOL_MANAGER::ShutdownTool( const std::string& aToolName )
496{
497 TOOL_BASE* tool = FindTool( aToolName );
498
499 if( tool && tool->GetType() == INTERACTIVE )
500 ShutdownTool( tool );
501
502 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::ShutdownTool - no tool with name %s" ),
503 aToolName );
504}
505
506
508{
509 wxASSERT( aTool != nullptr );
510
511 TOOL_ID id = aTool->GetId();
512
513 if( isActive( aTool ) )
514 {
515 TOOL_MANAGER::ID_LIST::iterator it = std::find( m_activeTools.begin(),
516 m_activeTools.end(), id );
517
518 TOOL_STATE* st = m_toolIdIndex[*it];
519
520 // the tool state handler is waiting for events (i.e. called Wait() method)
521 if( st && st->pendingWait )
522 {
523 // Wake up the tool and tell it to shutdown
524 st->shutdown = true;
525 st->pendingWait = false;
526 st->waitEvents.clear();
527
528 if( st->cofunc )
529 {
530 wxLogTrace( kicadTraceToolStack,
531 wxS( "TOOL_MANAGER::ShutdownTool - Shutting down tool %s" ),
532 st->theTool->GetName() );
533
534 setActiveState( st );
535 bool end = !st->cofunc->Resume();
536
537 if( end )
538 finishTool( st );
539 }
540 }
541 }
542}
543
544
546{
547 std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
548
549 if( it != m_toolIdIndex.end() )
550 return it->second->theTool;
551
552 return nullptr;
553}
554
555
556TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const
557{
558 std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName );
559
560 if( it != m_toolNameIndex.end() )
561 return it->second->theTool;
562
563 return nullptr;
564}
565
566
568{
569 // Deactivate the active tool, but do not run anything new
571 processEvent( evt );
572}
573
574
576{
577 if( aReason != TOOL_BASE::REDRAW )
579
580 for( auto& state : m_toolState )
581 {
582 TOOL_BASE* tool = state.first;
583 setActiveState( state.second );
584 tool->Reset( aReason );
585
586 if( tool->GetType() == INTERACTIVE )
587 static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
588 }
589}
590
591
593{
594 for( auto it = m_toolOrder.begin(); it != m_toolOrder.end(); /* iter inside */ )
595 {
596 TOOL_BASE* tool = *it;
597 wxASSERT( m_toolState.count( tool ) );
598 TOOL_STATE* state = m_toolState[tool];
599 setActiveState( state );
600 ++it; // keep the iterator valid if the element is going to be erased
601
602 if( !tool->Init() )
603 {
604 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER initialization of tool '%s' failed" ),
605 tool->GetName() );
606
607 // Unregister the tool
608 setActiveState( nullptr );
609 m_toolState.erase( tool );
610 m_toolNameIndex.erase( tool->GetName() );
611 m_toolIdIndex.erase( tool->GetId() );
612 m_toolTypes.erase( typeid( *tool ).name() );
613
614 delete state;
615 delete tool;
616 }
617 }
618
619 m_actionMgr->UpdateHotKeys( true );
620
622}
623
624
625int TOOL_MANAGER::GetPriority( int aToolId ) const
626{
627 int priority = 0;
628
629 for( TOOL_ID tool : m_activeTools )
630 {
631 if( tool == aToolId )
632 return priority;
633
634 ++priority;
635 }
636
637 return -1;
638}
639
640
642 const TOOL_EVENT_LIST& aConditions )
643{
644 TOOL_STATE* st = m_toolState[aTool];
645
646 st->transitions.emplace_back( TRANSITION( aConditions, aHandler ) );
647}
648
649
651{
652 m_toolState[aTool]->transitions.clear();
653}
654
655
656void TOOL_MANAGER::RunMainStack( TOOL_BASE* aTool, std::function<void()> aFunc )
657{
658 TOOL_STATE* st = m_toolState[aTool];
659 setActiveState( st );
660 st->cofunc->RunMainStack( std::move( aFunc ) );
661}
662
663
665{
666 TOOL_STATE* st = m_toolState[aTool];
667
668 wxASSERT( !st->pendingWait ); // everything collapses on two KiYield() in a row
669
670 // indicate to the manager that we are going to sleep and we shall be
671 // woken up when an event matching aConditions arrive
672 st->pendingWait = true;
673 st->waitEvents = aConditions;
674
675 // switch context back to event dispatcher loop
676 st->cofunc->KiYield();
677
678 // If the tool should shutdown, it gets a null event to break the loop
679 if( st->shutdown )
680 return nullptr;
681 else
682 return &st->wakeupEvent;
683}
684
685
687{
688 bool handled = false;
689
690 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchInternal - received event: %s" ),
691 aEvent.Format() );
692
693 auto it = m_activeTools.begin();
694
695 // iterate over active tool stack
696 while( it != m_activeTools.end() )
697 {
698 TOOL_STATE* st = m_toolIdIndex[*it];
699 bool increment = true;
700
701 // forward context menu events to the tool that created the menu
702 if( aEvent.IsChoiceMenu() )
703 {
704 if( *it != m_menuOwner )
705 {
706 ++it;
707 continue;
708 }
709 }
710
711 // If we're pendingWait then we had better have a cofunc to process the wait.
712 wxASSERT( !st || !st->pendingWait || st->cofunc );
713
714 // the tool state handler is waiting for events (i.e. called Wait() method)
715 if( st && st->cofunc && st->pendingWait && st->waitEvents.Matches( aEvent ) )
716 {
717 if( !aEvent.FirstResponder() )
718 aEvent.SetFirstResponder( st->theTool );
719
720 // got matching event? clear wait list and wake up the coroutine
721 st->wakeupEvent = aEvent;
722 st->pendingWait = false;
723 st->waitEvents.clear();
724
725 wxLogTrace( kicadTraceToolStack,
726 wxS( "TOOL_MANAGER::dispatchInternal - Waking tool %s for event: %s" ),
727 st->theTool->GetName(), aEvent.Format() );
728
729 setActiveState( st );
730 bool end = !st->cofunc->Resume();
731
732 if( end )
733 {
734 it = finishTool( st );
735 increment = false;
736 }
737
738 // If the tool did not request the event be passed to other tools, we're done
739 if( !st->wakeupEvent.PassEvent() )
740 {
741 wxLogTrace( kicadTraceToolStack,
742 wxS( "TOOL_MANAGER::dispatchInternal - tool %s stopped passing event: %s" ),
743 st->theTool->GetName(), aEvent.Format() );
744
745 return true;
746 }
747 }
748
749 if( increment )
750 ++it;
751 }
752
753 for( const auto& state : m_toolState )
754 {
755 TOOL_STATE* st = state.second;
756 bool finished = false;
757
758 // no state handler in progress - check if there are any transitions (defined by
759 // Go() method that match the event.
760 if( !st->transitions.empty() )
761 {
762 for( const TRANSITION& tr : st->transitions )
763 {
764 if( tr.first.Matches( aEvent ) )
765 {
766 auto func_copy = tr.second;
767
768 if( !aEvent.FirstResponder() )
769 aEvent.SetFirstResponder( st->theTool );
770
771 // if there is already a context, then push it on the stack
772 // and transfer the previous view control settings to the new context
773 if( st->cofunc )
774 {
775 auto vc = st->vcSettings;
776 st->Push();
777 st->vcSettings = vc;
778 }
779
780 st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( std::move( func_copy ) );
781
782 // as the state changes, the transition table has to be set up again
783 st->transitions.clear();
784
785 wxLogTrace( kicadTraceToolStack,
786 wxS( "TOOL_MANAGER::dispatchInternal - Running tool %s for event: %s" ),
787 st->theTool->GetName(), aEvent.Format() );
788
789 // got match? Run the handler.
790 setActiveState( st );
791 st->idle = false;
792 st->initialEvent = aEvent;
793 st->cofunc->Call( st->initialEvent );
794 handled = true;
795
796 if( !st->cofunc->Running() )
797 finishTool( st ); // The coroutine has finished immediately?
798
799 // if it is a message, continue processing
800 finished = !( aEvent.Category() == TC_MESSAGE );
801
802 // there is no point in further checking, as transitions got cleared
803 break;
804 }
805 }
806 }
807
808 if( finished )
809 break; // only the first tool gets the event
810 }
811
812 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchInternal - %s handle event: %s" ),
813 ( handled ? wxS( "Did" ) : wxS( "Did not" ) ), aEvent.Format() );
814
815 return handled;
816}
817
818
820{
821 if( aEvent.Action() == TA_KEY_PRESSED )
822 return m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() );
823
824 return false;
825}
826
827
829{
830 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchActivation - Received event: %s" ),
831 aEvent.Format() );
832
833 if( aEvent.IsActivate() )
834 {
835 auto tool = m_toolNameIndex.find( aEvent.getCommandStr() );
836
837 if( tool != m_toolNameIndex.end() )
838 {
839 wxLogTrace( kicadTraceToolStack,
840 wxS( "TOOL_MANAGER::dispatchActivation - Running tool %s for event: %s" ),
841 tool->second->theTool->GetName(), aEvent.Format() );
842
843 runTool( tool->second->theTool );
844 return true;
845 }
846 }
847
848 return false;
849}
850
852{
853 for( TOOL_ID toolId : m_activeTools )
854 {
855 TOOL_STATE* st = m_toolIdIndex[toolId];
856
857 // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
858 // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
859 if( st->contextMenuTrigger == CMENU_OFF )
860 continue;
861
862 if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
863 break;
864
865 if( st->cofunc )
866 {
867 st->pendingWait = true;
869 }
870
871 // Store the menu pointer in case it is changed by the TOOL when handling menu events
872 ACTION_MENU* m = st->contextMenu;
873
874 if( st->contextMenuTrigger == CMENU_NOW )
876
877 // Store the cursor position, so the tools could execute actions
878 // using the point where the user has invoked a context menu
879 if( m_viewControls )
881
882 // Save all tools cursor settings, as they will be overridden
883 for( const std::pair<const TOOL_ID, TOOL_STATE*>& idState : m_toolIdIndex )
884 {
885 TOOL_STATE* s = idState.second;
886 const auto& vc = s->vcSettings;
887
888 if( vc.m_forceCursorPosition )
889 m_cursorSettings[idState.first] = vc.m_forcedPosition;
890 else
891 m_cursorSettings[idState.first] = std::nullopt;
892 }
893
894 if( m_viewControls )
896
897 // Display a copy of menu
898 std::unique_ptr<ACTION_MENU> menu( m->Clone() );
899
900 m_menuOwner = toolId;
901 m_menuActive = true;
902
903 if( wxWindow* frame = dynamic_cast<wxWindow*>( m_frame ) )
904 frame->PopupMenu( menu.get() );
905
906 // If a menu is canceled then notify tool
907 if( menu->GetSelected() < 0 )
908 {
910 evt.SetHasPosition( false );
911 evt.SetParameter( m );
912 dispatchInternal( evt );
913 }
914
915 // Restore setting in case it was vetoed
917
918 // Notify the tools that menu has been closed
920 evt.SetHasPosition( false );
921 evt.SetParameter( m );
922 dispatchInternal( evt );
923
924 m_menuActive = false;
925 m_menuOwner = -1;
926
927 // Restore cursor settings
928 for( const std::pair<const TOOL_ID, std::optional<VECTOR2D>>& cursorSetting : m_cursorSettings )
929 {
930 auto it = m_toolIdIndex.find( cursorSetting.first );
931 wxASSERT( it != m_toolIdIndex.end() );
932
933 if( it == m_toolIdIndex.end() )
934 continue;
935
936 KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
937 vc.m_forceCursorPosition = (bool) cursorSetting.second;
938 vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
939 }
940
941 m_cursorSettings.clear();
942 break;
943 }
944}
945
946
947TOOL_MANAGER::ID_LIST::iterator TOOL_MANAGER::finishTool( TOOL_STATE* aState )
948{
949 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), aState->theTool->GetId() );
950
951 if( !aState->Pop() )
952 {
953 // Deactivate the tool if there are no other contexts saved on the stack
954 if( it != m_activeTools.end() )
955 it = m_activeTools.erase( it );
956
957 aState->idle = true;
958 }
959
960 if( aState == m_activeState )
961 setActiveState( nullptr );
962
963 // Set transitions to be ready for future TOOL_EVENTs
964 TOOL_BASE* tool = aState->theTool;
965
966 if( tool->GetType() == INTERACTIVE )
967 static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
968
969 return it;
970}
971
972
974{
975 // Once the tool manager is shutting down, don't start
976 // activating more tools
977 if( m_shuttingDown )
978 return true;
979
980 bool handled = processEvent( aEvent );
981
982 TOOL_STATE* activeTool = GetCurrentToolState();
983
984 if( activeTool )
985 setActiveState( activeTool );
986
987 if( m_view && m_view->IsDirty() )
988 {
989 if( GetToolHolder() )
991
992#if defined( __WXMAC__ )
993 wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu
994#endif
995 }
996
997 UpdateUI( aEvent );
998
999 return handled;
1000}
1001
1002
1004 CONTEXT_MENU_TRIGGER aTrigger )
1005{
1006 TOOL_STATE* st = m_toolState[aTool];
1007
1008 st->contextMenu = aMenu;
1009 st->contextMenuTrigger = aTrigger;
1010}
1011
1012
1013bool TOOL_MANAGER::SaveClipboard( const std::string& aTextUTF8 )
1014{
1015 wxLogNull doNotLog; // disable logging of failed clipboard actions
1016
1017 if( wxTheClipboard->Open() )
1018 {
1019 // Store the UTF8 string as Unicode string in clipboard:
1020 wxTheClipboard->SetData( new wxTextDataObject( wxString( aTextUTF8.c_str(),
1021 wxConvUTF8 ) ) );
1022
1023 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
1024 wxTheClipboard->Close();
1025
1026 return true;
1027 }
1028
1029 return false;
1030}
1031
1032
1034{
1035 std::string result;
1036
1037 wxLogNull doNotLog; // disable logging of failed clipboard actions
1038
1039 if( wxTheClipboard->Open() )
1040 {
1041 if( wxTheClipboard->IsSupported( wxDF_TEXT )
1042 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
1043 {
1044 wxTextDataObject data;
1045 wxTheClipboard->GetData( data );
1046
1047 // The clipboard is expected containing a Unicode string, so return it
1048 // as UTF8 string
1049 result = data.GetText().utf8_str();
1050 }
1051
1052 wxTheClipboard->Close();
1053 }
1054
1055 return result;
1056}
1057
1058
1060{
1061 if( TOOL_STATE* active = GetCurrentToolState() )
1062 return active->vcSettings;
1063
1064 return m_viewControls->GetSettings();
1065}
1066
1067
1068TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName )
1069{
1070 static int currentId;
1071
1072 return currentId++;
1073}
1074
1075
1077 KIGFX::VIEW_CONTROLS* aViewControls,
1078 APP_SETTINGS_BASE* aSettings, TOOLS_HOLDER* aFrame )
1079{
1080 m_model = aModel;
1081 m_view = aView;
1082 m_viewControls = aViewControls;
1083 m_frame = aFrame;
1084 m_settings = aSettings;
1085}
1086
1087
1089{
1090 if( !isRegistered( aTool ) )
1091 return false;
1092
1093 // Just check if the tool is on the active tools stack
1094 return alg::contains( m_activeTools, aTool->GetId() );
1095}
1096
1097
1099{
1101
1102 if( m_menuActive )
1103 {
1104 // Context menu is active, so the cursor settings are overridden (see DispatchContextMenu())
1105 auto it = m_cursorSettings.find( aState->theTool->GetId() );
1106
1107 if( it != m_cursorSettings.end() )
1108 {
1110
1111 // Tool has overridden the cursor position, so store the new settings
1113 {
1114 if( !curr.m_forceCursorPosition )
1115 it->second = std::nullopt;
1116 else
1117 it->second = curr.m_forcedPosition;
1118 }
1119 else
1120 {
1121 std::optional<VECTOR2D> cursor = it->second;
1122
1123 if( cursor )
1124 {
1125 aState->vcSettings.m_forceCursorPosition = true;
1127 }
1128 else
1129 {
1130 aState->vcSettings.m_forceCursorPosition = false;
1131 }
1132 }
1133 }
1134 }
1135}
1136
1137
1139{
1141}
1142
1143
1145{
1146 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::processEvent - %s" ), aEvent.Format() );
1147
1148 // First try to dispatch the action associated with the event if it is a key press event
1149 bool handled = DispatchHotKey( aEvent );
1150
1151 if( !handled )
1152 {
1153 TOOL_EVENT mod_event( aEvent );
1154
1155 // Only immediate actions get the position. Otherwise clear for tool activation
1156 if( GetToolHolder() && !GetToolHolder()->GetDoImmediateActions() )
1157 {
1158 // An tool-selection-event has no position
1159 if( !mod_event.getCommandStr().empty()
1160 && mod_event.getCommandStr() != GetToolHolder()->CurrentToolName()
1161 && !mod_event.ForceImmediate() )
1162 {
1163 mod_event.SetHasPosition( false );
1164 }
1165 }
1166
1167 // If the event is not handled through a hotkey activation, pass it to the currently
1168 // running tool loops
1169 handled |= dispatchInternal( mod_event );
1170 handled |= dispatchActivation( mod_event );
1171
1172 // Open the context menu if requested by a tool
1173 DispatchContextMenu( mod_event );
1174
1175 // Dispatch any remaining events in the event queue
1176 while( !m_eventQueue.empty() )
1177 {
1178 TOOL_EVENT event = m_eventQueue.front();
1179 m_eventQueue.pop_front();
1180 processEvent( event );
1181 }
1182 }
1183
1184 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::processEvent - %s handle event: %s" ),
1185 ( handled ? "Did" : "Did not" ), aEvent.Format() );
1186
1187 return handled;
1188}
1189
1190
1192{
1195
1196 m_activeState = aState;
1197
1199 applyViewControls( aState );
1200}
1201
1202
1204{
1205 auto it = m_toolIdIndex.find( aId );
1206 return !it->second->idle;
1207}
1208
1209
1211{
1212 EDA_BASE_FRAME* frame = dynamic_cast<EDA_BASE_FRAME*>( GetToolHolder() );
1213
1214 if( frame )
1215 frame->UpdateStatusBar();
1216}
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:90
const std::string & GetName() const
Return the name of the tool.
Definition: tool_base.h:134
TOOL_TYPE GetType() const
Return the type of the tool.
Definition: tool_base.h:109
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ REDRAW
Full drawing refresh.
Definition: tool_base.h:82
@ 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:121
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:241
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:193
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:87
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.
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition: vector2wx.h:40