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