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