You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

980 lines
26 KiB

12 years ago
12 years ago
11 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2018 CERN
  5. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <map>
  26. #include <stack>
  27. #include <algorithm>
  28. #include <core/optional.h>
  29. #include <wx/event.h>
  30. #include <wx/clipbrd.h>
  31. #include <view/view.h>
  32. #include <tool/tool_base.h>
  33. #include <tool/tool_interactive.h>
  34. #include <tool/tool_manager.h>
  35. #include <tool/action_menu.h>
  36. #include <tool/coroutine.h>
  37. #include <tool/action_manager.h>
  38. #include <class_draw_panel_gal.h>
  39. #include <draw_frame.h>
  40. /// Struct describing the current execution state of a TOOL
  41. struct TOOL_MANAGER::TOOL_STATE
  42. {
  43. TOOL_STATE( TOOL_BASE* aTool ) :
  44. theTool( aTool )
  45. {
  46. clear();
  47. }
  48. TOOL_STATE( const TOOL_STATE& aState )
  49. {
  50. theTool = aState.theTool;
  51. idle = aState.idle;
  52. pendingWait = aState.pendingWait;
  53. pendingContextMenu = aState.pendingContextMenu;
  54. contextMenu = aState.contextMenu;
  55. contextMenuTrigger = aState.contextMenuTrigger;
  56. cofunc = aState.cofunc;
  57. wakeupEvent = aState.wakeupEvent;
  58. waitEvents = aState.waitEvents;
  59. transitions = aState.transitions;
  60. vcSettings = aState.vcSettings;
  61. // do not copy stateStack
  62. }
  63. ~TOOL_STATE()
  64. {
  65. if( !stateStack.empty() )
  66. wxFAIL;
  67. }
  68. /// The tool itself
  69. TOOL_BASE* theTool;
  70. /// Is the tool active (pending execution) or disabled at the moment
  71. bool idle;
  72. /// Flag defining if the tool is waiting for any event (i.e. if it
  73. /// issued a Wait() call).
  74. bool pendingWait;
  75. /// Is there a context menu being displayed
  76. bool pendingContextMenu;
  77. /// Context menu currently used by the tool
  78. ACTION_MENU* contextMenu;
  79. /// Defines when the context menu is opened
  80. CONTEXT_MENU_TRIGGER contextMenuTrigger;
  81. /// Tool execution context
  82. COROUTINE<int, const TOOL_EVENT&>* cofunc;
  83. /// The event that triggered the execution/wakeup of the tool after Wait() call
  84. TOOL_EVENT wakeupEvent;
  85. /// List of events the tool is currently waiting for
  86. TOOL_EVENT_LIST waitEvents;
  87. /// List of possible transitions (ie. association of events and state handlers that are executed
  88. /// upon the event reception
  89. std::vector<TRANSITION> transitions;
  90. /// VIEW_CONTROLS settings to preserve settings when the tools are switched
  91. KIGFX::VC_SETTINGS vcSettings;
  92. TOOL_STATE& operator=( const TOOL_STATE& aState )
  93. {
  94. theTool = aState.theTool;
  95. idle = aState.idle;
  96. pendingWait = aState.pendingWait;
  97. pendingContextMenu = aState.pendingContextMenu;
  98. contextMenu = aState.contextMenu;
  99. contextMenuTrigger = aState.contextMenuTrigger;
  100. cofunc = aState.cofunc;
  101. wakeupEvent = aState.wakeupEvent;
  102. waitEvents = aState.waitEvents;
  103. transitions = aState.transitions;
  104. vcSettings = aState.vcSettings;
  105. // do not copy stateStack
  106. return *this;
  107. }
  108. bool operator==( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
  109. {
  110. return aRhs.theTool == this->theTool;
  111. }
  112. bool operator!=( const TOOL_MANAGER::TOOL_STATE& aRhs ) const
  113. {
  114. return aRhs.theTool != this->theTool;
  115. }
  116. /**
  117. * Function Push()
  118. * Stores the current state of the tool on stack. Stacks are stored internally and are not
  119. * shared between different TOOL_STATE objects.
  120. */
  121. void Push()
  122. {
  123. auto state = std::make_unique<TOOL_STATE>( *this );
  124. stateStack.push( std::move( state ) );
  125. clear();
  126. }
  127. /**
  128. * Function Pop()
  129. * Restores state of the tool from stack. Stacks are stored internally and are not
  130. * shared between different TOOL_STATE objects.
  131. * @return True if state was restored, false if the stack was empty.
  132. */
  133. bool Pop()
  134. {
  135. delete cofunc;
  136. if( !stateStack.empty() )
  137. {
  138. *this = *stateStack.top().get();
  139. stateStack.pop();
  140. return true;
  141. }
  142. else
  143. {
  144. cofunc = NULL;
  145. return false;
  146. }
  147. }
  148. private:
  149. ///> Stack preserving previous states of a TOOL.
  150. std::stack<std::unique_ptr<TOOL_STATE>> stateStack;
  151. ///> Restores the initial state.
  152. void clear()
  153. {
  154. idle = true;
  155. pendingWait = false;
  156. pendingContextMenu = false;
  157. cofunc = NULL;
  158. contextMenu = NULL;
  159. contextMenuTrigger = CMENU_OFF;
  160. vcSettings.Reset();
  161. transitions.clear();
  162. }
  163. };
  164. TOOL_MANAGER::TOOL_MANAGER() :
  165. m_model( NULL ),
  166. m_view( NULL ),
  167. m_viewControls( NULL ),
  168. m_editFrame( NULL ),
  169. m_passEvent( false ),
  170. m_menuActive( false ),
  171. m_menuOwner( -1 ),
  172. m_activeState( nullptr )
  173. {
  174. m_actionMgr = new ACTION_MANAGER( this );
  175. }
  176. TOOL_MANAGER::~TOOL_MANAGER()
  177. {
  178. std::map<TOOL_BASE*, TOOL_STATE*>::iterator it, it_end;
  179. for( it = m_toolState.begin(), it_end = m_toolState.end(); it != it_end; ++it )
  180. {
  181. delete it->second->cofunc; // delete cofunction
  182. delete it->second; // delete TOOL_STATE
  183. delete it->first; // delete the tool itself
  184. }
  185. delete m_actionMgr;
  186. }
  187. void TOOL_MANAGER::RegisterTool( TOOL_BASE* aTool )
  188. {
  189. wxASSERT_MSG( m_toolNameIndex.find( aTool->GetName() ) == m_toolNameIndex.end(),
  190. wxT( "Adding two tools with the same name may result in unexpected behaviour.") );
  191. wxASSERT_MSG( m_toolIdIndex.find( aTool->GetId() ) == m_toolIdIndex.end(),
  192. wxT( "Adding two tools with the same ID may result in unexpected behaviour.") );
  193. wxASSERT_MSG( m_toolTypes.find( typeid( *aTool ).name() ) == m_toolTypes.end(),
  194. wxT( "Adding two tools of the same type may result in unexpected behaviour.") );
  195. TOOL_STATE* st = new TOOL_STATE( aTool );
  196. m_toolState[aTool] = st;
  197. m_toolNameIndex[aTool->GetName()] = st;
  198. m_toolIdIndex[aTool->GetId()] = st;
  199. m_toolTypes[typeid( *aTool ).name()] = st->theTool;
  200. aTool->attachManager( this );
  201. }
  202. bool TOOL_MANAGER::InvokeTool( TOOL_ID aToolId )
  203. {
  204. TOOL_BASE* tool = FindTool( aToolId );
  205. if( tool && tool->GetType() == INTERACTIVE )
  206. return invokeTool( tool );
  207. return false; // there is no tool with the given id
  208. }
  209. bool TOOL_MANAGER::InvokeTool( const std::string& aToolName )
  210. {
  211. TOOL_BASE* tool = FindTool( aToolName );
  212. if( tool && tool->GetType() == INTERACTIVE )
  213. return invokeTool( tool );
  214. return false; // there is no tool with the given name
  215. }
  216. void TOOL_MANAGER::RegisterAction( TOOL_ACTION* aAction )
  217. {
  218. m_actionMgr->RegisterAction( aAction );
  219. }
  220. void TOOL_MANAGER::UnregisterAction( TOOL_ACTION* aAction )
  221. {
  222. m_actionMgr->UnregisterAction( aAction );
  223. }
  224. bool TOOL_MANAGER::RunAction( const std::string& aActionName, bool aNow, void* aParam )
  225. {
  226. TOOL_ACTION* action = m_actionMgr->FindAction( aActionName );
  227. if( !action )
  228. {
  229. wxASSERT_MSG( false, wxString::Format( wxT( "Could not find action %s." ), aActionName ) );
  230. return false;
  231. }
  232. RunAction( *action, aNow, aParam );
  233. return false;
  234. }
  235. void TOOL_MANAGER::RunAction( const TOOL_ACTION& aAction, bool aNow, void* aParam )
  236. {
  237. TOOL_EVENT event = aAction.MakeEvent();
  238. // Allow to override the action parameter
  239. if( aParam )
  240. event.SetParameter( aParam );
  241. if( aNow )
  242. {
  243. TOOL_STATE* current = m_activeState;
  244. processEvent( event );
  245. setActiveState( current );
  246. UpdateUI();
  247. }
  248. else
  249. {
  250. PostEvent( event );
  251. }
  252. }
  253. int TOOL_MANAGER::GetHotKey( const TOOL_ACTION& aAction )
  254. {
  255. return m_actionMgr->GetHotKey( aAction );
  256. }
  257. void TOOL_MANAGER::UpdateHotKeys()
  258. {
  259. m_actionMgr->UpdateHotKeys();
  260. }
  261. bool TOOL_MANAGER::invokeTool( TOOL_BASE* aTool )
  262. {
  263. wxASSERT( aTool != NULL );
  264. TOOL_EVENT evt( TC_COMMAND, TA_ACTIVATE, aTool->GetName() );
  265. processEvent( evt );
  266. if( TOOL_STATE* active = GetCurrentToolState() )
  267. setActiveState( active );
  268. return true;
  269. }
  270. bool TOOL_MANAGER::runTool( TOOL_ID aToolId )
  271. {
  272. TOOL_BASE* tool = FindTool( aToolId );
  273. if( tool && tool->GetType() == INTERACTIVE )
  274. return runTool( tool );
  275. return false; // there is no tool with the given id
  276. }
  277. bool TOOL_MANAGER::runTool( const std::string& aToolName )
  278. {
  279. TOOL_BASE* tool = FindTool( aToolName );
  280. if( tool && tool->GetType() == INTERACTIVE )
  281. return runTool( tool );
  282. return false; // there is no tool with the given name
  283. }
  284. bool TOOL_MANAGER::runTool( TOOL_BASE* aTool )
  285. {
  286. wxASSERT( aTool != NULL );
  287. if( !isRegistered( aTool ) )
  288. {
  289. wxASSERT_MSG( false, wxT( "You cannot run unregistered tools" ) );
  290. return false;
  291. }
  292. TOOL_ID id = aTool->GetId();
  293. if( aTool->GetType() == INTERACTIVE )
  294. static_cast<TOOL_INTERACTIVE*>( aTool )->resetTransitions();
  295. // If the tool is already active, bring it to the top of the active tools stack
  296. if( isActive( aTool ) )
  297. {
  298. m_activeTools.erase( std::find( m_activeTools.begin(), m_activeTools.end(), id ) );
  299. m_activeTools.push_front( id );
  300. return false;
  301. }
  302. setActiveState( m_toolIdIndex[id] );
  303. aTool->Reset( TOOL_INTERACTIVE::RUN );
  304. // Add the tool on the front of the processing queue (it gets events first)
  305. m_activeTools.push_front( id );
  306. return true;
  307. }
  308. TOOL_BASE* TOOL_MANAGER::FindTool( int aId ) const
  309. {
  310. std::map<TOOL_ID, TOOL_STATE*>::const_iterator it = m_toolIdIndex.find( aId );
  311. if( it != m_toolIdIndex.end() )
  312. return it->second->theTool;
  313. return NULL;
  314. }
  315. TOOL_BASE* TOOL_MANAGER::FindTool( const std::string& aName ) const
  316. {
  317. std::map<std::string, TOOL_STATE*>::const_iterator it = m_toolNameIndex.find( aName );
  318. if( it != m_toolNameIndex.end() )
  319. return it->second->theTool;
  320. return NULL;
  321. }
  322. void TOOL_MANAGER::DeactivateTool()
  323. {
  324. // Deactivate the active tool, but do not run anything new
  325. TOOL_EVENT evt( TC_COMMAND, TA_CANCEL_TOOL );
  326. processEvent( evt );
  327. }
  328. void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason )
  329. {
  330. DeactivateTool();
  331. for( auto& state : m_toolState )
  332. {
  333. TOOL_BASE* tool = state.first;
  334. setActiveState( state.second );
  335. tool->Reset( aReason );
  336. if( tool->GetType() == INTERACTIVE )
  337. static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
  338. }
  339. }
  340. void TOOL_MANAGER::InitTools()
  341. {
  342. for( auto it = m_toolState.begin(); it != m_toolState.end(); /* iteration in the loop */ )
  343. {
  344. TOOL_BASE* tool = it->first;
  345. TOOL_STATE* state = it->second;
  346. setActiveState( state );
  347. ++it; // keep the iterator valid if the element is going to be erased
  348. if( !tool->Init() )
  349. {
  350. wxMessageBox(
  351. wxString::Format( "Initialization of tool \"%s\" failed", tool->GetName() ) );
  352. // Unregister the tool
  353. setActiveState( nullptr );
  354. m_toolState.erase( tool );
  355. m_toolNameIndex.erase( tool->GetName() );
  356. m_toolIdIndex.erase( tool->GetId() );
  357. m_toolTypes.erase( typeid( *tool ).name() );
  358. delete state;
  359. delete tool;
  360. }
  361. }
  362. ResetTools( TOOL_BASE::RUN );
  363. }
  364. int TOOL_MANAGER::GetPriority( int aToolId ) const
  365. {
  366. int priority = 0;
  367. for( auto it = m_activeTools.begin(), itEnd = m_activeTools.end(); it != itEnd; ++it )
  368. {
  369. if( *it == aToolId )
  370. return priority;
  371. ++priority;
  372. }
  373. return -1;
  374. }
  375. void TOOL_MANAGER::ScheduleNextState( TOOL_BASE* aTool, TOOL_STATE_FUNC& aHandler,
  376. const TOOL_EVENT_LIST& aConditions )
  377. {
  378. TOOL_STATE* st = m_toolState[aTool];
  379. st->transitions.push_back( TRANSITION( aConditions, aHandler ) );
  380. }
  381. void TOOL_MANAGER::ClearTransitions( TOOL_BASE* aTool )
  382. {
  383. m_toolState[aTool]->transitions.clear();
  384. }
  385. void TOOL_MANAGER::RunMainStack( TOOL_BASE* aTool, std::function<void()> aFunc )
  386. {
  387. TOOL_STATE* st = m_toolState[aTool];
  388. setActiveState( st );
  389. st->cofunc->RunMainStack( std::move( aFunc ) );
  390. }
  391. OPT<TOOL_EVENT> TOOL_MANAGER::ScheduleWait( TOOL_BASE* aTool, const TOOL_EVENT_LIST& aConditions )
  392. {
  393. TOOL_STATE* st = m_toolState[aTool];
  394. wxASSERT( !st->pendingWait ); // everything collapses on two KiYield() in a row
  395. // indicate to the manager that we are going to sleep and we shall be
  396. // woken up when an event matching aConditions arrive
  397. st->pendingWait = true;
  398. st->waitEvents = aConditions;
  399. // switch context back to event dispatcher loop
  400. st->cofunc->KiYield();
  401. return st->wakeupEvent;
  402. }
  403. void TOOL_MANAGER::dispatchInternal( const TOOL_EVENT& aEvent )
  404. {
  405. // iterate over all registered tools
  406. for( auto it = m_activeTools.begin(); it != m_activeTools.end(); ++it )
  407. {
  408. TOOL_STATE* st = m_toolIdIndex[*it];
  409. // forward context menu events to the tool that created the menu
  410. if( aEvent.IsMenu() )
  411. {
  412. if( *it != m_menuOwner )
  413. continue;
  414. }
  415. // the tool state handler is waiting for events (i.e. called Wait() method)
  416. if( st->pendingWait )
  417. {
  418. if( st->waitEvents.Matches( aEvent ) )
  419. {
  420. // By default only messages are passed further
  421. m_passEvent = ( aEvent.Category() == TC_MESSAGE );
  422. // got matching event? clear wait list and wake up the coroutine
  423. st->wakeupEvent = aEvent;
  424. st->pendingWait = false;
  425. st->waitEvents.clear();
  426. if( st->cofunc )
  427. {
  428. setActiveState( st );
  429. bool end = !st->cofunc->Resume();
  430. if( end )
  431. it = finishTool( st );
  432. }
  433. // If the tool did not request to propagate
  434. // the event to other tools, we should stop it now
  435. if( !m_passEvent )
  436. break;
  437. }
  438. }
  439. }
  440. for( auto& state : m_toolState )
  441. {
  442. TOOL_STATE* st = state.second;
  443. bool finished = false;
  444. // no state handler in progress - check if there are any transitions (defined by
  445. // Go() method that match the event.
  446. if( !st->transitions.empty() )
  447. {
  448. for( TRANSITION& tr : st->transitions )
  449. {
  450. if( tr.first.Matches( aEvent ) )
  451. {
  452. auto func_copy = tr.second;
  453. // if there is already a context, then push it on the stack
  454. // and transfer the previous view control settings to the new context
  455. if( st->cofunc )
  456. {
  457. auto vc = st->vcSettings;
  458. st->Push();
  459. st->vcSettings = vc;
  460. }
  461. st->cofunc = new COROUTINE<int, const TOOL_EVENT&>( std::move( func_copy ) );
  462. // as the state changes, the transition table has to be set up again
  463. st->transitions.clear();
  464. // got match? Run the handler.
  465. setActiveState( st );
  466. st->idle = false;
  467. st->cofunc->Call( aEvent );
  468. if( !st->cofunc->Running() )
  469. finishTool( st ); // The couroutine has finished immediately?
  470. // if it is a message, continue processing
  471. finished = !( aEvent.Category() == TC_MESSAGE );
  472. // there is no point in further checking, as transitions got cleared
  473. break;
  474. }
  475. }
  476. }
  477. if( finished )
  478. break; // only the first tool gets the event
  479. }
  480. }
  481. bool TOOL_MANAGER::dispatchStandardEvents( const TOOL_EVENT& aEvent )
  482. {
  483. if( aEvent.Action() == TA_KEY_PRESSED )
  484. {
  485. // Check if there is a hotkey associated
  486. if( m_actionMgr->RunHotKey( aEvent.Modifier() | aEvent.KeyCode() ) )
  487. return false; // hotkey event was handled so it does not go any further
  488. }
  489. return true;
  490. }
  491. bool TOOL_MANAGER::dispatchActivation( const TOOL_EVENT& aEvent )
  492. {
  493. if( aEvent.IsActivate() )
  494. {
  495. wxString cmdStr( *aEvent.GetCommandStr() );
  496. std::map<std::string, TOOL_STATE*>::iterator tool = m_toolNameIndex.find( *aEvent.GetCommandStr() );
  497. if( tool != m_toolNameIndex.end() )
  498. {
  499. runTool( tool->second->theTool );
  500. return true;
  501. }
  502. }
  503. return false;
  504. }
  505. void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
  506. {
  507. for( TOOL_ID toolId : m_activeTools )
  508. {
  509. TOOL_STATE* st = m_toolIdIndex[toolId];
  510. // the tool requested a context menu. The menu is activated on RMB click (CMENU_BUTTON mode)
  511. // or immediately (CMENU_NOW) mode. The latter is used for clarification lists.
  512. if( st->contextMenuTrigger == CMENU_OFF )
  513. continue;
  514. if( st->contextMenuTrigger == CMENU_BUTTON && !aEvent.IsClick( BUT_RIGHT ) )
  515. break;
  516. st->pendingWait = true;
  517. st->waitEvents = TOOL_EVENT( TC_ANY, TA_ANY );
  518. // Store the menu pointer in case it is changed by the TOOL when handling menu events
  519. ACTION_MENU* m = st->contextMenu;
  520. if( st->contextMenuTrigger == CMENU_NOW )
  521. st->contextMenuTrigger = CMENU_OFF;
  522. // Store the cursor position, so the tools could execute actions
  523. // using the point where the user has invoked a context menu
  524. m_menuCursor = m_viewControls->GetCursorPosition();
  525. // Save all tools cursor settings, as they will be overridden
  526. for( auto idState : m_toolIdIndex )
  527. {
  528. TOOL_STATE* s = idState.second;
  529. const auto& vc = s->vcSettings;
  530. if( vc.m_forceCursorPosition )
  531. m_cursorSettings[idState.first] = vc.m_forcedPosition;
  532. else
  533. m_cursorSettings[idState.first] = NULLOPT;
  534. }
  535. m_viewControls->ForceCursorPosition( true, m_menuCursor );
  536. // Display a copy of menu
  537. std::unique_ptr<ACTION_MENU> menu( m->Clone() );
  538. m_menuOwner = toolId;
  539. m_menuActive = true;
  540. auto frame = dynamic_cast<wxFrame*>( m_editFrame );
  541. if( frame )
  542. frame->PopupMenu( menu.get() );
  543. // Warp the cursor if a menu item was selected
  544. if( menu->GetSelected() >= 0 && m_warpMouseAfterContextMenu )
  545. m_viewControls->WarpCursor( m_menuCursor, true, false );
  546. // Otherwise notify the tool of a cancelled menu
  547. else
  548. {
  549. TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CHOICE, -1 );
  550. evt.SetParameter( m );
  551. dispatchInternal( evt );
  552. }
  553. // Restore setting in case it was vetoed
  554. m_warpMouseAfterContextMenu = true;
  555. // Notify the tools that menu has been closed
  556. TOOL_EVENT evt( TC_COMMAND, TA_CONTEXT_MENU_CLOSED );
  557. evt.SetParameter( m );
  558. dispatchInternal( evt );
  559. m_menuActive = false;
  560. m_menuOwner = -1;
  561. // Restore cursor settings
  562. for( auto cursorSetting : m_cursorSettings )
  563. {
  564. auto it = m_toolIdIndex.find( cursorSetting.first );
  565. wxASSERT( it != m_toolIdIndex.end() );
  566. if( it == m_toolIdIndex.end() )
  567. continue;
  568. KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
  569. vc.m_forceCursorPosition = (bool) cursorSetting.second;
  570. vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
  571. }
  572. m_cursorSettings.clear();
  573. break;
  574. }
  575. }
  576. TOOL_MANAGER::ID_LIST::iterator TOOL_MANAGER::finishTool( TOOL_STATE* aState )
  577. {
  578. auto it = std::find( m_activeTools.begin(), m_activeTools.end(), aState->theTool->GetId() );
  579. if( !aState->Pop() )
  580. {
  581. // Deactivate the tool if there are no other contexts saved on the stack
  582. if( it != m_activeTools.end() )
  583. it = m_activeTools.erase( it );
  584. aState->idle = true;
  585. }
  586. if( aState == m_activeState )
  587. setActiveState( nullptr );
  588. // Set transitions to be ready for future TOOL_EVENTs
  589. TOOL_BASE* tool = aState->theTool;
  590. if( tool->GetType() == INTERACTIVE )
  591. static_cast<TOOL_INTERACTIVE*>( tool )->resetTransitions();
  592. return it;
  593. }
  594. bool TOOL_MANAGER::ProcessEvent( const TOOL_EVENT& aEvent )
  595. {
  596. bool hotkey_handled = processEvent( aEvent );
  597. if( TOOL_STATE* active = GetCurrentToolState() )
  598. setActiveState( active );
  599. if( m_view->IsDirty() )
  600. {
  601. auto f = dynamic_cast<EDA_DRAW_FRAME*>( GetEditFrame() );
  602. if( f )
  603. f->GetGalCanvas()->Refresh(); // fixme: ugly hack, provide a method in TOOL_DISPATCHER.
  604. #if defined( __WXMAC__ ) || defined( __WINDOWS__ )
  605. wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu
  606. #endif
  607. }
  608. UpdateUI();
  609. return hotkey_handled;
  610. }
  611. void TOOL_MANAGER::ScheduleContextMenu( TOOL_BASE* aTool, ACTION_MENU* aMenu,
  612. CONTEXT_MENU_TRIGGER aTrigger )
  613. {
  614. TOOL_STATE* st = m_toolState[aTool];
  615. st->contextMenu = aMenu;
  616. st->contextMenuTrigger = aTrigger;
  617. }
  618. bool TOOL_MANAGER::SaveClipboard( const std::string& aText )
  619. {
  620. if( wxTheClipboard->Open() )
  621. {
  622. wxTheClipboard->SetData( new wxTextDataObject( wxString( aText.c_str(), wxConvUTF8 ) ) );
  623. wxTheClipboard->Close();
  624. return true;
  625. }
  626. return false;
  627. }
  628. std::string TOOL_MANAGER::GetClipboard() const
  629. {
  630. std::string result;
  631. if( wxTheClipboard->Open() )
  632. {
  633. if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
  634. {
  635. wxTextDataObject data;
  636. wxTheClipboard->GetData( data );
  637. result = data.GetText().mb_str();
  638. }
  639. wxTheClipboard->Close();
  640. }
  641. return result;
  642. }
  643. const KIGFX::VC_SETTINGS& TOOL_MANAGER::GetCurrentToolVC() const
  644. {
  645. if( TOOL_STATE* active = GetCurrentToolState() )
  646. return active->vcSettings;
  647. return m_viewControls->GetSettings();
  648. }
  649. TOOL_ID TOOL_MANAGER::MakeToolId( const std::string& aToolName )
  650. {
  651. static int currentId;
  652. return currentId++;
  653. }
  654. void TOOL_MANAGER::SetEnvironment( EDA_ITEM* aModel, KIGFX::VIEW* aView,
  655. KIGFX::VIEW_CONTROLS* aViewControls, EDA_DRAW_FRAME* aFrame )
  656. {
  657. m_model = aModel;
  658. m_view = aView;
  659. m_viewControls = aViewControls;
  660. m_editFrame = aFrame;
  661. m_actionMgr->UpdateHotKeys();
  662. }
  663. bool TOOL_MANAGER::isActive( TOOL_BASE* aTool )
  664. {
  665. if( !isRegistered( aTool ) )
  666. return false;
  667. // Just check if the tool is on the active tools stack
  668. return std::find( m_activeTools.begin(), m_activeTools.end(), aTool->GetId() ) != m_activeTools.end();
  669. }
  670. void TOOL_MANAGER::saveViewControls( TOOL_STATE* aState )
  671. {
  672. aState->vcSettings = m_viewControls->GetSettings();
  673. if( m_menuActive )
  674. {
  675. // Context menu is active, so the cursor settings are overridden (see dispatchContextMenu())
  676. auto it = m_cursorSettings.find( aState->theTool->GetId() );
  677. if( it != m_cursorSettings.end() )
  678. {
  679. const KIGFX::VC_SETTINGS& curr = m_viewControls->GetSettings();
  680. // Tool has overridden the cursor position, so store the new settings
  681. if( !curr.m_forceCursorPosition || curr.m_forcedPosition != m_menuCursor )
  682. {
  683. if( !curr.m_forceCursorPosition )
  684. it->second = NULLOPT;
  685. else
  686. it->second = curr.m_forcedPosition;
  687. }
  688. else
  689. {
  690. OPT<VECTOR2D> cursor = it->second;
  691. if( cursor )
  692. {
  693. aState->vcSettings.m_forceCursorPosition = true;
  694. aState->vcSettings.m_forcedPosition = *cursor;
  695. }
  696. else
  697. {
  698. aState->vcSettings.m_forceCursorPosition = false;
  699. }
  700. }
  701. }
  702. }
  703. }
  704. void TOOL_MANAGER::applyViewControls( TOOL_STATE* aState )
  705. {
  706. m_viewControls->ApplySettings( aState->vcSettings );
  707. }
  708. bool TOOL_MANAGER::processEvent( const TOOL_EVENT& aEvent )
  709. {
  710. // Early dispatch of events destined for the TOOL_MANAGER
  711. if( !dispatchStandardEvents( aEvent ) )
  712. return true;
  713. dispatchInternal( aEvent );
  714. dispatchActivation( aEvent );
  715. dispatchContextMenu( aEvent );
  716. // Dispatch queue
  717. while( !m_eventQueue.empty() )
  718. {
  719. TOOL_EVENT event = m_eventQueue.front();
  720. m_eventQueue.pop_front();
  721. processEvent( event );
  722. }
  723. return false;
  724. }
  725. void TOOL_MANAGER::setActiveState( TOOL_STATE* aState )
  726. {
  727. if( m_activeState )
  728. saveViewControls( m_activeState );
  729. m_activeState = aState;
  730. if( m_activeState )
  731. applyViewControls( aState );
  732. }
  733. bool TOOL_MANAGER::IsToolActive( TOOL_ID aId ) const
  734. {
  735. auto it = m_toolIdIndex.find( aId );
  736. return !it->second->idle;
  737. }
  738. void TOOL_MANAGER::UpdateUI()
  739. {
  740. EDA_DRAW_FRAME* frame = GetEditFrame();
  741. if( frame )
  742. {
  743. frame->UpdateStatusBar();
  744. frame->SyncMenusAndToolbars();
  745. }
  746. }