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.

1392 lines
43 KiB

7 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 CERN
  5. * Copyright (C) 2014-2019 KiCad Developers, see AUTHORS.txt for contributors.
  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 <cstdint>
  26. #include <thread>
  27. #include <mutex>
  28. #include <functional>
  29. #include "pcb_editor_control.h"
  30. #include "pcb_actions.h"
  31. #include <tool/tool_manager.h>
  32. #include <tools/tool_event_utils.h>
  33. #include <wx/progdlg.h>
  34. #include <ws_proxy_undo_item.h>
  35. #include "edit_tool.h"
  36. #include "selection_tool.h"
  37. #include "drawing_tool.h"
  38. #include "pcbnew_picker_tool.h"
  39. #include <painter.h>
  40. #include <project.h>
  41. #include <pcbnew_id.h>
  42. #include <pcb_edit_frame.h>
  43. #include <class_board.h>
  44. #include <class_zone.h>
  45. #include <pcb_draw_panel_gal.h>
  46. #include <class_module.h>
  47. #include <class_pcb_target.h>
  48. #include <connectivity/connectivity_data.h>
  49. #include <collectors.h>
  50. #include <zones_functions_for_undo_redo.h>
  51. #include <board_commit.h>
  52. #include <confirm.h>
  53. #include <bitmaps.h>
  54. #include <hotkeys.h>
  55. #include <view/view_group.h>
  56. #include <view/view_controls.h>
  57. #include <origin_viewitem.h>
  58. #include <profile.h>
  59. #include <widgets/progress_reporter.h>
  60. #include <dialogs/dialog_find.h>
  61. #include <dialogs/dialog_page_settings.h>
  62. using namespace std::placeholders;
  63. // Track & via size control
  64. TOOL_ACTION PCB_ACTIONS::trackWidthInc( "pcbnew.EditorControl.trackWidthInc",
  65. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_NEXT ),
  66. "", "" );
  67. TOOL_ACTION PCB_ACTIONS::trackWidthDec( "pcbnew.EditorControl.trackWidthDec",
  68. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_WIDTH_TO_PREVIOUS ),
  69. "", "" );
  70. TOOL_ACTION PCB_ACTIONS::viaSizeInc( "pcbnew.EditorControl.viaSizeInc",
  71. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_VIA_SIZE_INC ),
  72. "", "" );
  73. TOOL_ACTION PCB_ACTIONS::viaSizeDec( "pcbnew.EditorControl.viaSizeDec",
  74. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_VIA_SIZE_DEC ),
  75. "", "" );
  76. TOOL_ACTION PCB_ACTIONS::trackViaSizeChanged( "pcbnew.EditorControl.trackViaSizeChanged",
  77. AS_GLOBAL, 0,
  78. "", "",
  79. nullptr, AF_NOTIFY );
  80. TOOL_ACTION PCB_ACTIONS::zoneMerge( "pcbnew.EditorControl.zoneMerge",
  81. AS_GLOBAL, 0,
  82. _( "Merge Zones" ), _( "Merge zones" ) );
  83. TOOL_ACTION PCB_ACTIONS::zoneDuplicate( "pcbnew.EditorControl.zoneDuplicate",
  84. AS_GLOBAL, 0,
  85. _( "Duplicate Zone onto Layer..." ), _( "Duplicate zone outline onto a different layer" ),
  86. zone_duplicate_xpm );
  87. TOOL_ACTION PCB_ACTIONS::placeTarget( "pcbnew.EditorControl.placeTarget",
  88. AS_GLOBAL, 0,
  89. _( "Add Layer Alignment Target" ), _( "Add a layer alignment target" ),
  90. add_pcb_target_xpm, AF_ACTIVATE );
  91. TOOL_ACTION PCB_ACTIONS::placeModule( "pcbnew.EditorControl.placeModule",
  92. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_ADD_MODULE ),
  93. _( "Add Footprint" ), _( "Add a footprint" ),
  94. module_xpm, AF_ACTIVATE );
  95. TOOL_ACTION PCB_ACTIONS::drillOrigin( "pcbnew.EditorControl.drillOrigin",
  96. AS_GLOBAL, 0,
  97. _( "Drill and Place Offset" ), _( "Place origin point for drill and place files" ),
  98. pcb_offset_xpm );
  99. TOOL_ACTION PCB_ACTIONS::crossProbeSchToPcb( "pcbnew.EditorControl.crossProbSchToPcb",
  100. AS_GLOBAL, 0,
  101. "", "" );
  102. TOOL_ACTION PCB_ACTIONS::toggleLock( "pcbnew.EditorControl.toggleLock",
  103. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_LOCK_UNLOCK_FOOTPRINT ),
  104. "Toggle Lock", "",
  105. lock_unlock_xpm );
  106. TOOL_ACTION PCB_ACTIONS::lock( "pcbnew.EditorControl.lock",
  107. AS_GLOBAL, 0,
  108. _( "Lock" ), "",
  109. locked_xpm );
  110. TOOL_ACTION PCB_ACTIONS::unlock( "pcbnew.EditorControl.unlock",
  111. AS_GLOBAL, 0,
  112. _( "Unlock" ), "",
  113. unlocked_xpm );
  114. TOOL_ACTION PCB_ACTIONS::appendBoard( "pcbnew.EditorControl.appendBoard",
  115. AS_GLOBAL, 0,
  116. "", "" );
  117. TOOL_ACTION PCB_ACTIONS::highlightNet( "pcbnew.EditorControl.highlightNet",
  118. AS_GLOBAL, 0,
  119. "", "" );
  120. TOOL_ACTION PCB_ACTIONS::clearHighlight( "pcbnew.EditorControl.clearHighlight",
  121. AS_GLOBAL, 0,
  122. "", "" );
  123. TOOL_ACTION PCB_ACTIONS::highlightNetTool( "pcbnew.EditorControl.highlightNetTool",
  124. AS_GLOBAL, 0,
  125. _( "Highlight Nets" ), _( "Highlight all copper items of a net" ),
  126. net_highlight_xpm );
  127. TOOL_ACTION PCB_ACTIONS::highlightNetSelection( "pcbnew.EditorControl.highlightNetSelection",
  128. AS_GLOBAL, TOOL_ACTION::LegacyHotKey( HK_HIGHLIGHT_NET_SELECTION ),
  129. _( "Highlight Net" ), _( "Highlight all copper items of a net" ),
  130. net_highlight_xpm );
  131. TOOL_ACTION PCB_ACTIONS::localRatsnestTool( "pcbnew.Control.localRatsnestTool",
  132. AS_GLOBAL, 0,
  133. _( "Highlight Ratsnest" ), "",
  134. tool_ratsnest_xpm );
  135. TOOL_ACTION PCB_ACTIONS::hideLocalRatsnest( "pcbnew.Control.hideLocalRatsnest",
  136. AS_GLOBAL, 0,
  137. "", "" );
  138. TOOL_ACTION PCB_ACTIONS::updateLocalRatsnest( "pcbnew.Control.updateLocalRatsnest",
  139. AS_GLOBAL, 0,
  140. "", "" );
  141. class ZONE_CONTEXT_MENU : public ACTION_MENU
  142. {
  143. public:
  144. ZONE_CONTEXT_MENU()
  145. {
  146. SetIcon( add_zone_xpm );
  147. SetTitle( _( "Zones" ) );
  148. Add( PCB_ACTIONS::zoneFill );
  149. Add( PCB_ACTIONS::zoneFillAll );
  150. Add( PCB_ACTIONS::zoneUnfill );
  151. Add( PCB_ACTIONS::zoneUnfillAll );
  152. AppendSeparator();
  153. Add( PCB_ACTIONS::zoneMerge );
  154. Add( PCB_ACTIONS::zoneDuplicate );
  155. Add( PCB_ACTIONS::drawZoneCutout );
  156. Add( PCB_ACTIONS::drawSimilarZone );
  157. }
  158. protected:
  159. ACTION_MENU* create() const override
  160. {
  161. return new ZONE_CONTEXT_MENU();
  162. }
  163. private:
  164. void update() override
  165. {
  166. SELECTION_TOOL* selTool = getToolManager()->GetTool<SELECTION_TOOL>();
  167. // enable zone actions that act on a single zone
  168. bool singleZoneActionsEnabled = ( SELECTION_CONDITIONS::Count( 1 )
  169. && SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T )
  170. )( selTool->GetSelection() );
  171. Enable( getMenuId( PCB_ACTIONS::zoneDuplicate ), singleZoneActionsEnabled );
  172. Enable( getMenuId( PCB_ACTIONS::drawZoneCutout ), singleZoneActionsEnabled );
  173. Enable( getMenuId( PCB_ACTIONS::drawSimilarZone ), singleZoneActionsEnabled );
  174. // enable zone actions that ably to a specific set of zones (as opposed to all of them)
  175. bool nonGlobalActionsEnabled = ( SELECTION_CONDITIONS::MoreThan( 0 ) )( selTool->GetSelection() );
  176. Enable( getMenuId( PCB_ACTIONS::zoneFill ), nonGlobalActionsEnabled );
  177. Enable( getMenuId( PCB_ACTIONS::zoneUnfill ), nonGlobalActionsEnabled );
  178. // lines like this make me really think about a better name for SELECTION_CONDITIONS class
  179. bool mergeEnabled = ( SELECTION_CONDITIONS::MoreThan( 1 ) &&
  180. /*SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T ) &&*/
  181. PCB_SELECTION_CONDITIONS::SameNet( true ) &&
  182. PCB_SELECTION_CONDITIONS::SameLayer() )( selTool->GetSelection() );
  183. Enable( getMenuId( PCB_ACTIONS::zoneMerge ), mergeEnabled );
  184. }
  185. };
  186. class LOCK_CONTEXT_MENU : public ACTION_MENU
  187. {
  188. public:
  189. LOCK_CONTEXT_MENU()
  190. {
  191. SetIcon( locked_xpm );
  192. SetTitle( _( "Locking" ) );
  193. AppendSeparator();
  194. Add( PCB_ACTIONS::lock );
  195. Add( PCB_ACTIONS::unlock );
  196. Add( PCB_ACTIONS::toggleLock );
  197. }
  198. ACTION_MENU* create() const override
  199. {
  200. return new LOCK_CONTEXT_MENU();
  201. }
  202. };
  203. PCB_EDITOR_CONTROL::PCB_EDITOR_CONTROL() :
  204. PCB_TOOL_BASE( "pcbnew.EditorControl" ),
  205. m_frame( nullptr ),
  206. m_menu( *this )
  207. {
  208. m_placeOrigin.reset( new KIGFX::ORIGIN_VIEWITEM( KIGFX::COLOR4D( 0.8, 0.0, 0.0, 1.0 ),
  209. KIGFX::ORIGIN_VIEWITEM::CIRCLE_CROSS ) );
  210. m_probingSchToPcb = false;
  211. m_slowRatsnest = false;
  212. }
  213. PCB_EDITOR_CONTROL::~PCB_EDITOR_CONTROL()
  214. {
  215. }
  216. void PCB_EDITOR_CONTROL::Reset( RESET_REASON aReason )
  217. {
  218. m_frame = getEditFrame<PCB_EDIT_FRAME>();
  219. if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
  220. {
  221. m_placeOrigin->SetPosition( getModel<BOARD>()->GetAuxOrigin() );
  222. getView()->Remove( m_placeOrigin.get() );
  223. getView()->Add( m_placeOrigin.get() );
  224. }
  225. }
  226. bool PCB_EDITOR_CONTROL::Init()
  227. {
  228. auto activeToolCondition = [ this ] ( const SELECTION& aSel ) {
  229. return ( m_frame->GetToolId() != ID_NO_TOOL_SELECTED );
  230. };
  231. auto inactiveStateCondition = [ this ] ( const SELECTION& aSel ) {
  232. return ( m_frame->GetToolId() == ID_NO_TOOL_SELECTED && aSel.Size() == 0 );
  233. };
  234. auto placeModuleCondition = [ this ] ( const SELECTION& aSel ) {
  235. return ( m_frame->GetToolId() == ID_PCB_MODULE_BUTT && aSel.GetSize() == 0 );
  236. };
  237. auto& ctxMenu = m_menu.GetMenu();
  238. // "Cancel" goes at the top of the context menu when a tool is active
  239. ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolCondition, 1000 );
  240. ctxMenu.AddSeparator( activeToolCondition, 1000 );
  241. // "Get and Place Footprint" should be available for Place Footprint tool
  242. ctxMenu.AddItem( PCB_ACTIONS::findMove, placeModuleCondition, 1000 );
  243. ctxMenu.AddSeparator( placeModuleCondition, 1000 );
  244. // Finally, add the standard zoom & grid items
  245. m_menu.AddStandardSubMenus( getEditFrame<PCB_BASE_FRAME>() );
  246. auto zoneMenu = std::make_shared<ZONE_CONTEXT_MENU>();
  247. zoneMenu->SetTool( this );
  248. auto lockMenu = std::make_shared<LOCK_CONTEXT_MENU>();
  249. lockMenu->SetTool( this );
  250. // Add the PCB control menus to relevant other tools
  251. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  252. if( selTool )
  253. {
  254. auto& toolMenu = selTool->GetToolMenu();
  255. auto& menu = toolMenu.GetMenu();
  256. // Add "Get and Place Footprint" when Selection tool is in an inactive state
  257. menu.AddItem( PCB_ACTIONS::findMove, inactiveStateCondition );
  258. menu.AddSeparator( inactiveStateCondition );
  259. toolMenu.AddSubMenu( zoneMenu );
  260. toolMenu.AddSubMenu( lockMenu );
  261. menu.AddMenu( zoneMenu.get(), SELECTION_CONDITIONS::OnlyType( PCB_ZONE_AREA_T ), 200 );
  262. menu.AddMenu( lockMenu.get(), SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::LockableItems ), 200 );
  263. }
  264. DRAWING_TOOL* drawingTool = m_toolMgr->GetTool<DRAWING_TOOL>();
  265. if( drawingTool )
  266. {
  267. auto& toolMenu = drawingTool->GetToolMenu();
  268. auto& menu = toolMenu.GetMenu();
  269. toolMenu.AddSubMenu( zoneMenu );
  270. // Functor to say if the PCB_EDIT_FRAME is in a given mode
  271. // Capture the tool pointer and tool mode by value
  272. auto toolActiveFunctor = [=]( DRAWING_TOOL::MODE aMode )
  273. {
  274. return [=]( const SELECTION& sel )
  275. {
  276. return drawingTool->GetDrawingMode() == aMode;
  277. };
  278. };
  279. menu.AddMenu( zoneMenu.get(), toolActiveFunctor( DRAWING_TOOL::MODE::ZONE ), 200 );
  280. }
  281. m_ratsnestTimer.SetOwner( this );
  282. Connect( m_ratsnestTimer.GetId(), wxEVT_TIMER,
  283. wxTimerEventHandler( PCB_EDITOR_CONTROL::ratsnestTimer ), NULL, this );
  284. return true;
  285. }
  286. int PCB_EDITOR_CONTROL::New( const TOOL_EVENT& aEvent )
  287. {
  288. m_frame->Files_io_from_id( ID_NEW_BOARD );
  289. return 0;
  290. }
  291. int PCB_EDITOR_CONTROL::Open( const TOOL_EVENT& aEvent )
  292. {
  293. m_frame->Files_io_from_id( ID_LOAD_FILE );
  294. return 0;
  295. }
  296. int PCB_EDITOR_CONTROL::Save( const TOOL_EVENT& aEvent )
  297. {
  298. m_frame->Files_io_from_id( ID_SAVE_BOARD );
  299. return 0;
  300. }
  301. int PCB_EDITOR_CONTROL::SaveAs( const TOOL_EVENT& aEvent )
  302. {
  303. m_frame->Files_io_from_id( ID_SAVE_BOARD_AS );
  304. return 0;
  305. }
  306. int PCB_EDITOR_CONTROL::SaveCopyAs( const TOOL_EVENT& aEvent )
  307. {
  308. m_frame->Files_io_from_id( ID_COPY_BOARD_AS );
  309. return 0;
  310. }
  311. int PCB_EDITOR_CONTROL::PageSettings( const TOOL_EVENT& aEvent )
  312. {
  313. PICKED_ITEMS_LIST undoCmd;
  314. WS_PROXY_UNDO_ITEM* undoItem = new WS_PROXY_UNDO_ITEM( m_frame );
  315. ITEM_PICKER wrapper( undoItem, UR_PAGESETTINGS );
  316. undoCmd.PushItem( wrapper );
  317. m_frame->SaveCopyInUndoList( undoCmd, UR_PAGESETTINGS );
  318. DIALOG_PAGES_SETTINGS dlg( m_frame, wxSize( MAX_PAGE_SIZE_PCBNEW_MILS,
  319. MAX_PAGE_SIZE_PCBNEW_MILS ) );
  320. dlg.SetWksFileName( BASE_SCREEN::m_PageLayoutDescrFileName );
  321. if( dlg.ShowModal() == wxID_OK )
  322. m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
  323. else
  324. m_frame->RollbackFromUndo();
  325. return 0;
  326. }
  327. int PCB_EDITOR_CONTROL::Plot( const TOOL_EVENT& aEvent )
  328. {
  329. wxCommandEvent evt( wxEVT_NULL, ID_GEN_PLOT );
  330. m_frame->ToPlotter( evt );
  331. return 0;
  332. }
  333. int PCB_EDITOR_CONTROL::Find( const TOOL_EVENT& aEvent )
  334. {
  335. DIALOG_FIND dlg( m_frame );
  336. dlg.ShowModal();
  337. return 0;
  338. }
  339. // Track & via size control
  340. int PCB_EDITOR_CONTROL::TrackWidthInc( const TOOL_EVENT& aEvent )
  341. {
  342. BOARD* board = getModel<BOARD>();
  343. int widthIndex = board->GetDesignSettings().GetTrackWidthIndex() + 1;
  344. if( widthIndex >= (int) board->GetDesignSettings().m_TrackWidthList.size() )
  345. widthIndex = board->GetDesignSettings().m_TrackWidthList.size() - 1;
  346. board->GetDesignSettings().SetTrackWidthIndex( widthIndex );
  347. board->GetDesignSettings().UseCustomTrackViaSize( false );
  348. m_toolMgr->RunAction( PCB_ACTIONS::trackViaSizeChanged );
  349. return 0;
  350. }
  351. int PCB_EDITOR_CONTROL::TrackWidthDec( const TOOL_EVENT& aEvent )
  352. {
  353. BOARD* board = getModel<BOARD>();
  354. int widthIndex = board->GetDesignSettings().GetTrackWidthIndex() - 1;
  355. if( widthIndex < 0 )
  356. widthIndex = 0;
  357. board->GetDesignSettings().SetTrackWidthIndex( widthIndex );
  358. board->GetDesignSettings().UseCustomTrackViaSize( false );
  359. m_toolMgr->RunAction( PCB_ACTIONS::trackViaSizeChanged );
  360. return 0;
  361. }
  362. int PCB_EDITOR_CONTROL::ViaSizeInc( const TOOL_EVENT& aEvent )
  363. {
  364. BOARD* board = getModel<BOARD>();
  365. int sizeIndex = board->GetDesignSettings().GetViaSizeIndex() + 1;
  366. if( sizeIndex >= (int) board->GetDesignSettings().m_ViasDimensionsList.size() )
  367. sizeIndex = board->GetDesignSettings().m_ViasDimensionsList.size() - 1;
  368. board->GetDesignSettings().SetViaSizeIndex( sizeIndex );
  369. board->GetDesignSettings().UseCustomTrackViaSize( false );
  370. m_toolMgr->RunAction( PCB_ACTIONS::trackViaSizeChanged );
  371. return 0;
  372. }
  373. int PCB_EDITOR_CONTROL::ViaSizeDec( const TOOL_EVENT& aEvent )
  374. {
  375. BOARD* board = getModel<BOARD>();
  376. int sizeIndex = board->GetDesignSettings().GetViaSizeIndex() - 1;
  377. if( sizeIndex < 0 )
  378. sizeIndex = 0;
  379. board->GetDesignSettings().SetViaSizeIndex( sizeIndex );
  380. board->GetDesignSettings().UseCustomTrackViaSize( false );
  381. m_toolMgr->RunAction( PCB_ACTIONS::trackViaSizeChanged );
  382. return 0;
  383. }
  384. int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent )
  385. {
  386. MODULE* module = aEvent.Parameter<MODULE*>();
  387. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  388. BOARD_COMMIT commit( m_frame );
  389. BOARD* board = getModel<BOARD>();
  390. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  391. controls->ShowCursor( true );
  392. controls->SetSnapping( true );
  393. Activate();
  394. m_frame->SetToolID( ID_PCB_MODULE_BUTT, wxCURSOR_PENCIL, _( "Add footprint" ) );
  395. // Add all the drawable parts to preview
  396. VECTOR2I cursorPos = controls->GetCursorPosition();
  397. if( module )
  398. {
  399. module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  400. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, module );
  401. }
  402. bool reselect = false;
  403. // Main loop: keep receiving events
  404. while( OPT_TOOL_EVENT evt = Wait() )
  405. {
  406. cursorPos = controls->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  407. if( reselect && module )
  408. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, module );
  409. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  410. {
  411. if( module )
  412. {
  413. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  414. commit.Revert();
  415. module = NULL;
  416. }
  417. else // let's have another chance placing a module
  418. break;
  419. if( evt->IsActivate() ) // now finish unconditionally
  420. break;
  421. }
  422. else if( evt->IsClick( BUT_LEFT ) )
  423. {
  424. if( !module )
  425. {
  426. // Pick the module to be placed
  427. module = m_frame->SelectFootprintFromLibTree();
  428. if( module == NULL )
  429. continue;
  430. module->SetLink( 0 );
  431. module->SetFlags( IS_NEW ); // whatever
  432. module->SetTimeStamp( GetNewTimeStamp() );
  433. // Set parent so that clearance can be loaded
  434. module->SetParent( board );
  435. // Put it on FRONT layer,
  436. // (Can be stored flipped if the lib is an archive built from a board)
  437. if( module->IsFlipped() )
  438. module->Flip( module->GetPosition() );
  439. module->SetOrientation( 0 );
  440. module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  441. commit.Add( module );
  442. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, module );
  443. controls->SetCursorPosition( cursorPos, false );
  444. }
  445. else
  446. {
  447. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  448. commit.Push( _( "Place a module" ) );
  449. module = NULL; // to indicate that there is no module that we currently modify
  450. }
  451. }
  452. else if( evt->IsClick( BUT_RIGHT ) )
  453. {
  454. m_menu.ShowContextMenu( selection() );
  455. }
  456. else if( module && evt->IsMotion() )
  457. {
  458. module->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  459. selection().SetReferencePoint( cursorPos );
  460. getView()->Update( & selection() );
  461. }
  462. else if( module && evt->IsAction( &PCB_ACTIONS::properties ) )
  463. {
  464. // Calling 'Properties' action clears the selection, so we need to restore it
  465. reselect = true;
  466. }
  467. // Enable autopanning and cursor capture only when there is a module to be placed
  468. controls->SetAutoPan( !!module );
  469. controls->CaptureCursor( !!module );
  470. }
  471. m_frame->SetNoToolSelected();
  472. return 0;
  473. }
  474. int PCB_EDITOR_CONTROL::ToggleLockSelected( const TOOL_EVENT& aEvent )
  475. {
  476. return modifyLockSelected( TOGGLE );
  477. }
  478. int PCB_EDITOR_CONTROL::LockSelected( const TOOL_EVENT& aEvent )
  479. {
  480. return modifyLockSelected( ON );
  481. }
  482. int PCB_EDITOR_CONTROL::UnlockSelected( const TOOL_EVENT& aEvent )
  483. {
  484. return modifyLockSelected( OFF );
  485. }
  486. int PCB_EDITOR_CONTROL::modifyLockSelected( MODIFY_MODE aMode )
  487. {
  488. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  489. const SELECTION& selection = selTool->GetSelection();
  490. if( selection.Empty() )
  491. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
  492. bool modified = false;
  493. for( auto i : selection )
  494. {
  495. auto item = static_cast<BOARD_ITEM*>( i );
  496. bool prevState = item->IsLocked();
  497. switch( aMode )
  498. {
  499. case ON:
  500. item->SetLocked( true );
  501. break;
  502. case OFF:
  503. item->SetLocked( false );
  504. break;
  505. case TOGGLE:
  506. item->SetLocked( !prevState );
  507. break;
  508. }
  509. // Check if we really modified an item
  510. if( !modified && prevState != item->IsLocked() )
  511. modified = true;
  512. }
  513. if( modified )
  514. m_frame->OnModify();
  515. return 0;
  516. }
  517. int PCB_EDITOR_CONTROL::PlaceTarget( const TOOL_EVENT& aEvent )
  518. {
  519. KIGFX::VIEW* view = getView();
  520. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  521. BOARD* board = getModel<BOARD>();
  522. PCB_TARGET* target = new PCB_TARGET( board );
  523. // Init the new item attributes
  524. target->SetLayer( Edge_Cuts );
  525. target->SetWidth( board->GetDesignSettings().GetLineThickness( Edge_Cuts ) );
  526. target->SetSize( Millimeter2iu( 5 ) );
  527. VECTOR2I cursorPos = controls->GetCursorPosition();
  528. target->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  529. // Add a VIEW_GROUP that serves as a preview for the new item
  530. KIGFX::VIEW_GROUP preview( view );
  531. preview.Add( target );
  532. view->Add( &preview );
  533. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  534. controls->SetSnapping( true );
  535. Activate();
  536. m_frame->SetToolID( ID_PCB_TARGET_BUTT, wxCURSOR_PENCIL, _( "Add layer alignment target" ) );
  537. // Main loop: keep receiving events
  538. while( OPT_TOOL_EVENT evt = Wait() )
  539. {
  540. cursorPos = controls->GetCursorPosition( !evt->Modifier( MD_ALT ) );
  541. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  542. break;
  543. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  544. {
  545. target->SetWidth( target->GetWidth() + WIDTH_STEP );
  546. view->Update( &preview );
  547. }
  548. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
  549. {
  550. int width = target->GetWidth();
  551. if( width > WIDTH_STEP )
  552. {
  553. target->SetWidth( width - WIDTH_STEP );
  554. view->Update( &preview );
  555. }
  556. }
  557. else if( evt->IsClick( BUT_LEFT ) )
  558. {
  559. assert( target->GetSize() > 0 );
  560. assert( target->GetWidth() > 0 );
  561. BOARD_COMMIT commit( m_frame );
  562. commit.Add( target );
  563. commit.Push( _( "Place a layer alignment target" ) );
  564. preview.Remove( target );
  565. // Create next PCB_TARGET
  566. target = new PCB_TARGET( *target );
  567. preview.Add( target );
  568. }
  569. else if( evt->IsClick( BUT_RIGHT ) )
  570. {
  571. m_menu.ShowContextMenu( selection() );
  572. }
  573. else if( evt->IsMotion() )
  574. {
  575. target->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
  576. view->Update( &preview );
  577. }
  578. }
  579. delete target;
  580. controls->SetSnapping( false );
  581. view->Remove( &preview );
  582. m_frame->SetNoToolSelected();
  583. return 0;
  584. }
  585. static bool mergeZones( BOARD_COMMIT& aCommit, std::vector<ZONE_CONTAINER *>& aOriginZones,
  586. std::vector<ZONE_CONTAINER *>& aMergedZones )
  587. {
  588. for( unsigned int i = 1; i < aOriginZones.size(); i++ )
  589. {
  590. aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline(),
  591. SHAPE_POLY_SET::PM_FAST );
  592. }
  593. aOriginZones[0]->Outline()->Simplify( SHAPE_POLY_SET::PM_FAST );
  594. // We should have one polygon with hole
  595. // We can have 2 polygons with hole, if the 2 initial polygons have only one common corner
  596. // and therefore cannot be merged (they are dectected as intersecting)
  597. // but we should never have more than 2 polys
  598. if( aOriginZones[0]->Outline()->OutlineCount() > 1 )
  599. {
  600. wxLogMessage( "BOARD::CombineAreas error: more than 2 polys after merging" );
  601. return false;
  602. }
  603. for( unsigned int i = 1; i < aOriginZones.size(); i++ )
  604. {
  605. aCommit.Remove( aOriginZones[i] );
  606. }
  607. aCommit.Modify( aOriginZones[0] );
  608. aMergedZones.push_back( aOriginZones[0] );
  609. aOriginZones[0]->SetLocalFlags( 1 );
  610. aOriginZones[0]->Hatch();
  611. aOriginZones[0]->CacheTriangulation();
  612. return true;
  613. }
  614. int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent )
  615. {
  616. const SELECTION& selection = m_toolMgr->GetTool<SELECTION_TOOL>()->GetSelection();
  617. BOARD* board = getModel<BOARD>();
  618. BOARD_COMMIT commit( m_frame );
  619. if( selection.Size() < 2 )
  620. return 0;
  621. int netcode = -1;
  622. ZONE_CONTAINER* firstZone = nullptr;
  623. std::vector<ZONE_CONTAINER*> toMerge, merged;
  624. for( auto item : selection )
  625. {
  626. auto curr_area = dynamic_cast<ZONE_CONTAINER*>( item );
  627. if( !curr_area )
  628. continue;
  629. if( !firstZone )
  630. firstZone = curr_area;
  631. netcode = curr_area->GetNetCode();
  632. if( firstZone->GetNetCode() != netcode )
  633. continue;
  634. if( curr_area->GetPriority() != firstZone->GetPriority() )
  635. continue;
  636. if( curr_area->GetIsKeepout() != firstZone->GetIsKeepout() )
  637. continue;
  638. if( curr_area->GetLayer() != firstZone->GetLayer() )
  639. continue;
  640. if( !board->TestAreaIntersection( curr_area, firstZone ) )
  641. continue;
  642. toMerge.push_back( curr_area );
  643. }
  644. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  645. if( mergeZones( commit, toMerge, merged ) )
  646. {
  647. commit.Push( _( "Merge zones" ) );
  648. for( auto item : merged )
  649. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, item );
  650. }
  651. return 0;
  652. }
  653. int PCB_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent )
  654. {
  655. auto selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  656. const auto& selection = selTool->GetSelection();
  657. // because this pops up the zone editor, it would be confusing to handle multiple zones,
  658. // so just handle single selections containing exactly one zone
  659. if( selection.Size() != 1 )
  660. return 0;
  661. auto oldZone = dyn_cast<ZONE_CONTAINER*>( selection[0] );
  662. if( !oldZone )
  663. return 0;
  664. ZONE_SETTINGS zoneSettings;
  665. zoneSettings << *oldZone;
  666. int dialogResult;
  667. if( oldZone->GetIsKeepout() )
  668. dialogResult = InvokeKeepoutAreaEditor( m_frame, &zoneSettings );
  669. else if( oldZone->IsOnCopperLayer() )
  670. dialogResult = InvokeCopperZonesEditor( m_frame, &zoneSettings );
  671. else
  672. dialogResult = InvokeNonCopperZonesEditor( m_frame, &zoneSettings );
  673. if( dialogResult != wxID_OK )
  674. return 0;
  675. // duplicate the zone
  676. BOARD_COMMIT commit( m_frame );
  677. auto newZone = std::make_unique<ZONE_CONTAINER>( *oldZone );
  678. newZone->ClearSelected();
  679. newZone->UnFill();
  680. zoneSettings.ExportSetting( *newZone );
  681. // If the new zone is on the same layer(s) as the the initial zone,
  682. // offset it a bit so it can more easily be picked.
  683. if( oldZone->GetIsKeepout() && ( oldZone->GetLayerSet() == zoneSettings.m_Layers ) )
  684. newZone->Move( wxPoint( IU_PER_MM, IU_PER_MM ) );
  685. else if( !oldZone->GetIsKeepout() && ( oldZone->GetLayer() == zoneSettings.m_CurrentZone_Layer ) )
  686. newZone->Move( wxPoint( IU_PER_MM, IU_PER_MM ) );
  687. commit.Add( newZone.release() );
  688. commit.Push( _( "Duplicate zone" ) );
  689. return 0;
  690. }
  691. int PCB_EDITOR_CONTROL::CrossProbePcbToSch( const TOOL_EVENT& aEvent )
  692. {
  693. // Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ...
  694. if( m_probingSchToPcb )
  695. {
  696. m_probingSchToPcb = false;
  697. return 0;
  698. }
  699. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  700. const SELECTION& selection = selTool->GetSelection();
  701. if( selection.Size() == 1 )
  702. m_frame->SendMessageToEESCHEMA( static_cast<BOARD_ITEM*>( selection.Front() ) );
  703. else
  704. m_frame->SendMessageToEESCHEMA( nullptr );
  705. return 0;
  706. }
  707. int PCB_EDITOR_CONTROL::CrossProbeSchToPcb( const TOOL_EVENT& aEvent )
  708. {
  709. BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
  710. if( item )
  711. {
  712. m_probingSchToPcb = true;
  713. getView()->SetCenter( VECTOR2D( item->GetPosition() ) );
  714. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  715. // If it is a pad and the net highlighting tool is enabled, highlight the net
  716. if( item->Type() == PCB_PAD_T && m_frame->GetToolId() == ID_PCB_HIGHLIGHT_BUTT )
  717. {
  718. int net = static_cast<D_PAD*>( item )->GetNetCode();
  719. m_toolMgr->RunAction( PCB_ACTIONS::highlightNet, false, net );
  720. }
  721. else
  722. // Otherwise simply select the corresponding item
  723. {
  724. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, item );
  725. // Ensure the display is refreshed, because in some installs
  726. // the refresh is done only when the gal canvas has the focus, and
  727. // that is not the case when crossprobing from Eeschema:
  728. m_frame->GetGalCanvas()->Refresh();
  729. }
  730. }
  731. return 0;
  732. }
  733. bool PCB_EDITOR_CONTROL::DoSetDrillOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
  734. BOARD_ITEM* originViewItem, const VECTOR2D& aPosition )
  735. {
  736. aFrame->SetAuxOrigin( wxPoint( aPosition.x, aPosition.y ) );
  737. originViewItem->SetPosition( wxPoint( aPosition.x, aPosition.y ) );
  738. aView->MarkDirty();
  739. aFrame->OnModify();
  740. return true;
  741. }
  742. bool PCB_EDITOR_CONTROL::SetDrillOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
  743. BOARD_ITEM* originViewItem, const VECTOR2D& aPosition )
  744. {
  745. aFrame->SaveCopyInUndoList( originViewItem, UR_DRILLORIGIN );
  746. return DoSetDrillOrigin( aView, aFrame, originViewItem, aPosition );
  747. }
  748. int PCB_EDITOR_CONTROL::DrillOrigin( const TOOL_EVENT& aEvent )
  749. {
  750. Activate();
  751. PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool<PCBNEW_PICKER_TOOL>();
  752. assert( picker );
  753. m_frame->SetToolID( ID_PCB_PLACE_OFFSET_COORD_BUTT, wxCURSOR_HAND, _( "Adjust zero" ) );
  754. picker->SetClickHandler( std::bind( SetDrillOrigin, getView(), m_frame,
  755. m_placeOrigin.get(), _1 ) );
  756. picker->Activate();
  757. Wait();
  758. return 0;
  759. }
  760. /**
  761. * Look for a BOARD_CONNECTED_ITEM in a given spot and if one is found - it enables
  762. * highlight for its net.
  763. *
  764. * @param aToolMgr is the TOOL_MANAGER currently in use.
  765. * @param aPosition is the point where an item is expected (world coordinates).
  766. * @param aUseSelection is true if we should use the current selection to pick the netcode
  767. */
  768. static bool highlightNet( TOOL_MANAGER* aToolMgr, const VECTOR2D& aPosition,
  769. bool aUseSelection = false )
  770. {
  771. auto render = aToolMgr->GetView()->GetPainter()->GetSettings();
  772. auto frame = static_cast<PCB_EDIT_FRAME*>( aToolMgr->GetEditFrame() );
  773. BOARD* board = static_cast<BOARD*>( aToolMgr->GetModel() );
  774. int net = -1;
  775. bool enableHighlight = false;
  776. if( aUseSelection )
  777. {
  778. auto selectionTool = aToolMgr->GetTool<SELECTION_TOOL>();
  779. const SELECTION& selection = selectionTool->GetSelection();
  780. for( auto item : selection )
  781. {
  782. if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
  783. {
  784. auto ci = static_cast<BOARD_CONNECTED_ITEM*>( item );
  785. int item_net = ci->GetNetCode();
  786. if( net < 0 )
  787. {
  788. net = item_net;
  789. }
  790. else if( net != item_net )
  791. {
  792. // more than one net selected: do nothing
  793. return 0;
  794. }
  795. }
  796. }
  797. enableHighlight = ( net >= 0 && net != render->GetHighlightNetCode() );
  798. }
  799. // If we didn't get a net to highlight from the selection, use the cursor
  800. if( net < 0 )
  801. {
  802. auto guide = frame->GetCollectorsGuide();
  803. GENERAL_COLLECTOR collector;
  804. // Find a connected item for which we are going to highlight a net
  805. collector.Collect( board, GENERAL_COLLECTOR::PadsOrTracks, (wxPoint) aPosition, guide );
  806. if( collector.GetCount() == 0 )
  807. collector.Collect( board, GENERAL_COLLECTOR::Zones, (wxPoint) aPosition, guide );
  808. // Clear the previous highlight
  809. frame->SendMessageToEESCHEMA( nullptr );
  810. for( int i = 0; i < collector.GetCount(); i++ )
  811. {
  812. if( ( collector[i]->GetLayerSet() & LSET::AllCuMask() ).none() )
  813. collector.Remove( i );
  814. if( collector[i]->Type() == PCB_PAD_T )
  815. {
  816. frame->SendMessageToEESCHEMA( static_cast<BOARD_CONNECTED_ITEM*>( collector[i] ) );
  817. break;
  818. }
  819. }
  820. enableHighlight = ( collector.GetCount() > 0 );
  821. // Obtain net code for the clicked item
  822. if( enableHighlight )
  823. net = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] )->GetNetCode();
  824. }
  825. // Toggle highlight when the same net was picked
  826. if( net > 0 && net == render->GetHighlightNetCode() )
  827. enableHighlight = !render->IsHighlightEnabled();
  828. if( enableHighlight != render->IsHighlightEnabled() || net != render->GetHighlightNetCode() )
  829. {
  830. render->SetHighlight( enableHighlight, net );
  831. aToolMgr->GetView()->UpdateAllLayersColor();
  832. }
  833. // Store the highlighted netcode in the current board (for dialogs for instance)
  834. if( enableHighlight && net >= 0 )
  835. {
  836. board->SetHighLightNet( net );
  837. NETINFO_ITEM* netinfo = board->FindNet( net );
  838. if( netinfo )
  839. {
  840. MSG_PANEL_ITEMS items;
  841. netinfo->GetMsgPanelInfo( frame->GetUserUnits(), items );
  842. frame->SetMsgPanel( items );
  843. frame->SendCrossProbeNetName( netinfo->GetNetname() );
  844. }
  845. }
  846. else
  847. {
  848. board->ResetHighLight();
  849. frame->SetMsgPanel( board );
  850. frame->SendCrossProbeNetName( "" );
  851. }
  852. return true;
  853. }
  854. int PCB_EDITOR_CONTROL::HighlightNet( const TOOL_EVENT& aEvent )
  855. {
  856. int netcode = aEvent.Parameter<intptr_t>();
  857. if( netcode > 0 )
  858. {
  859. KIGFX::RENDER_SETTINGS* render = m_toolMgr->GetView()->GetPainter()->GetSettings();
  860. render->SetHighlight( true, netcode );
  861. m_toolMgr->GetView()->UpdateAllLayersColor();
  862. }
  863. else
  864. {
  865. // No net code specified, pick the net code belonging to the item under the cursor
  866. highlightNet( m_toolMgr, getViewControls()->GetMousePosition() );
  867. }
  868. return 0;
  869. }
  870. int PCB_EDITOR_CONTROL::ClearHighlight( const TOOL_EVENT& aEvent )
  871. {
  872. auto frame = static_cast<PCB_EDIT_FRAME*>( m_toolMgr->GetEditFrame() );
  873. auto board = static_cast<BOARD*>( m_toolMgr->GetModel() );
  874. KIGFX::RENDER_SETTINGS* render = m_toolMgr->GetView()->GetPainter()->GetSettings();
  875. board->ResetHighLight();
  876. render->SetHighlight( false );
  877. m_toolMgr->GetView()->UpdateAllLayersColor();
  878. frame->SetMsgPanel( board );
  879. frame->SendCrossProbeNetName( "" );
  880. return 0;
  881. }
  882. int PCB_EDITOR_CONTROL::HighlightNetCursor( const TOOL_EVENT& aEvent )
  883. {
  884. // If the keyboard hotkey was triggered, the behavior is as follows:
  885. // If we are already in the highlight tool, behave the same as a left click.
  886. // If we are not, highlight the net of the selected item(s), or if there is
  887. // no selection, then behave like a Ctrl+Left Click.
  888. if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) )
  889. {
  890. bool use_selection = ( m_frame->GetToolId() != ID_PCB_HIGHLIGHT_BUTT );
  891. highlightNet( m_toolMgr, getViewControls()->GetMousePosition(),
  892. use_selection );
  893. }
  894. Activate();
  895. PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool<PCBNEW_PICKER_TOOL>();
  896. assert( picker );
  897. m_frame->SetToolID( ID_PCB_HIGHLIGHT_BUTT, wxCURSOR_HAND, _( "Highlight net" ) );
  898. picker->SetClickHandler( std::bind( highlightNet, m_toolMgr, _1, false ) );
  899. picker->SetLayerSet( LSET::AllCuMask() );
  900. picker->Activate();
  901. Wait();
  902. return 0;
  903. }
  904. static bool showLocalRatsnest( TOOL_MANAGER* aToolMgr, BOARD* aBoard, const VECTOR2D& aPosition )
  905. {
  906. auto selectionTool = aToolMgr->GetTool<SELECTION_TOOL>();
  907. aToolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  908. aToolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, EDIT_TOOL::PadFilter );
  909. SELECTION& selection = selectionTool->GetSelection();
  910. if( selection.Empty() )
  911. {
  912. aToolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, EDIT_TOOL::FootprintFilter );
  913. selection = selectionTool->GetSelection();
  914. }
  915. if( selection.Empty() )
  916. {
  917. // Clear the previous local ratsnest if we click off all items
  918. for( auto mod : aBoard->Modules() )
  919. {
  920. for( auto pad : mod->Pads() )
  921. pad->SetLocalRatsnestVisible( aBoard->IsElementVisible( LAYER_RATSNEST ) );
  922. }
  923. }
  924. else
  925. {
  926. for( auto item : selection )
  927. {
  928. if( auto pad = dyn_cast<D_PAD*>(item) )
  929. {
  930. pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() );
  931. }
  932. else if( auto mod = dyn_cast<MODULE*>(item) )
  933. {
  934. bool enable = !( *( mod->Pads().begin() ) )->GetLocalRatsnestVisible();
  935. for( auto modpad : mod->Pads() )
  936. {
  937. modpad->SetLocalRatsnestVisible( enable );
  938. }
  939. }
  940. }
  941. }
  942. aToolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
  943. return true;
  944. }
  945. int PCB_EDITOR_CONTROL::LocalRatsnestTool( const TOOL_EVENT& aEvent )
  946. {
  947. Activate();
  948. auto picker = m_toolMgr->GetTool<PCBNEW_PICKER_TOOL>();
  949. auto board = getModel<BOARD>();
  950. wxASSERT( picker );
  951. wxASSERT( board );
  952. m_frame->SetToolID( ID_LOCAL_RATSNEST_BUTT, wxCURSOR_PENCIL,
  953. _( "Pick Components for Local Ratsnest" ) );
  954. picker->SetClickHandler( std::bind( showLocalRatsnest, m_toolMgr, board, _1 ) );
  955. picker->SetFinalizeHandler( [ board ]( int aCondition ){
  956. auto vis = board->IsElementVisible( LAYER_RATSNEST );
  957. if( aCondition != PCBNEW_PICKER_TOOL::END_ACTIVATE )
  958. {
  959. for( auto mod : board->Modules() )
  960. for( auto pad : mod->Pads() )
  961. pad->SetLocalRatsnestVisible( vis );
  962. }
  963. } );
  964. picker->Activate();
  965. Wait();
  966. return 0;
  967. }
  968. int PCB_EDITOR_CONTROL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent )
  969. {
  970. auto selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  971. auto& selection = selectionTool->GetSelection();
  972. auto connectivity = getModel<BOARD>()->GetConnectivity();
  973. if( selection.Empty() )
  974. {
  975. connectivity->ClearDynamicRatsnest();
  976. }
  977. else if( m_slowRatsnest )
  978. {
  979. // Compute ratsnest only when user stops dragging for a moment
  980. connectivity->HideDynamicRatsnest();
  981. m_ratsnestTimer.Start( 20 );
  982. }
  983. else
  984. {
  985. // Check how much time doest it take to calculate ratsnest
  986. PROF_COUNTER counter;
  987. calculateSelectionRatsnest();
  988. counter.Stop();
  989. // If it is too slow, then switch to 'slow ratsnest' mode when
  990. // ratsnest is calculated when user stops dragging items for a moment
  991. if( counter.msecs() > 25 )
  992. {
  993. m_slowRatsnest = true;
  994. connectivity->HideDynamicRatsnest();
  995. }
  996. }
  997. return 0;
  998. }
  999. int PCB_EDITOR_CONTROL::HideSelectionRatsnest( const TOOL_EVENT& aEvent )
  1000. {
  1001. getModel<BOARD>()->GetConnectivity()->ClearDynamicRatsnest();
  1002. m_slowRatsnest = false;
  1003. return 0;
  1004. }
  1005. void PCB_EDITOR_CONTROL::ratsnestTimer( wxTimerEvent& aEvent )
  1006. {
  1007. m_ratsnestTimer.Stop();
  1008. calculateSelectionRatsnest();
  1009. static_cast<PCB_DRAW_PANEL_GAL*>( m_frame->GetGalCanvas() )->RedrawRatsnest();
  1010. m_frame->GetGalCanvas()->Refresh();
  1011. }
  1012. void PCB_EDITOR_CONTROL::calculateSelectionRatsnest()
  1013. {
  1014. auto selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  1015. auto& selection = selectionTool->GetSelection();
  1016. auto connectivity = board()->GetConnectivity();
  1017. std::vector<BOARD_ITEM*> items;
  1018. items.reserve( selection.Size() );
  1019. for( auto item : selection )
  1020. {
  1021. auto board_item = static_cast<BOARD_CONNECTED_ITEM*>( item );
  1022. if( board_item->Type() != PCB_MODULE_T && board_item->GetLocalRatsnestVisible() )
  1023. {
  1024. items.push_back( board_item );
  1025. }
  1026. else if( board_item->Type() == PCB_MODULE_T )
  1027. {
  1028. for( auto pad : static_cast<MODULE*>( item )->Pads() )
  1029. {
  1030. if( pad->GetLocalRatsnestVisible() )
  1031. items.push_back( pad );
  1032. }
  1033. }
  1034. }
  1035. connectivity->ComputeDynamicRatsnest( items );
  1036. }
  1037. void PCB_EDITOR_CONTROL::setTransitions()
  1038. {
  1039. Go( &PCB_EDITOR_CONTROL::New, ACTIONS::doNew.MakeEvent() );
  1040. Go( &PCB_EDITOR_CONTROL::Open, ACTIONS::open.MakeEvent() );
  1041. Go( &PCB_EDITOR_CONTROL::Save, ACTIONS::save.MakeEvent() );
  1042. Go( &PCB_EDITOR_CONTROL::SaveAs, ACTIONS::saveAs.MakeEvent() );
  1043. Go( &PCB_EDITOR_CONTROL::SaveCopyAs, ACTIONS::saveCopyAs.MakeEvent() );
  1044. Go( &PCB_EDITOR_CONTROL::PageSettings, ACTIONS::pageSettings.MakeEvent() );
  1045. Go( &PCB_EDITOR_CONTROL::Plot, ACTIONS::plot.MakeEvent() );
  1046. Go( &PCB_EDITOR_CONTROL::Find, ACTIONS::find.MakeEvent() );
  1047. // Track & via size control
  1048. Go( &PCB_EDITOR_CONTROL::TrackWidthInc, PCB_ACTIONS::trackWidthInc.MakeEvent() );
  1049. Go( &PCB_EDITOR_CONTROL::TrackWidthDec, PCB_ACTIONS::trackWidthDec.MakeEvent() );
  1050. Go( &PCB_EDITOR_CONTROL::ViaSizeInc, PCB_ACTIONS::viaSizeInc.MakeEvent() );
  1051. Go( &PCB_EDITOR_CONTROL::ViaSizeDec, PCB_ACTIONS::viaSizeDec.MakeEvent() );
  1052. // Zone actions
  1053. Go( &PCB_EDITOR_CONTROL::ZoneMerge, PCB_ACTIONS::zoneMerge.MakeEvent() );
  1054. Go( &PCB_EDITOR_CONTROL::ZoneDuplicate, PCB_ACTIONS::zoneDuplicate.MakeEvent() );
  1055. // Placing tools
  1056. Go( &PCB_EDITOR_CONTROL::PlaceTarget, PCB_ACTIONS::placeTarget.MakeEvent() );
  1057. Go( &PCB_EDITOR_CONTROL::PlaceModule, PCB_ACTIONS::placeModule.MakeEvent() );
  1058. // Other
  1059. Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() );
  1060. Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() );
  1061. Go( &PCB_EDITOR_CONTROL::UnlockSelected, PCB_ACTIONS::unlock.MakeEvent() );
  1062. Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::SelectedEvent );
  1063. Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::UnselectedEvent );
  1064. Go( &PCB_EDITOR_CONTROL::CrossProbePcbToSch, EVENTS::ClearedEvent );
  1065. Go( &PCB_EDITOR_CONTROL::CrossProbeSchToPcb, PCB_ACTIONS::crossProbeSchToPcb.MakeEvent() );
  1066. Go( &PCB_EDITOR_CONTROL::DrillOrigin, PCB_ACTIONS::drillOrigin.MakeEvent() );
  1067. Go( &PCB_EDITOR_CONTROL::HighlightNet, PCB_ACTIONS::highlightNet.MakeEvent() );
  1068. Go( &PCB_EDITOR_CONTROL::ClearHighlight, PCB_ACTIONS::clearHighlight.MakeEvent() );
  1069. Go( &PCB_EDITOR_CONTROL::HighlightNetCursor, PCB_ACTIONS::highlightNetTool.MakeEvent() );
  1070. Go( &PCB_EDITOR_CONTROL::HighlightNetCursor, PCB_ACTIONS::highlightNetSelection.MakeEvent() );
  1071. Go( &PCB_EDITOR_CONTROL::LocalRatsnestTool, PCB_ACTIONS::localRatsnestTool.MakeEvent() );
  1072. Go( &PCB_EDITOR_CONTROL::HideSelectionRatsnest, PCB_ACTIONS::hideLocalRatsnest.MakeEvent() );
  1073. Go( &PCB_EDITOR_CONTROL::UpdateSelectionRatsnest, PCB_ACTIONS::updateLocalRatsnest.MakeEvent() );
  1074. }
  1075. const int PCB_EDITOR_CONTROL::WIDTH_STEP = 100000;