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.

1213 lines
37 KiB

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