KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 CERN
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <core/kicad_algo.h>
24#include <scoped_set_reset.h>
25#include <optional>
26#include <map>
27#include <stack>
28#include <trace_helpers.h>
29#include <kiplatform/ui.h>
30#include <app_monitor.h>
31
32#include <wx/event.h>
33#include <wx/evtloop.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 <view/view_controls.h>
41#include <eda_base_frame.h>
42#include <tool/tool_base.h>
44#include <tool/tool_manager.h>
45#include <tool/action_menu.h>
46#include <tool/coroutine.h>
47#include <tool/action_manager.h>
48
50
53{
55 theTool( aTool )
56 {
57 clear();
58 }
59
60 TOOL_STATE( const TOOL_STATE& aState )
61 {
62 theTool = aState.theTool;
63 idle = aState.idle;
64 shutdown = aState.shutdown;
65 pendingWait = aState.pendingWait;
67 contextMenu = aState.contextMenu;
69 cofunc = aState.cofunc;
71 wakeupEvent = aState.wakeupEvent;
72 waitEvents = aState.waitEvents;
73 transitions = aState.transitions;
74 vcSettings = aState.vcSettings;
75 // do not copy stateStack
76 }
77
79 {
80 wxASSERT_MSG( stateStack.empty(), wxT( "StateStack not empty!" ) );
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
140 // do not copy stateStack
141 return *this;
142 }
143
144 bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
145 {
146 return aRhs.theTool == theTool;
147 }
148
149 bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
150 {
151 return aRhs.theTool != theTool;
152 }
153
158 void Push()
159 {
160 auto state = std::make_unique<TOOL_STATE>( *this );
161 stateStack.push( std::move( state ) );
162 clear();
163 }
164
171 bool Pop()
172 {
173 delete cofunc;
174
175 if( !stateStack.empty() )
176 {
177 *this = *stateStack.top().get();
178 stateStack.pop();
179 return true;
180 }
181
183 return false;
184 }
185
186private:
188 std::stack<std::unique_ptr<TOOL_STATE>> stateStack;
189
192 {
193 cofunc = nullptr;
194 shutdown = false;
195 pendingWait = false;
196 pendingContextMenu = false;
197 contextMenu = nullptr;
199 }
200
202 void clear()
203 {
204 idle = true;
206 vcSettings.Reset();
207 transitions.clear();
208 }
209};
210
211
213 m_model( nullptr ),
214 m_view( nullptr ),
215 m_viewControls( nullptr ),
216 m_frame( nullptr ),
217 m_settings( nullptr ),
219 m_menuActive( false ),
220 m_menuOwner( -1 ),
221 m_activeState( nullptr ),
222 m_shuttingDown( false )
223{
224 m_actionMgr = new ACTION_MANAGER( this );
225}
226
227
229{
230 std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end;
231
232 for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it )
233 {
234 delete it->second->cofunc; // delete cofunction
235 delete it->second; // delete TOOL_STATE
236 delete it->first; // delete the tool itself
237 }
238
239 delete m_actionMgr;
240}
241
242
244{
245 wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(),
246 wxT( "Adding two tools with the same name may result in unexpected behavior.") );
247 wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(),
248 wxT( "Adding two tools with the same ID may result in unexpected behavior.") );
249 wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(),
250 wxT( "Adding two tools of the same type may result in unexpected behavior.") );
251
252 wxLogTrace( kicadTraceToolStack,
253 wxS( "TOOL_MANAGER::RegisterTool: Registering tool %s with ID %d" ),
254 aTool->GetName(), aTool->GetId() );
255
256 m_toolOrder.push_back( aTool );
257
258 TOOL_STATE* st = new TOOL_STATE( aTool );
259
260 m_toolState[aTool] = st;
261 m_toolNameIndex[aTool->GetName()] = st;
262 m_toolIdIndex[aTool->GetId()] = st;
263 m_toolTypes[typeid( *aTool ).name()] = st->theTool;
264
265 aTool->attachManager( this );
266}
267
268
270{
271 TOOL_BASE* tool = FindTool( aToolId );
272
273 if( tool && tool->GetType() == INTERACTIVE )
274 return invokeTool( tool );
275
276 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::InvokeTool - no tool with ID %d" ),
277 aToolId );
278
279 return false; // there is no tool with the given id
280}
281
282
283bool TOOL_MANAGER::InvokeTool( const std::string& aToolName )
284{
285 TOOL_BASE* tool = FindTool( aToolName );
286
287 if( tool && tool->GetType() == INTERACTIVE )
288 return invokeTool( tool );
289
290 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::InvokeTool - no tool with name %s" ),
291 aToolName );
292
293 return false; // there is no tool with the given name
294}
295
296
297bool TOOL_MANAGER::doRunAction( const std::string& aActionName, bool aNow, const ki::any& aParam,
298 COMMIT* aCommit )
299{
300 TOOL_ACTION* action = m_actionMgr->FindAction( aActionName );
301
302 if( !action )
303 {
304 // Names reaching this overload come from outside KiCad (the IPC API, plugins), so an
305 // unknown one is caller error to report, not an internal fault to assert on
306 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::doRunAction - no action named %s" ),
307 aActionName );
308
309 return false;
310 }
311
312 doRunAction( *action, aNow, aParam, aCommit );
313
314 return true;
315}
316
317
319{
320 if( m_viewControls )
321 return m_viewControls->GetMousePosition();
322 else
324}
325
326
328{
329 if( m_viewControls )
330 return m_viewControls->GetCursorPosition();
331 else
333}
334
335
336bool TOOL_MANAGER::doRunAction( const TOOL_ACTION& aAction, bool aNow, const ki::any& aParam,
337 COMMIT* aCommit, bool aFromAPI )
338{
339 if( m_shuttingDown )
340 return true;
341
342 bool retVal = false;
343 TOOL_EVENT event = aAction.MakeEvent();
344
345 if( event.Category() == TC_COMMAND )
346 event.SetMousePosition( m_hotKeyPos.value_or( GetCursorPosition() ) );
347
348 // Allow to override the action parameter
349 if( aParam.has_value() )
350 event.SetParameter( aParam );
351
352 if( aNow )
353 {
354 TOOL_STATE* current = m_activeState;
355
356 // An event with a commit must be run synchronously
357 if( aCommit )
358 {
359 // We initialize the SYNCHRONOUS state to finished so that tools that don't have an
360 // event loop won't hang if someone forgets to set the state.
361 std::atomic<SYNCRONOUS_TOOL_STATE> synchronousControl = STS_FINISHED;
362
363 event.SetSynchronous( &synchronousControl );
364 event.SetCommit( aCommit );
365
366 processEvent( event );
367
368 while( synchronousControl == STS_RUNNING )
369 {
370 wxYield(); // Needed to honor mouse (and other) events during editing
371 wxMilliSleep( 1 ); // Needed to avoid 100% use of one cpu core.
372 // The sleeping time must be must be small to avoid
373 // noticeable lag in mouse and editing events
374 // (1 to 5 ms is a good value)
375 }
376
377 retVal = synchronousControl != STS_CANCELLED;
378 }
379 else
380 {
381 retVal = processEvent( event );
382 }
383
384 setActiveState( current );
385 UpdateUI( event );
386 }
387 else
388 {
389 // It is really dangerous to pass a commit (whose lifetime we can't guarantee) to
390 // deferred event processing. There is a possibility that user actions will get run
391 // in between, which might either affect the lifetime of the commit or push or pop
392 // other commits. However, we don't currently have a better solution for the API.
393 if( aCommit )
394 {
395 wxASSERT_MSG( aFromAPI, wxT( "Deferred actions have no way of guaranteeing the "
396 "lifetime of the COMMIT object" ) );
397 event.SetCommit( aCommit );
398 }
399
400 PostEvent( event );
401 }
402
403 return retVal;
404}
405
406
408{
410
411 processEvent( evt );
412}
413
414
415void TOOL_MANAGER::PrimeTool( const VECTOR2D& aPosition )
416{
417 int modifiers = 0;
418
419 /*
420 * Don't include any modifiers. They're part of the hotkey, not part of the resulting
421 * click.
422 *
423 * modifiers |= wxGetKeyState( WXK_SHIFT ) ? MD_SHIFT : 0;
424 * modifiers |= wxGetKeyState( WXK_CONTROL ) ? MD_CTRL : 0;
425 * modifiers |= wxGetKeyState( WXK_ALT ) ? MD_ALT : 0;
426 */
427
428 TOOL_EVENT evt( TC_MOUSE, TA_PRIME, BUT_LEFT | modifiers );
429 evt.SetMousePosition( aPosition );
430
431 PostEvent( evt );
432}
433
434
436{
437 // Horrific hack, but it's a crash bug. Don't let inter-frame commands stack up
438 // waiting to be processed.
439 if( aEvent.IsSimulator() && m_eventQueue.size() > 0 && m_eventQueue.back().IsSimulator() )
440 m_eventQueue.pop_back();
441
442 m_eventQueue.push_back( aEvent );
443}
444
445
446int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
447{
448 return m_actionMgr->GetHotKey( aAction );
449}
450
451
453{
454 wxASSERT( aTool != nullptr );
455
456 TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() );
457
458 // Attach the cursor position if there is a canvas to query
459 if( m_viewControls )
461
462 processEvent( evt );
463
464 if( TOOL_STATE* active = GetCurrentToolState() )
465 setActiveState( active );
466
467 return true;
468}
469
470
472{
473 wxASSERT( aTool != nullptr );
474
475 wxString msg = wxString::Format( wxS( "TOOL_MANAGER::runTool - running tool %s" ), aTool->GetName() );
476 APP_MONITOR::AddTransactionBreadcrumb( msg, "tool.run" );
477
478 if( !isRegistered( aTool ) )
479 {
480 wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) );
481 return false;
482 }
483
484 TOOL_ID id = aTool->GetId();
485
486 wxLogTrace( kicadTraceToolStack, msg,
487 aTool->GetName() );
488
489 if( aTool->GetType() == INTERACTIVE )
490 static_cast<TOOL_INTERACTIVE*>( aTool )->resetTransitions();
491
492 // If the tool is already active, bring it to the top of the active tools stack
493 if( isActive( aTool ) && m_activeTools.size() > 1 )
494 {
495 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), id );
496
497 if( it != m_activeTools.end() )
498 {
499 if( it != m_activeTools.begin() )
500 {
501 m_activeTools.erase( it );
502 m_activeTools.push_front( id );
503 }
504
505 return false;
506 }
507 }
508
511
512 // Add the tool on the front of the processing queue (it gets events first)
513 m_activeTools.push_front( id );
514
515 return true;
516}
517
518
520{
521 m_shuttingDown = true;
522
523 // Create a temporary list of tools to iterate over since when the tools shutdown
524 // they remove themselves from the list automatically (invalidating the iterator)
525 ID_LIST tmpList = m_activeTools;
526
527 // Make sure each tool knows that it is shutting down, so that loops get shut down
528 // at the dispatcher
529 for( auto id : tmpList )
530 {
531 if( m_toolIdIndex.count( id ) == 0 )
532 continue;
533
534 m_toolIdIndex[id]->shutdown = true;
535 }
536
537 for( auto id : tmpList )
538 {
539 ShutdownTool( id );
540 }
541}
542
543
545{
546 TOOL_BASE* tool = FindTool( aToolId );
547
548 if( tool && tool->GetType() == INTERACTIVE )
549 ShutdownTool( tool );
550
551 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::ShutdownTool - no tool with ID %d" ),
552 aToolId );
553}
554
555
556void TOOL_MANAGER::ShutdownTool( const std::string& aToolName )
557{
558 TOOL_BASE* tool = FindTool( aToolName );
559
560 if( tool && tool->GetType() == INTERACTIVE )
561 ShutdownTool( tool );
562
563 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::ShutdownTool - no tool with name %s" ),
564 aToolName );
565}
566
567
569{
570 wxASSERT( aTool != nullptr );
571
572 TOOL_ID id = aTool->GetId();
573
574 if( isActive( aTool ) )
575 {
576 TOOL_MANAGER::ID_LIST::iterator it = std::find( m_activeTools.begin(),
577 m_activeTools.end(), id );
578
579 TOOL_STATE* st = m_toolIdIndex[*it];
580
581 // the tool state handler is waiting for events (i.e. called Wait() method)
582 if( st && st->pendingWait )
583 {
584 // Wake up the tool and tell it to shutdown
585 st->shutdown = true;
586 st->pendingWait = false;
587 st->waitEvents.clear();
588
589 if( st->cofunc )
590 {
591 wxLogTrace( kicadTraceToolStack,
592 wxS( "TOOL_MANAGER::ShutdownTool - Shutting down tool %s" ),
593 st->theTool->GetName() );
594
595 setActiveState( st );
596 bool end = !st->cofunc->Resume();
597
598 if( end )
599 finishTool( st );
600 }
601 }
602 }
603}
604
605
607{
608 std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
609
610 if( it != m_toolIdIndex.end() )
611 return it->second->theTool;
612
613 return nullptr;
614}
615
616
617TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const
618{
619 std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName );
620
621 if( it != m_toolNameIndex.end() )
622 return it->second->theTool;
623
624 return nullptr;
625}
626
627
629{
630 // Deactivate the active tool, but do not run anything new
632 processEvent( evt );
633}
634
635
637{
638 if( aReason != TOOL_BASE::REDRAW )
640
641 for( auto& state : m_toolState )
642 {
643 TOOL_BASE* tool = state.first;
644
645 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::ResetTools: Resetting tool '%s'" ),
646 tool->GetName() );
647
648 setActiveState( state.second );
649 tool->Reset( aReason );
650
651 if( tool->GetType() == INTERACTIVE )
652 static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
653 }
654}
655
656
658{
659 for( auto it = m_toolOrder.begin(); it != m_toolOrder.end(); ++it )
660 {
661 TOOL_BASE* tool = *it;
662
663 wxASSERT( m_toolState.count( tool ) );
664 TOOL_STATE* state = m_toolState[tool];
665 setActiveState( state );
666
667 if( !tool->Init() )
668 {
669 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER initialization of tool '%s' failed" ),
670 tool->GetName() );
671
672 // Unregister the tool
673 setActiveState( nullptr );
674
675 m_toolOrder.erase( it );
676
677 m_toolState.erase( tool );
678 m_toolNameIndex.erase( tool->GetName() );
679 m_toolIdIndex.erase( tool->GetId() );
680 m_toolTypes.erase( typeid( *tool ).name() );
681
682 delete state;
683 delete tool;
684 *it = nullptr;
685 }
686 }
687
688 // Clear out any tools that failed to initialize
689 std::erase_if( m_toolOrder,
690 []( const TOOL_BASE* aTool )
691 {
692 return aTool == nullptr;
693 } );
694
695 m_actionMgr->UpdateHotKeys( true );
696
698}
699
700
701int TOOL_MANAGER::GetPriority( int aToolId ) const
702{
703 int priority = 0;
704
705 for( TOOL_ID tool : m_activeTools )
706 {
707 if( tool == aToolId )
708 return priority;
709
710 ++priority;
711 }
712
713 return -1;
714}
715
716
718 const TOOL_EVENT_LIST& aConditions )
719{
720 TOOL_STATE* st = m_toolState[aTool];
721
722 st->transitions.emplace_back( TRANSITION( aConditions, aHandler ) );
723}
724
725
727{
728 m_toolState[aTool]->transitions.clear();
729}
730
731
732void TOOL_MANAGER::RunMainStack( TOOL_BASE* aTool, std::function<void()> aFunc )
733{
734 TOOL_STATE* st = m_toolState[aTool];
735 setActiveState( st );
736 wxCHECK( st->cofunc, /* void */ );
737 st->cofunc->RunMainStack( std::move( aFunc ) );
738}
739
740
742{
743 TOOL_STATE* st = m_toolState[aTool];
744
745 wxCHECK( !st->pendingWait, nullptr ); // everything collapses on two KiYield() in a row
746
747 // indicate to the manager that we are going to sleep and we shall be
748 // woken up when an event matching aConditions arrive
749 st->pendingWait = true;
750 st->waitEvents = aConditions;
751
752 wxCHECK( st->cofunc, nullptr );
753
754 // switch context back to event dispatcher loop
755 st->cofunc->KiYield();
756
757 // If the tool should shutdown, it gets a null event to break the loop
758 if( st->shutdown )
759 return nullptr;
760 else
761 return &st->wakeupEvent;
762}
763
764
766{
767 bool handled = false;
768
769 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchInternal - received event: %s" ),
770 aEvent.Format() );
771
772 auto it = m_activeTools.begin();
773
774 // iterate over active tool stack
775 while( it != m_activeTools.end() )
776 {
777 TOOL_STATE* st = m_toolIdIndex[*it];
778 bool increment = true;
779
780 // forward context menu events to the tool that created the menu
781 if( aEvent.IsChoiceMenu() )
782 {
783 if( *it != m_menuOwner )
784 {
785 ++it;
786 continue;
787 }
788 }
789
790 // If we're pendingWait then we had better have a cofunc to process the wait.
791 wxASSERT( !st || !st->pendingWait || st->cofunc );
792
793 // the tool state handler is waiting for events (i.e. called Wait() method)
794 if( st && st->cofunc && st->pendingWait && st->waitEvents.Matches( aEvent ) )
795 {
796 if( !aEvent.FirstResponder() )
797 aEvent.SetFirstResponder( st->theTool );
798
799 // got matching event? clear wait list and wake up the coroutine
800 st->wakeupEvent = aEvent;
801 st->pendingWait = false;
802 st->waitEvents.clear();
803
804 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchInternal - Waking tool %s for event: %s" ),
805 st->theTool->GetName(), aEvent.Format() );
806
807 setActiveState( st );
808 bool end = !st->cofunc->Resume();
809
810 if( end )
811 {
812 it = finishTool( st );
813 increment = false;
814 }
815
816 // If the tool did not request the event be passed to other tools, we're done
817 if( !st->wakeupEvent.PassEvent() )
818 {
819 wxLogTrace( kicadTraceToolStack,
820 wxS( "TOOL_MANAGER::dispatchInternal - tool %s stopped passing event: %s" ),
821 st->theTool->GetName(), aEvent.Format() );
822
823 return true;
824 }
825 }
826
827 if( increment )
828 ++it;
829 }
830
831 for( const auto& state : m_toolState )
832 {
833 TOOL_STATE* st = state.second;
834 bool finished = false;
835
836 // no state handler in progress - check if there are any transitions (defined by
837 // Go() method that match the event.
838 if( !st->transitions.empty() )
839 {
840 for( const TRANSITION& tr : st->transitions )
841 {
842 if( tr.first.Matches( aEvent ) )
843 {
844 auto func_copy = tr.second;
845
846 if( !aEvent.FirstResponder() )
847 aEvent.SetFirstResponder( st->theTool );
848
849 // if there is already a context, then push it on the stack
850 // and transfer the previous view control settings to the new context
851 if( st->cofunc )
852 {
853 KIGFX::VC_SETTINGS viewControlSettings = st->vcSettings;
854 st->Push();
855 st->vcSettings = std::move( viewControlSettings );
856 }
857
858 st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( std::move( func_copy ) );
859
860 wxLogTrace( kicadTraceToolStack,
861 wxS( "TOOL_MANAGER::dispatchInternal - Running tool %s for event: %s" ),
862 st->theTool->GetName(), aEvent.Format() );
863
864 // got match? Run the handler.
865 setActiveState( st );
866 st->idle = false;
867 st->initialEvent = aEvent;
868 st->cofunc->Call( st->initialEvent );
869 handled = true;
870
871 if( !st->cofunc->Running() )
872 finishTool( st ); // The coroutine has finished immediately?
873
874 // if it is a message, continue processing
875 finished = !( aEvent.Category() == TC_MESSAGE );
876
877 // there is no point in further checking, as transitions got cleared
878 break;
879 }
880 }
881 }
882
883 if( finished )
884 break; // only the first tool gets the event
885 }
886
887 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchInternal - %s handle event: %s" ),
888 ( handled ? wxS( "Did" ) : wxS( "Did not" ) ), aEvent.Format() );
889
890 return handled;
891}
892
893
895{
896 if( aEvent.Action() == TA_KEY_PRESSED )
897 return m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() );
898
899 return false;
900}
901
902
904{
905 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::dispatchActivation - Received event: %s" ),
906 aEvent.Format() );
907
908 if( aEvent.IsActivate() )
909 {
910 auto tool = m_toolNameIndex.find( aEvent.getCommandStr() );
911
912 if( tool != m_toolNameIndex.end() )
913 {
914 wxLogTrace( kicadTraceToolStack,
915 wxS( "TOOL_MANAGER::dispatchActivation - Running tool %s for event: %s" ),
916 tool->second->theTool->GetName(), aEvent.Format() );
917
918 runTool( tool->second->theTool );
919 return true;
920 }
921 }
922
923 return false;
924}
925
926
928{
929 // Don't open context menus if we're inside a yielding event loop such as a progress dialog.
930 // Opening a popup menu during YieldFor creates a nested modal situation that can leave the
931 // menu stuck and unresponsive, potentially locking up the entire UI on some platforms.
932 if( !aEvent.IsClick( BUT_RIGHT ) )
933 {
934 if( wxEventLoopBase* loop = wxEventLoopBase::GetActive() )
935 {
936 if( loop->IsYielding() )
937 return;
938 }
939 }
940
941 for( TOOL_ID toolId : m_activeTools )
942 {
943 TOOL_STATE* st = m_toolIdIndex[toolId];
944
945 // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
946 // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
947 if( st->contextMenuTrigger == CMENU_OFF )
948 continue;
949
950 if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
951 break;
952
953 if( st->cofunc )
954 {
955 st->pendingWait = true;
957 }
958
959 // Store the menu pointer in case it is changed by the TOOL when handling menu events
960 ACTION_MENU* m = st->contextMenu;
961
962 if( st->contextMenuTrigger == CMENU_NOW )
964
965 // Store the cursor position, so the tools could execute actions
966 // using the point where the user has invoked a context menu
967 if( m_viewControls )
968 m_menuCursor = m_viewControls->GetCursorPosition();
969
970 // Save all tools cursor settings, as they will be overridden
971 for( const std::pair<const TOOL_ID, TOOL_STATE*>& idState : m_toolIdIndex )
972 {
973 TOOL_STATE* s = idState.second;
974 const auto& vc = s->vcSettings;
975
976 if( vc.m_forceCursorPosition )
977 m_cursorSettings[idState.first] = vc.m_forcedPosition;
978 else
979 m_cursorSettings[idState.first] = std::nullopt;
980 }
981
982 if( m_viewControls )
983 m_viewControls->ForceCursorPosition( true, m_menuCursor );
984
985 // Display a copy of menu
986 std::unique_ptr<ACTION_MENU> menu( m->Clone() );
987
988 m_menuOwner = toolId;
989 m_menuActive = true;
990
991 if( wxWindow* frame = dynamic_cast<wxWindow*>( m_frame ) )
992 {
993 // By providing an explicit position, wx engages its screen clamp mechanism
994 frame->PopupMenu( menu.get(), frame->ScreenToClient( KIPLATFORM::UI::GetMousePosition() ) );
995 }
996
997 // Warp the cursor if a menu item was selected
998 if( menu->GetSelected() >= 0 )
999 {
1001 m_viewControls->WarpMouseCursor( m_menuCursor, true, false );
1002 }
1003 // Otherwise notify the tool of a canceled menu
1004 else
1005 {
1007 evt.SetHasPosition( false );
1008 evt.SetParameter( m );
1009 dispatchInternal( evt );
1010 }
1011
1012 // Restore setting in case it was vetoed
1014
1015 // Notify the tools that menu has been closed
1017 evt.SetHasPosition( false );
1018 evt.SetParameter( m );
1019 dispatchInternal( evt );
1020
1021 m_menuActive = false;
1022 m_menuOwner = -1;
1023
1024 // Restore cursor settings
1025 for( const std::pair<const TOOL_ID,
1026 std::optional<VECTOR2D>>& cursorSetting : m_cursorSettings )
1027 {
1028 auto it = m_toolIdIndex.find( cursorSetting.first );
1029 wxASSERT( it != m_toolIdIndex.end() );
1030
1031 if( it == m_toolIdIndex.end() )
1032 continue;
1033
1034 KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
1035 vc.m_forceCursorPosition = (bool) cursorSetting.second;
1036 vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
1037 }
1038
1039 m_cursorSettings.clear();
1040 break;
1041 }
1042}
1043
1044
1046{
1048 m_viewControls->WarpMouseCursor( m_menuCursor, true, false );
1049
1050 // Don't warp again when the menu is closed
1052}
1053
1054
1055TOOL_MANAGER::ID_LIST::iterator TOOL_MANAGER::finishTool( TOOL_STATE* aState )
1056{
1057 auto it = std::find( m_activeTools.begin(), m_activeTools.end(), aState->theTool->GetId() );
1058
1059 if( !aState->Pop() )
1060 {
1061 // Deactivate the tool if there are no other contexts saved on the stack
1062 if( it != m_activeTools.end() )
1063 it = m_activeTools.erase( it );
1064
1065 aState->idle = true;
1066 }
1067
1068 if( aState == m_activeState )
1069 setActiveState( nullptr );
1070
1071 return it;
1072}
1073
1074
1076{
1077 // Once the tool manager is shutting down, don't start
1078 // activating more tools
1079 if( m_shuttingDown )
1080 return true;
1081
1082 bool handled = processEvent( aEvent );
1083
1084 TOOL_STATE* activeTool = GetCurrentToolState();
1085
1086 if( activeTool )
1087 setActiveState( activeTool );
1088
1089 if( m_view && m_view->IsDirty() )
1090 {
1091#if defined( __WXMAC__ )
1092 wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu
1093#endif
1094 }
1095
1096 UpdateUI( aEvent );
1097
1098 return handled;
1099}
1100
1101
1103 CONTEXT_MENU_TRIGGER aTrigger )
1104{
1105 TOOL_STATE* st = m_toolState[aTool];
1106
1107 st->contextMenu = aMenu;
1108 st->contextMenuTrigger = aTrigger;
1109}
1110
1111
1113{
1114 if( TOOL_STATE* active = GetCurrentToolState() )
1115 return active->vcSettings;
1116
1117 return m_viewControls->GetSettings();
1118}
1119
1120
1121TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName )
1122{
1123 static int currentId;
1124
1125 return currentId++;
1126}
1127
1128
1130 KIGFX::VIEW_CONTROLS* aViewControls,
1131 APP_SETTINGS_BASE* aSettings, TOOLS_HOLDER* aFrame )
1132{
1133 m_model = aModel;
1134 m_view = aView;
1135 m_viewControls = aViewControls;
1136 m_frame = aFrame;
1137 m_settings = aSettings;
1138}
1139
1140
1142{
1143 if( !isRegistered( aTool ) )
1144 return false;
1145
1146 // Just check if the tool is on the active tools stack
1147 return alg::contains( m_activeTools, aTool->GetId() );
1148}
1149
1150
1152{
1153 aState->vcSettings = m_viewControls->GetSettings();
1154
1155 if( m_menuActive )
1156 {
1157 // Context menu is active, so the cursor settings are overridden (see DispatchContextMenu())
1158 auto it = m_cursorSettings.find( aState->theTool->GetId() );
1159
1160 if( it != m_cursorSettings.end() )
1161 {
1162 const KIGFX::VC_SETTINGS& curr = m_viewControls->GetSettings();
1163
1164 // Tool has overridden the cursor position, so store the new settings
1166 {
1167 if( !curr.m_forceCursorPosition )
1168 it->second = std::nullopt;
1169 else
1170 it->second = curr.m_forcedPosition;
1171 }
1172 else
1173 {
1174 std::optional<VECTOR2D> cursor = it->second;
1175
1176 if( cursor )
1177 {
1178 aState->vcSettings.m_forceCursorPosition = true;
1180 }
1181 else
1182 {
1183 aState->vcSettings.m_forceCursorPosition = false;
1184 }
1185 }
1186 }
1187 }
1188}
1189
1190
1192{
1193 m_viewControls->ApplySettings( aState->vcSettings );
1194}
1195
1196
1198{
1199 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::processEvent - %s" ), aEvent.Format() );
1200
1201 // Capture the cursor position from the keyboard event so that hotkey-triggered actions use
1202 // the position at keypress time rather than polling a potentially stale position later in the
1203 // dispatch chain. The scoped guard restores any prior value so a nested hotkey dispatch does
1204 // not clobber the outer position.
1205 std::optional<VECTOR2D> hotKeyPos = m_hotKeyPos;
1206
1207 if( aEvent.HasPosition() && aEvent.Action() == TA_KEY_PRESSED )
1208 hotKeyPos = aEvent.Position();
1209
1210 // The guard's restore vectorises into an unconditional load of the optional's payload, which
1211 // GCC then reports as an uninitialized read on the disengaged path it can never observe.
1212#if defined( __GNUC__ ) && !defined( __clang__ )
1213#pragma GCC diagnostic push
1214#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
1215#endif
1216 SCOPED_SET_RESET<std::optional<VECTOR2D>> scopedHotKeyPos( m_hotKeyPos, hotKeyPos );
1217#if defined( __GNUC__ ) && !defined( __clang__ )
1218#pragma GCC diagnostic pop
1219#endif
1220
1221 // First try to dispatch the action associated with the event if it is a key press event
1222 bool handled = DispatchHotKey( aEvent );
1223
1224 if( !handled )
1225 {
1226 TOOL_EVENT mod_event( aEvent );
1227
1228 // Only immediate actions get the position. Otherwise clear for tool activation
1229 if( GetToolHolder() && !GetToolHolder()->GetDoImmediateActions() )
1230 {
1231 // An tool-selection-event has no position
1232 if( !mod_event.getCommandStr().empty()
1233 && mod_event.getCommandStr() != GetToolHolder()->CurrentToolName()
1234 && !mod_event.ForceImmediate() )
1235 {
1236 mod_event.SetHasPosition( false );
1237 }
1238 }
1239
1240 // If the event is not handled through a hotkey activation, pass it to the currently
1241 // running tool loops
1242 handled |= dispatchInternal( mod_event );
1243 handled |= dispatchActivation( mod_event );
1244
1245 // Open the context menu if requested by a tool
1246 DispatchContextMenu( mod_event );
1247
1248 // Dispatch any remaining events in the event queue
1249 while( !m_eventQueue.empty() )
1250 {
1251 TOOL_EVENT event = m_eventQueue.front();
1252 m_eventQueue.pop_front();
1253 processEvent( event );
1254 }
1255 }
1256
1257 wxLogTrace( kicadTraceToolStack, wxS( "TOOL_MANAGER::processEvent - %s handle event: %s" ),
1258 ( handled ? "Did" : "Did not" ), aEvent.Format() );
1259
1260 return handled;
1261}
1262
1263
1265{
1268
1269 m_activeState = aState;
1270
1272 applyViewControls( aState );
1273}
1274
1275
1277{
1278 auto it = m_toolIdIndex.find( aId );
1279
1280 if( it == m_toolIdIndex.end() )
1281 return false;
1282
1283 return !it->second->idle;
1284}
1285
1286
1288{
1289 EDA_BASE_FRAME* frame = dynamic_cast<EDA_BASE_FRAME*>( GetToolHolder() );
1290
1291 if( frame )
1292 frame->UpdateStatusBar();
1293}
Manage TOOL_ACTION objects.
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
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.
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
Implement a coroutine.
Definition coroutine.h:80
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition coroutine.h:282
void KiYield()
Stop execution of the coroutine and returns control to the caller.
Definition coroutine.h:245
bool Resume()
Resume execution of a previously yielded coroutine.
Definition coroutine.h:328
bool Running() const
Definition coroutine.h:377
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
Definition coroutine.h:268
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:98
An interface for classes handling user events controlling the view behavior such as zooming,...
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
RAII class that sets an value at construction and resets it to the original value at destruction.
Represent a single user action.
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Base abstract interface for all kinds of tools.
Definition tool_base.h:62
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:88
const std::string & GetName() const
Return the name of the tool.
Definition tool_base.h:132
TOOL_TYPE GetType() const
Return the type of the tool.
Definition tool_base.h:107
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ REDRAW
Full drawing refresh.
Definition tool_base.h:79
@ RUN
Tool is invoked after being inactive.
Definition tool_base.h:75
TOOL_ID GetId() const
Return the unique identifier of the tool.
Definition tool_base.h:119
void attachManager(TOOL_MANAGER *aManager)
Set the TOOL_MANAGER the tool will belong to.
Definition tool_base.cpp:58
A list of TOOL_EVENTs, with overloaded || operators allowing for concatenating TOOL_EVENTs with littl...
Definition tool_event.h:659
OPT_TOOL_EVENT Matches(const TOOL_EVENT &aEvent) const
Definition tool_event.h:698
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
bool PassEvent() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition tool_event.h:251
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
Definition tool_event.h:246
void SetMousePosition(const VECTOR2D &aP)
Definition tool_event.h:549
int KeyCode() const
Definition tool_event.h:372
TOOL_BASE * FirstResponder() const
Definition tool_event.h:264
bool IsActivate() const
Definition tool_event.h:341
void SetFirstResponder(TOOL_BASE *aTool)
Definition tool_event.h:265
bool IsSimulator() const
Indicate if the event is from the simulator.
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition tool_event.h:539
bool ForceImmediate() const
Returns if the action associated with this event should be treated as immediate regardless of the cur...
Definition tool_event.h:261
bool IsClick(int aButtonMask=BUT_ANY) const
TOOL_EVENT_CATEGORY Category() const
Return the category (eg. mouse/keyboard/action) of an event.
Definition tool_event.h:243
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:362
void SetHasPosition(bool aHasPosition)
Definition tool_event.h:257
const std::string & getCommandStr() const
Definition tool_event.h:569
bool IsChoiceMenu() const
Definition tool_event.h:351
const std::string Format() const
Return information about event in form of a human-readable string.
void applyViewControls(const TOOL_STATE *aState)
Apply KIGFX::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
Pointer to the state object corresponding to the currently executed tool.
void UpdateUI(const TOOL_EVENT &aEvent)
Update the status bar and synchronizes toolbars.
std::map< TOOL_ID, std::optional< VECTOR2D > > m_cursorSettings
Original cursor position, if overridden by the context menu handler.
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
Index of the registered tools to easily lookup by their type.
bool isRegistered(TOOL_BASE *aTool) const
Return information about a tool registration status.
APP_SETTINGS_BASE * m_settings
TOOL_STATE * GetCurrentToolState() const
Return the TOOL_STATE object representing the state of the active tool.
VECTOR2D GetCursorPosition() const
std::list< TOOL_EVENT > m_eventQueue
Queue that stores events to be processed at the end of the event processing cycle.
std::pair< TOOL_EVENT_LIST, TOOL_STATE_FUNC > TRANSITION
void setActiveState(TOOL_STATE *aState)
Save the previous active state and sets a new one.
void DeactivateTool()
Deactivate the currently active tool.
std::optional< VECTOR2D > m_hotKeyPos
Mouse position captured at hotkey time, used to avoid the delay between keypress and action dispatch ...
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' names.
TOOLS_HOLDER * m_frame
void CancelTool()
Send a cancel event to the tool currently at the top of the tool stack.
bool m_warpMouseAfterContextMenu
KIGFX::VIEW_CONTROLS * m_viewControls
void WarpAfterContextMenu()
Normally we warp the mouse after the context menu action runs.
ACTION_MANAGER * m_actionMgr
Instance of ACTION_MANAGER that handles TOOL_ACTIONs.
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
VECTOR2D GetMousePosition() const
bool processEvent(const TOOL_EVENT &aEvent)
Main function for event processing.
std::list< TOOL_ID > ID_LIST
ID_LIST::iterator finishTool(TOOL_STATE *aState)
Deactivate a tool and does the necessary clean up.
bool doRunAction(const TOOL_ACTION &aAction, bool aNow, const ki::any &aParam, COMMIT *aCommit, bool aFromAPI=false)
Helper function to actually run an action.
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 current states, associated by tools' ID numbers.
TOOL_ID m_menuOwner
Tool currently displaying a popup menu. It is negative when there is no menu displayed.
KIGFX::VIEW * m_view
void ClearTransitions(TOOL_BASE *aTool)
Clear the state transition map for a tool.
bool m_shuttingDown
True if the tool manager is shutting down (don't process additional events)
void saveViewControls(TOOL_STATE *aState)
Save the KIGFX::VIEW_CONTROLS settings to the tool state object.
int GetHotKey(const TOOL_ACTION &aAction) const
Return the hot key associated with a given action or 0 if there is none.
bool m_menuActive
Flag indicating whether a context menu is currently displayed.
TOOL_STATE_MAP m_toolState
Index of registered tools current states, associated by tools' objects.
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
Stack of the active tools.
void ResetTools(TOOL_BASE::RESET_REASON aReason)
Reset all tools (i.e.
EDA_ITEM * m_model
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)
Generate 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
List of tools in the order they were registered.
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()
Initialize all registered tools.
VECTOR2D m_menuCursor
Right click context menu position.
void ShutdownAllTools()
Shutdown all tools with a currently registered event loop in this tool manager by waking them up with...
A type-safe container of any type.
Definition ki_any.h:92
bool has_value() const noexcept
Report whether there is a contained object or not.
Definition ki_any.h:311
Base window classes and related definitions.
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
void AddTransactionBreadcrumb(const wxString &aMsg, const wxString &aCategory)
Add a transaction breadcrumb.
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:851
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
Structure to keep VIEW_CONTROLS settings for easy store/restore operations.
VECTOR2D m_forcedPosition
Forced cursor position (world coordinates).
bool m_forceCursorPosition
Is the forced cursor position enabled.
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.
void resetRuntimeState()
Resets runtime-only state that must not leak across tool activations.
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.
void clear()
Restores the initial state.
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
VECTOR2I end
std::function< int(const TOOL_EVENT &)> TOOL_STATE_FUNC
Definition tool_base.h:54
int TOOL_ID
Unique identifier for tools.
Definition tool_base.h:52
@ INTERACTIVE
Tool that interacts with the user.
Definition tool_base.h:45
@ TA_ANY
Definition tool_event.h:122
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ TA_ACTIVATE
Tool activation event.
Definition tool_event.h:111
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:97
@ TA_PRIME
Tool priming event (a special mouse click).
Definition tool_event.h:120
@ TA_KEY_PRESSED
Definition tool_event.h:72
@ TA_CANCEL_TOOL
Tool cancel event.
Definition tool_event.h:86
CONTEXT_MENU_TRIGGER
Defines when a context menu is opened.
Definition tool_event.h:150
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:152
@ CMENU_OFF
Never.
Definition tool_event.h:153
@ CMENU_BUTTON
On the right button.
Definition tool_event.h:151
@ STS_CANCELLED
Definition tool_event.h:160
@ STS_FINISHED
Definition tool_event.h:159
@ STS_RUNNING
Definition tool_event.h:158
@ TC_ANY
Definition tool_event.h:56
@ TC_COMMAND
Definition tool_event.h:53
@ TC_MOUSE
Definition tool_event.h:51
@ TC_MESSAGE
Definition tool_event.h:54
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
wxLogTrace helper definitions.
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
VECTOR2D ToVECTOR2D(const wxPoint &aPoint)
Definition vector2wx.h:36