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.

1859 lines
62 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2016 CERN
  5. * Copyright (C) 2019-2023 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 <kiplatform/ui.h>
  26. #include <tools/edit_tool.h>
  27. #include <tools/board_inspection_tool.h>
  28. #include <router/router_tool.h>
  29. #include <pgm_base.h>
  30. #include <tools/pcb_actions.h>
  31. #include <tools/pcb_control.h>
  32. #include <tools/pcb_picker_tool.h>
  33. #include <tools/pcb_selection_tool.h>
  34. #include <tools/board_reannotate_tool.h>
  35. #include <3d_viewer/eda_3d_viewer_frame.h>
  36. #include <board_commit.h>
  37. #include <board.h>
  38. #include <board_design_settings.h>
  39. #include <board_item.h>
  40. #include <dialogs/dialog_paste_special.h>
  41. #include <pcb_dimension.h>
  42. #include <gal/graphics_abstraction_layer.h>
  43. #include <footprint.h>
  44. #include <pcb_group.h>
  45. #include <pcb_textbox.h>
  46. #include <pcb_track.h>
  47. #include <pcb_generator.h>
  48. #include <wildcards_and_files_ext.h>
  49. #include <zone.h>
  50. #include <confirm.h>
  51. #include <kidialog.h>
  52. #include <connectivity/connectivity_data.h>
  53. #include <core/kicad_algo.h>
  54. #include <dialogs/hotkey_cycle_popup.h>
  55. #include <kicad_clipboard.h>
  56. #include <origin_viewitem.h>
  57. #include <pcb_edit_frame.h>
  58. #include <pcb_painter.h>
  59. #include <string_utf8_map.h>
  60. #include <settings/color_settings.h>
  61. #include <string>
  62. #include <tool/tool_manager.h>
  63. #include <footprint_edit_frame.h>
  64. #include <footprint_editor_settings.h>
  65. #include <widgets/appearance_controls.h>
  66. #include <widgets/wx_progress_reporters.h>
  67. #include <widgets/wx_infobar.h>
  68. #include <wx/hyperlink.h>
  69. using namespace std::placeholders;
  70. // files.cpp
  71. extern bool AskLoadBoardFileName( PCB_EDIT_FRAME* aParent, wxString* aFileName, int aCtl = 0 );
  72. PCB_CONTROL::PCB_CONTROL() :
  73. PCB_TOOL_BASE( "pcbnew.Control" ),
  74. m_frame( nullptr ),
  75. m_pickerItem( nullptr )
  76. {
  77. m_gridOrigin.reset( new KIGFX::ORIGIN_VIEWITEM() );
  78. }
  79. PCB_CONTROL::~PCB_CONTROL()
  80. {
  81. }
  82. void PCB_CONTROL::Reset( RESET_REASON aReason )
  83. {
  84. m_frame = getEditFrame<PCB_BASE_FRAME>();
  85. if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH || aReason == REDRAW )
  86. {
  87. m_gridOrigin->SetPosition( board()->GetDesignSettings().GetGridOrigin() );
  88. m_gridOrigin->SetColor( m_frame->GetGridColor() );
  89. getView()->Remove( m_gridOrigin.get() );
  90. getView()->Add( m_gridOrigin.get() );
  91. }
  92. }
  93. int PCB_CONTROL::AddLibrary( const TOOL_EVENT& aEvent )
  94. {
  95. if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) || m_frame->IsType( FRAME_PCB_EDITOR ) )
  96. {
  97. if( aEvent.IsAction( &ACTIONS::newLibrary ) )
  98. static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->CreateNewLibrary();
  99. else if( aEvent.IsAction( &ACTIONS::addLibrary ) )
  100. static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary();
  101. }
  102. return 0;
  103. }
  104. int PCB_CONTROL::DdAddLibrary( const TOOL_EVENT& aEvent )
  105. {
  106. const wxString fn = *aEvent.Parameter<wxString*>();
  107. static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary( fn );
  108. return 0;
  109. }
  110. int PCB_CONTROL::DdImportFootprint( const TOOL_EVENT& aEvent )
  111. {
  112. const wxString fn = *aEvent.Parameter<wxString*>();
  113. static_cast<FOOTPRINT_EDIT_FRAME*>( m_frame )->ImportFootprint( fn );
  114. m_frame->Zoom_Automatique( false );
  115. return 0;
  116. }
  117. int PCB_CONTROL::Quit( const TOOL_EVENT& aEvent )
  118. {
  119. m_frame->Close( false );
  120. return 0;
  121. }
  122. template<class T> void Flip( T& aValue )
  123. {
  124. aValue = !aValue;
  125. }
  126. int PCB_CONTROL::TrackDisplayMode( const TOOL_EVENT& aEvent )
  127. {
  128. Flip( displayOptions().m_DisplayPcbTrackFill );
  129. for( PCB_TRACK* track : board()->Tracks() )
  130. {
  131. if( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T )
  132. view()->Update( track, KIGFX::REPAINT );
  133. }
  134. for( BOARD_ITEM* shape : board()->Drawings() )
  135. {
  136. if( shape->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( shape )->IsOnCopperLayer() )
  137. view()->Update( shape, KIGFX::REPAINT );
  138. }
  139. canvas()->Refresh();
  140. return 0;
  141. }
  142. int PCB_CONTROL::ToggleRatsnest( const TOOL_EVENT& aEvent )
  143. {
  144. if( aEvent.IsAction( &PCB_ACTIONS::showRatsnest ) )
  145. {
  146. // N.B. Do not disable the Ratsnest layer here. We use it for local ratsnest
  147. Flip( displayOptions().m_ShowGlobalRatsnest );
  148. getEditFrame<PCB_EDIT_FRAME>()->SetElementVisibility( LAYER_RATSNEST,
  149. displayOptions().m_ShowGlobalRatsnest );
  150. }
  151. else if( aEvent.IsAction( &PCB_ACTIONS::ratsnestLineMode ) )
  152. {
  153. Flip( displayOptions().m_DisplayRatsnestLinesCurved );
  154. }
  155. frame()->OnDisplayOptionsChanged();
  156. canvas()->RedrawRatsnest();
  157. canvas()->Refresh();
  158. return 0;
  159. }
  160. int PCB_CONTROL::ViaDisplayMode( const TOOL_EVENT& aEvent )
  161. {
  162. Flip( displayOptions().m_DisplayViaFill );
  163. for( PCB_TRACK* track : board()->Tracks() )
  164. {
  165. if( track->Type() == PCB_VIA_T )
  166. view()->Update( track, KIGFX::REPAINT );
  167. }
  168. canvas()->Refresh();
  169. return 0;
  170. }
  171. /**
  172. * We have bug reports indicating that some new users confuse zone filling/unfilling with the
  173. * display modes. This will put up a warning if they show zone fills when one or more zones
  174. * are unfilled.
  175. */
  176. void PCB_CONTROL::unfilledZoneCheck()
  177. {
  178. if( Pgm().GetCommonSettings()->m_DoNotShowAgain.zone_fill_warning )
  179. return;
  180. bool unfilledZones = false;
  181. for( const ZONE* zone : board()->Zones() )
  182. {
  183. if( !zone->GetIsRuleArea() && !zone->IsFilled() )
  184. {
  185. unfilledZones = true;
  186. break;
  187. }
  188. }
  189. if( unfilledZones )
  190. {
  191. WX_INFOBAR* infobar = frame()->GetInfoBar();
  192. wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Don't show again" ),
  193. wxEmptyString );
  194. button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
  195. [&]( wxHyperlinkEvent& aEvent )
  196. {
  197. Pgm().GetCommonSettings()->m_DoNotShowAgain.zone_fill_warning = true;
  198. frame()->GetInfoBar()->Dismiss();
  199. } ) );
  200. infobar->RemoveAllButtons();
  201. infobar->AddButton( button );
  202. wxString msg;
  203. msg.Printf( _( "Not all zones are filled. Use Edit > Fill All Zones (%s) "
  204. "if you wish to see all fills." ),
  205. KeyNameFromKeyCode( PCB_ACTIONS::zoneFillAll.GetHotKey() ) );
  206. infobar->ShowMessageFor( msg, 5000, wxICON_WARNING );
  207. }
  208. }
  209. int PCB_CONTROL::ZoneDisplayMode( const TOOL_EVENT& aEvent )
  210. {
  211. PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions();
  212. // Apply new display options to the GAL canvas
  213. if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayFilled ) )
  214. {
  215. unfilledZoneCheck();
  216. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_FILLED;
  217. }
  218. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayOutline ) )
  219. {
  220. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE;
  221. }
  222. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayFractured ) )
  223. {
  224. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_FRACTURE_BORDERS;
  225. }
  226. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayTriangulated ) )
  227. {
  228. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_TRIANGULATION;
  229. }
  230. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayToggle ) )
  231. {
  232. if( opts.m_ZoneDisplayMode == ZONE_DISPLAY_MODE::SHOW_FILLED )
  233. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE;
  234. else
  235. opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_FILLED;
  236. }
  237. else
  238. {
  239. wxFAIL;
  240. }
  241. m_frame->SetDisplayOptions( opts );
  242. for( ZONE* zone : board()->Zones() )
  243. view()->Update( zone, KIGFX::REPAINT );
  244. canvas()->Refresh();
  245. return 0;
  246. }
  247. int PCB_CONTROL::HighContrastMode( const TOOL_EVENT& aEvent )
  248. {
  249. PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions();
  250. opts.m_ContrastModeDisplay = opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::NORMAL
  251. ? HIGH_CONTRAST_MODE::DIMMED
  252. : HIGH_CONTRAST_MODE::NORMAL;
  253. m_frame->SetDisplayOptions( opts );
  254. return 0;
  255. }
  256. int PCB_CONTROL::HighContrastModeCycle( const TOOL_EVENT& aEvent )
  257. {
  258. PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions();
  259. switch( opts.m_ContrastModeDisplay )
  260. {
  261. case HIGH_CONTRAST_MODE::NORMAL: opts.m_ContrastModeDisplay = HIGH_CONTRAST_MODE::DIMMED; break;
  262. case HIGH_CONTRAST_MODE::DIMMED: opts.m_ContrastModeDisplay = HIGH_CONTRAST_MODE::HIDDEN; break;
  263. case HIGH_CONTRAST_MODE::HIDDEN: opts.m_ContrastModeDisplay = HIGH_CONTRAST_MODE::NORMAL; break;
  264. }
  265. m_frame->SetDisplayOptions( opts );
  266. m_toolMgr->PostEvent( EVENTS::ContrastModeChangedByKeyEvent );
  267. return 0;
  268. }
  269. int PCB_CONTROL::ContrastModeFeedback( const TOOL_EVENT& aEvent )
  270. {
  271. if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
  272. return 0;
  273. PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions();
  274. wxArrayString labels;
  275. labels.Add( _( "Normal" ) );
  276. labels.Add( _( "Dimmed" ) );
  277. labels.Add( _( "Hidden" ) );
  278. if( !m_frame->GetHotkeyPopup() )
  279. m_frame->CreateHotkeyPopup();
  280. HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
  281. if( popup )
  282. {
  283. popup->Popup( _( "Inactive Layer Display" ), labels,
  284. static_cast<int>( opts.m_ContrastModeDisplay ) );
  285. }
  286. return 0;
  287. }
  288. int PCB_CONTROL::NetColorModeCycle( const TOOL_EVENT& aEvent )
  289. {
  290. PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions();
  291. switch( opts.m_NetColorMode )
  292. {
  293. case NET_COLOR_MODE::ALL: opts.m_NetColorMode = NET_COLOR_MODE::RATSNEST; break;
  294. case NET_COLOR_MODE::RATSNEST: opts.m_NetColorMode = NET_COLOR_MODE::OFF; break;
  295. case NET_COLOR_MODE::OFF: opts.m_NetColorMode = NET_COLOR_MODE::ALL; break;
  296. }
  297. m_frame->SetDisplayOptions( opts );
  298. return 0;
  299. }
  300. int PCB_CONTROL::RatsnestModeCycle( const TOOL_EVENT& aEvent )
  301. {
  302. if( !displayOptions().m_ShowGlobalRatsnest )
  303. {
  304. displayOptions().m_ShowGlobalRatsnest = true;
  305. displayOptions().m_RatsnestMode = RATSNEST_MODE::ALL;
  306. }
  307. else if( displayOptions().m_RatsnestMode == RATSNEST_MODE::ALL )
  308. {
  309. displayOptions().m_RatsnestMode = RATSNEST_MODE::VISIBLE;
  310. }
  311. else
  312. {
  313. displayOptions().m_ShowGlobalRatsnest = false;
  314. }
  315. getEditFrame<PCB_EDIT_FRAME>()->SetElementVisibility( LAYER_RATSNEST,
  316. displayOptions().m_ShowGlobalRatsnest );
  317. frame()->OnDisplayOptionsChanged();
  318. canvas()->RedrawRatsnest();
  319. canvas()->Refresh();
  320. return 0;
  321. }
  322. int PCB_CONTROL::LayerSwitch( const TOOL_EVENT& aEvent )
  323. {
  324. m_frame->SwitchLayer( aEvent.Parameter<PCB_LAYER_ID>() );
  325. return 0;
  326. }
  327. int PCB_CONTROL::LayerNext( const TOOL_EVENT& aEvent )
  328. {
  329. PCB_BASE_FRAME* editFrame = m_frame;
  330. BOARD* brd = board();
  331. int layer = editFrame->GetActiveLayer();
  332. int startLayer = layer;
  333. bool wraparound = false;
  334. while( startLayer != ++layer )
  335. {
  336. if( brd->IsLayerVisible( static_cast<PCB_LAYER_ID>( layer ) ) && IsCopperLayer( layer ) )
  337. break;
  338. if( layer >= B_Cu )
  339. {
  340. if( wraparound )
  341. {
  342. wxBell();
  343. return 0;
  344. }
  345. else
  346. {
  347. wraparound = true;
  348. layer = F_Cu - 1;
  349. }
  350. }
  351. }
  352. wxCHECK( IsCopperLayer( layer ), 0 );
  353. editFrame->SwitchLayer( ToLAYER_ID( layer ) );
  354. return 0;
  355. }
  356. int PCB_CONTROL::LayerPrev( const TOOL_EVENT& aEvent )
  357. {
  358. PCB_BASE_FRAME* editFrame = m_frame;
  359. BOARD* brd = board();
  360. int layer = editFrame->GetActiveLayer();
  361. int startLayer = layer;
  362. bool wraparound = false;
  363. while( startLayer != --layer )
  364. {
  365. if( IsCopperLayer( layer ) // also test for valid layer id (layer >= F_Cu)
  366. && brd->IsLayerVisible( static_cast<PCB_LAYER_ID>( layer ) ) )
  367. {
  368. break;
  369. }
  370. if( layer <= F_Cu )
  371. {
  372. if( wraparound )
  373. {
  374. wxBell();
  375. return 0;
  376. }
  377. else
  378. {
  379. wraparound = true;
  380. layer = B_Cu + 1;
  381. }
  382. }
  383. }
  384. wxCHECK( IsCopperLayer( layer ), 0 );
  385. editFrame->SwitchLayer( ToLAYER_ID( layer ) );
  386. return 0;
  387. }
  388. int PCB_CONTROL::LayerToggle( const TOOL_EVENT& aEvent )
  389. {
  390. int currentLayer = m_frame->GetActiveLayer();
  391. PCB_SCREEN* screen = m_frame->GetScreen();
  392. if( currentLayer == screen->m_Route_Layer_TOP )
  393. m_frame->SwitchLayer( screen->m_Route_Layer_BOTTOM );
  394. else
  395. m_frame->SwitchLayer( screen->m_Route_Layer_TOP );
  396. return 0;
  397. }
  398. // It'd be nice to share the min/max with the DIALOG_COLOR_PICKER, but those are
  399. // set in wxFormBuilder.
  400. #define ALPHA_MIN 0.20
  401. #define ALPHA_MAX 1.00
  402. #define ALPHA_STEP 0.05
  403. int PCB_CONTROL::LayerAlphaInc( const TOOL_EVENT& aEvent )
  404. {
  405. COLOR_SETTINGS* settings = m_frame->GetColorSettings();
  406. int currentLayer = m_frame->GetActiveLayer();
  407. KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
  408. if( currentColor.a <= ALPHA_MAX - ALPHA_STEP )
  409. {
  410. currentColor.a += ALPHA_STEP;
  411. settings->SetColor( currentLayer, currentColor );
  412. m_frame->GetCanvas()->UpdateColors();
  413. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  414. view->UpdateLayerColor( currentLayer );
  415. view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
  416. if( IsCopperLayer( currentLayer ) )
  417. view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
  418. }
  419. else
  420. {
  421. wxBell();
  422. }
  423. return 0;
  424. }
  425. int PCB_CONTROL::LayerAlphaDec( const TOOL_EVENT& aEvent )
  426. {
  427. COLOR_SETTINGS* settings = m_frame->GetColorSettings();
  428. int currentLayer = m_frame->GetActiveLayer();
  429. KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
  430. if( currentColor.a >= ALPHA_MIN + ALPHA_STEP )
  431. {
  432. currentColor.a -= ALPHA_STEP;
  433. settings->SetColor( currentLayer, currentColor );
  434. m_frame->GetCanvas()->UpdateColors();
  435. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  436. view->UpdateLayerColor( currentLayer );
  437. view->UpdateLayerColor( GetNetnameLayer( currentLayer ) );
  438. if( IsCopperLayer( currentLayer ) )
  439. view->UpdateLayerColor( ZONE_LAYER_FOR( currentLayer ) );
  440. }
  441. else
  442. {
  443. wxBell();
  444. }
  445. return 0;
  446. }
  447. void PCB_CONTROL::DoSetGridOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
  448. EDA_ITEM* originViewItem, const VECTOR2D& aPoint )
  449. {
  450. aFrame->GetDesignSettings().SetGridOrigin( VECTOR2I( aPoint ) );
  451. aView->GetGAL()->SetGridOrigin( aPoint );
  452. originViewItem->SetPosition( aPoint );
  453. aView->MarkDirty();
  454. aFrame->OnModify();
  455. }
  456. int PCB_CONTROL::GridPlaceOrigin( const TOOL_EVENT& aEvent )
  457. {
  458. VECTOR2D* origin = aEvent.Parameter<VECTOR2D*>();
  459. if( origin )
  460. {
  461. // We can't undo the other grid dialog settings, so no sense undoing just the origin
  462. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), *origin );
  463. delete origin;
  464. }
  465. else
  466. {
  467. if( m_isFootprintEditor && !getEditFrame<PCB_BASE_EDIT_FRAME>()->GetModel() )
  468. return 0;
  469. PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
  470. if( !picker ) // Happens in footprint wizard
  471. return 0;
  472. // Deactivate other tools; particularly important if another PICKER is currently running
  473. Activate();
  474. picker->SetClickHandler(
  475. [this]( const VECTOR2D& pt ) -> bool
  476. {
  477. m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
  478. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), pt );
  479. return false; // drill origin is a one-shot; don't continue with tool
  480. } );
  481. m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
  482. }
  483. return 0;
  484. }
  485. int PCB_CONTROL::GridResetOrigin( const TOOL_EVENT& aEvent )
  486. {
  487. m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UNDO_REDO::GRIDORIGIN );
  488. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), VECTOR2D( 0, 0 ) );
  489. return 0;
  490. }
  491. #define HITTEST_THRESHOLD_PIXELS 5
  492. int PCB_CONTROL::InteractiveDelete( const TOOL_EVENT& aEvent )
  493. {
  494. if( m_isFootprintEditor && !m_frame->GetBoard()->GetFirstFootprint() )
  495. return 0;
  496. PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
  497. m_pickerItem = nullptr;
  498. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  499. // Deactivate other tools; particularly important if another PICKER is currently running
  500. Activate();
  501. picker->SetCursor( KICURSOR::REMOVE );
  502. picker->SetClickHandler(
  503. [this]( const VECTOR2D& aPosition ) -> bool
  504. {
  505. if( m_pickerItem )
  506. {
  507. if( m_pickerItem && m_pickerItem->IsLocked() )
  508. {
  509. m_statusPopup.reset( new STATUS_TEXT_POPUP( m_frame ) );
  510. m_statusPopup->SetText( _( "Item locked." ) );
  511. m_statusPopup->PopupFor( 2000 );
  512. m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition()
  513. + wxPoint( 20, 20 ) );
  514. return true;
  515. }
  516. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  517. selectionTool->UnbrightenItem( m_pickerItem );
  518. PCB_SELECTION items;
  519. items.Add( m_pickerItem );
  520. EDIT_TOOL* editTool = m_toolMgr->GetTool<EDIT_TOOL>();
  521. editTool->DeleteItems( items, false );
  522. m_pickerItem = nullptr;
  523. }
  524. return true;
  525. } );
  526. picker->SetMotionHandler(
  527. [this]( const VECTOR2D& aPos )
  528. {
  529. BOARD* board = m_frame->GetBoard();
  530. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  531. GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
  532. GENERAL_COLLECTOR collector;
  533. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  534. if( m_isFootprintEditor )
  535. collector.Collect( board, GENERAL_COLLECTOR::FootprintItems, aPos, guide );
  536. else
  537. collector.Collect( board, GENERAL_COLLECTOR::BoardLevelItems, aPos, guide );
  538. // Remove unselectable items
  539. for( int i = collector.GetCount() - 1; i >= 0; --i )
  540. {
  541. if( !selectionTool->Selectable( collector[ i ] ) )
  542. collector.Remove( i );
  543. }
  544. selectionTool->FilterCollectorForHierarchy( collector, false );
  545. selectionTool->FilterCollectedItems( collector, false );
  546. if( collector.GetCount() > 1 )
  547. selectionTool->GuessSelectionCandidates( collector, aPos );
  548. BOARD_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  549. if( m_pickerItem != item )
  550. {
  551. if( m_pickerItem )
  552. selectionTool->UnbrightenItem( m_pickerItem );
  553. m_pickerItem = item;
  554. if( m_pickerItem )
  555. selectionTool->BrightenItem( m_pickerItem );
  556. }
  557. } );
  558. picker->SetFinalizeHandler(
  559. [this]( const int& aFinalState )
  560. {
  561. if( m_pickerItem )
  562. m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  563. m_statusPopup.reset();
  564. // Ensure the cursor gets changed&updated
  565. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  566. m_frame->GetCanvas()->Refresh();
  567. } );
  568. m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
  569. return 0;
  570. }
  571. static void pasteFootprintItemsToFootprintEditor( FOOTPRINT* aClipFootprint, BOARD* aBoard,
  572. std::vector<BOARD_ITEM*>& aPastedItems )
  573. {
  574. FOOTPRINT* editorFootprint = aBoard->GetFirstFootprint();
  575. aClipFootprint->SetParent( aBoard );
  576. for( PAD* pad : aClipFootprint->Pads() )
  577. {
  578. pad->SetParent( editorFootprint );
  579. aPastedItems.push_back( pad );
  580. }
  581. aClipFootprint->Pads().clear();
  582. // Not all items can be added to the current footprint: mandatory fields are already existing
  583. // in the current footprint.
  584. //
  585. for( PCB_FIELD* field : aClipFootprint->Fields() )
  586. {
  587. if( field->IsMandatoryField() )
  588. {
  589. if( PCB_GROUP* parentGroup = field->GetParentGroup() )
  590. parentGroup->RemoveItem( field );
  591. }
  592. else
  593. {
  594. PCB_TEXT* text = static_cast<PCB_TEXT*>( field );
  595. text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
  596. text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
  597. VECTOR2I pos = field->GetFPRelativePosition();
  598. field->SetParent( editorFootprint );
  599. field->SetFPRelativePosition( pos );
  600. aPastedItems.push_back( field );
  601. }
  602. }
  603. aClipFootprint->Fields().clear();
  604. for( BOARD_ITEM* item : aClipFootprint->GraphicalItems() )
  605. {
  606. if( item->Type() == PCB_TEXT_T )
  607. {
  608. PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
  609. text->SetTextAngle( text->GetTextAngle() - aClipFootprint->GetOrientation() );
  610. text->SetTextAngle( text->GetTextAngle() + editorFootprint->GetOrientation() );
  611. }
  612. item->Rotate( item->GetPosition(), -aClipFootprint->GetOrientation() );
  613. item->Rotate( item->GetPosition(), editorFootprint->GetOrientation() );
  614. VECTOR2I pos = item->GetFPRelativePosition();
  615. item->SetParent( editorFootprint );
  616. item->SetFPRelativePosition( pos );
  617. aPastedItems.push_back( item );
  618. }
  619. aClipFootprint->GraphicalItems().clear();
  620. for( ZONE* zone : aClipFootprint->Zones() )
  621. {
  622. zone->SetParent( editorFootprint );
  623. aPastedItems.push_back( zone );
  624. }
  625. aClipFootprint->Zones().clear();
  626. for( PCB_GROUP* group : aClipFootprint->Groups() )
  627. {
  628. group->SetParent( editorFootprint );
  629. aPastedItems.push_back( group );
  630. }
  631. aClipFootprint->Groups().clear();
  632. }
  633. void PCB_CONTROL::pruneItemLayers( std::vector<BOARD_ITEM*>& aItems )
  634. {
  635. // Do not prune items or layers when copying to the FP editor, because all
  636. // layers are accepted, even if they are not enabled in the dummy board
  637. // This is mainly true for internal copper layers: all are allowed but only one
  638. // (In1.cu) is enabled for the GUI.
  639. if( m_isFootprintEditor || frame()->IsType( FRAME_FOOTPRINT_EDITOR ) )
  640. return;
  641. LSET enabledLayers = board()->GetEnabledLayers();
  642. std::vector<BOARD_ITEM*> returnItems;
  643. bool fpItemDeleted = false;
  644. auto processFPItem =
  645. [&]( FOOTPRINT* aFootprint, BOARD_ITEM* aItem )
  646. {
  647. LSET allowed = aItem->GetLayerSet() & enabledLayers;
  648. if( allowed.any() )
  649. {
  650. // Don't prune internal copper layers on items with holes
  651. if( aItem->HasHole() && aItem->IsOnCopperLayer() )
  652. allowed |= LSET::InternalCuMask();
  653. aItem->SetLayerSet( allowed );
  654. }
  655. else
  656. {
  657. aFootprint->Remove( aItem );
  658. fpItemDeleted = true;
  659. }
  660. };
  661. for( BOARD_ITEM* item : aItems )
  662. {
  663. if( item->Type() == PCB_FOOTPRINT_T )
  664. {
  665. FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
  666. if( !enabledLayers.test( fp->Reference().GetLayer() ) )
  667. fp->Reference().SetLayer( fp->IsFlipped() ? B_SilkS : F_SilkS );
  668. if( !enabledLayers.test( fp->Value().GetLayer() ) )
  669. fp->Value().SetLayer( fp->IsFlipped() ? B_Fab : F_Fab );
  670. // NOTE: all traversals from the back as processFPItem() might delete the item
  671. for( int ii = static_cast<int>( fp->Fields().size() ) - 1; ii >= 0; ii-- )
  672. processFPItem( fp, fp->Fields()[ii] );
  673. for( int ii = static_cast<int>( fp->Pads().size() ) - 1; ii >= 0; ii-- )
  674. processFPItem( fp, fp->Pads()[ii] );
  675. for( int ii = static_cast<int>( fp->Zones().size() ) - 1; ii >= 0; ii-- )
  676. processFPItem( fp, fp->Zones()[ii] );
  677. for( int ii = static_cast<int>( fp->GraphicalItems().size() ) - 1; ii >= 0; ii-- )
  678. processFPItem( fp, fp->GraphicalItems()[ii] );
  679. if( fp->Fields().size()
  680. || fp->GraphicalItems().size()
  681. || fp->Pads().size()
  682. || fp->Zones().size() )
  683. {
  684. returnItems.push_back( fp );
  685. }
  686. else
  687. {
  688. if( PCB_GROUP* parentGroup = fp->GetParentGroup() )
  689. parentGroup->RemoveItem( item );
  690. }
  691. }
  692. else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
  693. {
  694. returnItems.push_back( item );
  695. }
  696. else
  697. {
  698. LSET allowed = item->GetLayerSet() & enabledLayers;
  699. if( allowed.any() )
  700. {
  701. item->SetLayerSet( allowed );
  702. returnItems.push_back( item );
  703. }
  704. else
  705. {
  706. if( PCB_GROUP* parentGroup = item->GetParentGroup() )
  707. parentGroup->RemoveItem( item );
  708. }
  709. }
  710. }
  711. if( ( returnItems.size() < aItems.size() ) || fpItemDeleted )
  712. {
  713. DisplayError( m_frame, _( "Warning: some pasted items were on layers which are not "
  714. "present in the current board.\n"
  715. "These items could not be pasted.\n" ) );
  716. }
  717. aItems = returnItems;
  718. }
  719. int PCB_CONTROL::Paste( const TOOL_EVENT& aEvent )
  720. {
  721. CLIPBOARD_IO pi;
  722. BOARD_ITEM* clipItem = pi.Parse();
  723. if( !clipItem )
  724. return 0;
  725. // The viewer frames cannot paste
  726. if( !frame()->IsType( FRAME_FOOTPRINT_EDITOR ) && !frame()->IsType( FRAME_PCB_EDITOR ) )
  727. return 0;
  728. PASTE_MODE mode = PASTE_MODE::KEEP_ANNOTATIONS;
  729. bool clear_nets = false;
  730. const wxString defaultRef = wxT( "REF**" );
  731. if( aEvent.IsAction( &ACTIONS::pasteSpecial ) )
  732. {
  733. DIALOG_PASTE_SPECIAL dlg( m_frame, &mode, defaultRef );
  734. if( clipItem->Type() != PCB_T )
  735. dlg.HideClearNets();
  736. if( dlg.ShowModal() == wxID_CANCEL )
  737. return 0;
  738. clear_nets = dlg.GetClearNets();
  739. }
  740. bool isFootprintEditor = m_isFootprintEditor || frame()->IsType( FRAME_FOOTPRINT_EDITOR );
  741. if( clipItem->Type() == PCB_T )
  742. {
  743. BOARD* clipBoard = static_cast<BOARD*>( clipItem );
  744. if( isFootprintEditor || clear_nets )
  745. {
  746. for( BOARD_CONNECTED_ITEM* item : clipBoard->AllConnectedItems() )
  747. item->SetNet( NETINFO_LIST::OrphanedItem() );
  748. }
  749. else
  750. {
  751. clipBoard->MapNets( m_frame->GetBoard() );
  752. }
  753. }
  754. // The clipboard can contain two different things, an entire kicad_pcb or a single footprint
  755. if( isFootprintEditor && ( !board() || !footprint() ) )
  756. return 0;
  757. BOARD_COMMIT commit( frame() );
  758. bool cancelled = false;
  759. switch( clipItem->Type() )
  760. {
  761. case PCB_T:
  762. {
  763. BOARD* clipBoard = static_cast<BOARD*>( clipItem );
  764. if( isFootprintEditor )
  765. {
  766. FOOTPRINT* editorFootprint = board()->GetFirstFootprint();
  767. std::vector<BOARD_ITEM*> pastedItems;
  768. for( PCB_GROUP* group : clipBoard->Groups() )
  769. {
  770. group->SetParent( editorFootprint );
  771. pastedItems.push_back( group );
  772. }
  773. clipBoard->RemoveAll( { PCB_GROUP_T } );
  774. for( FOOTPRINT* clipFootprint : clipBoard->Footprints() )
  775. pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
  776. for( BOARD_ITEM* clipDrawItem : clipBoard->Drawings() )
  777. {
  778. switch( clipDrawItem->Type() )
  779. {
  780. case PCB_TEXT_T:
  781. case PCB_TEXTBOX_T:
  782. case PCB_TABLE_T:
  783. case PCB_SHAPE_T:
  784. case PCB_DIM_ALIGNED_T:
  785. case PCB_DIM_CENTER_T:
  786. case PCB_DIM_LEADER_T:
  787. case PCB_DIM_ORTHOGONAL_T:
  788. case PCB_DIM_RADIAL_T:
  789. clipDrawItem->SetParent( editorFootprint );
  790. pastedItems.push_back( clipDrawItem );
  791. break;
  792. default:
  793. // Everything we *didn't* put into pastedItems is going to get nuked, so
  794. // make sure it's not still included in its parent group.
  795. if( PCB_GROUP* parentGroup = clipDrawItem->GetParentGroup() )
  796. parentGroup->RemoveItem( clipDrawItem );
  797. break;
  798. }
  799. }
  800. // NB: PCB_SHAPE_T actually removes everything in Drawings() (including PCB_TEXTs,
  801. // PCB_TABLES, dimensions, etc.), not just PCB_SHAPEs.)
  802. clipBoard->RemoveAll( { PCB_SHAPE_T } );
  803. clipBoard->Visit(
  804. [&]( EDA_ITEM* item, void* testData )
  805. {
  806. // Anything still on the clipboard didn't get copied and needs to be
  807. // removed from the pasted groups.
  808. BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
  809. PCB_GROUP* parentGroup = boardItem->GetParentGroup();
  810. if( parentGroup )
  811. parentGroup->RemoveItem( boardItem );
  812. return INSPECT_RESULT::CONTINUE;
  813. },
  814. nullptr, GENERAL_COLLECTOR::AllBoardItems );
  815. delete clipBoard;
  816. pruneItemLayers( pastedItems );
  817. cancelled = !placeBoardItems( &commit, pastedItems, true, true,
  818. mode == PASTE_MODE::UNIQUE_ANNOTATIONS );
  819. }
  820. else // isBoardEditor
  821. {
  822. if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
  823. {
  824. for( FOOTPRINT* clipFootprint : clipBoard->Footprints() )
  825. clipFootprint->SetReference( defaultRef );
  826. }
  827. cancelled = !placeBoardItems( &commit, clipBoard, true,
  828. mode == PASTE_MODE::UNIQUE_ANNOTATIONS );
  829. }
  830. break;
  831. }
  832. case PCB_FOOTPRINT_T:
  833. {
  834. FOOTPRINT* clipFootprint = static_cast<FOOTPRINT*>( clipItem );
  835. std::vector<BOARD_ITEM*> pastedItems;
  836. if( isFootprintEditor )
  837. {
  838. pasteFootprintItemsToFootprintEditor( clipFootprint, board(), pastedItems );
  839. delete clipFootprint;
  840. }
  841. else
  842. {
  843. if( mode == PASTE_MODE::REMOVE_ANNOTATIONS )
  844. clipFootprint->SetReference( defaultRef );
  845. clipFootprint->SetParent( board() );
  846. pastedItems.push_back( clipFootprint );
  847. }
  848. pruneItemLayers( pastedItems );
  849. cancelled = !placeBoardItems( &commit, pastedItems, true, true,
  850. mode == PASTE_MODE::UNIQUE_ANNOTATIONS );
  851. break;
  852. }
  853. default:
  854. m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
  855. break;
  856. }
  857. if( cancelled )
  858. commit.Revert();
  859. else
  860. commit.Push( _( "Paste" ) );
  861. return 1;
  862. }
  863. int PCB_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent )
  864. {
  865. wxString fileName;
  866. PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  867. if( !editFrame )
  868. return 1;
  869. // Pick a file to append
  870. if( !AskLoadBoardFileName( editFrame, &fileName, KICTL_KICAD_ONLY ) )
  871. return 1;
  872. PCB_IO_MGR::PCB_FILE_T pluginType =
  873. PCB_IO_MGR::FindPluginTypeFromBoardPath( fileName, KICTL_KICAD_ONLY );
  874. IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( pluginType ) );
  875. if( !pi )
  876. return 1;
  877. return AppendBoard( *pi, fileName );
  878. }
  879. template<typename T>
  880. static void moveUnflaggedItems( const std::deque<T>& aList, std::vector<BOARD_ITEM*>& aTarget,
  881. bool aIsNew )
  882. {
  883. std::copy_if( aList.begin(), aList.end(), std::back_inserter( aTarget ),
  884. [aIsNew]( T aItem )
  885. {
  886. bool doCopy = ( aItem->GetFlags() & SKIP_STRUCT ) == 0;
  887. aItem->ClearFlags( SKIP_STRUCT );
  888. aItem->SetFlags( aIsNew ? IS_NEW : 0 );
  889. return doCopy;
  890. } );
  891. }
  892. template<typename T>
  893. static void moveUnflaggedItems( const std::vector<T>& aList, std::vector<BOARD_ITEM*>& aTarget,
  894. bool aIsNew )
  895. {
  896. std::copy_if( aList.begin(), aList.end(), std::back_inserter( aTarget ),
  897. [aIsNew]( T aItem )
  898. {
  899. bool doCopy = ( aItem->GetFlags() & SKIP_STRUCT ) == 0;
  900. aItem->ClearFlags( SKIP_STRUCT );
  901. aItem->SetFlags( aIsNew ? IS_NEW : 0 );
  902. return doCopy;
  903. } );
  904. }
  905. bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, BOARD* aBoard, bool aAnchorAtOrigin,
  906. bool aReannotateDuplicates )
  907. {
  908. // items are new if the current board is not the board source
  909. bool isNew = board() != aBoard;
  910. std::vector<BOARD_ITEM*> items;
  911. moveUnflaggedItems( aBoard->Tracks(), items, isNew );
  912. moveUnflaggedItems( aBoard->Footprints(), items, isNew );
  913. moveUnflaggedItems( aBoard->Drawings(), items, isNew );
  914. moveUnflaggedItems( aBoard->Zones(), items, isNew );
  915. // Subtlety: When selecting a group via the mouse,
  916. // PCB_SELECTION_TOOL::highlightInternal runs, which does a SetSelected() on all
  917. // descendants. In PCB_CONTROL::placeBoardItems, below, we skip that and
  918. // mark items non-recursively. That works because the saving of the
  919. // selection created aBoard that has the group and all descendants in it.
  920. moveUnflaggedItems( aBoard->Groups(), items, isNew );
  921. moveUnflaggedItems( aBoard->Generators(), items, isNew );
  922. if( isNew )
  923. aBoard->RemoveAll();
  924. // Reparent before calling pruneItemLayers, as SetLayer can have a dependence on the
  925. // item's parent board being set correctly.
  926. if( isNew )
  927. {
  928. for( BOARD_ITEM* item : items )
  929. item->SetParent( board() );
  930. }
  931. pruneItemLayers( items );
  932. return placeBoardItems( aCommit, items, isNew, aAnchorAtOrigin, aReannotateDuplicates );
  933. }
  934. bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM*>& aItems,
  935. bool aIsNew, bool aAnchorAtOrigin, bool aReannotateDuplicates )
  936. {
  937. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  938. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  939. std::vector<BOARD_ITEM*> itemsToSel;
  940. itemsToSel.reserve( aItems.size() );
  941. for( BOARD_ITEM* item : aItems )
  942. {
  943. if( aIsNew )
  944. {
  945. const_cast<KIID&>( item->m_Uuid ) = KIID();
  946. // Even though BOARD_COMMIT::Push() will add any new items to the group, we're
  947. // going to run PCB_ACTIONS::move first, and the move tool will throw out any
  948. // items that aren't in the entered group.
  949. if( selectionTool->GetEnteredGroup() && !item->GetParentGroup() )
  950. selectionTool->GetEnteredGroup()->AddItem( item );
  951. item->SetParent( board() );
  952. }
  953. // Update item attributes if needed
  954. if( BaseType( item->Type() ) == PCB_DIMENSION_T )
  955. {
  956. static_cast<PCB_DIMENSION_BASE*>( item )->UpdateUnits();
  957. }
  958. else if( item->Type() == PCB_FOOTPRINT_T )
  959. {
  960. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
  961. // Update the footprint path with the new KIID path if the footprint is new
  962. if( aIsNew )
  963. footprint->SetPath( KIID_PATH() );
  964. for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
  965. {
  966. if( BaseType( dwg->Type() ) == PCB_DIMENSION_T )
  967. static_cast<PCB_DIMENSION_BASE*>( dwg )->UpdateUnits();
  968. }
  969. }
  970. // We only need to add the items that aren't inside a group currently selected
  971. // to the selection. If an item is inside a group and that group is selected,
  972. // then the selection tool will select it for us.
  973. if( !item->GetParentGroup() || !alg::contains( aItems, item->GetParentGroup() ) )
  974. itemsToSel.push_back( item );
  975. }
  976. // Select the items that should be selected
  977. EDA_ITEMS toSel( itemsToSel.begin(), itemsToSel.end() );
  978. m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &toSel );
  979. // Reannotate duplicate footprints (make sense only in board editor )
  980. if( aReannotateDuplicates && m_isBoardEditor )
  981. m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicatesInSelection();
  982. for( BOARD_ITEM* item : aItems )
  983. {
  984. if( aIsNew )
  985. aCommit->Add( item );
  986. else
  987. aCommit->Added( item );
  988. }
  989. PCB_SELECTION& selection = selectionTool->GetSelection();
  990. if( selection.Size() > 0 )
  991. {
  992. if( aAnchorAtOrigin )
  993. {
  994. selection.SetReferencePoint( VECTOR2I( 0, 0 ) );
  995. }
  996. else
  997. {
  998. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.GetTopLeftItem() );
  999. selection.SetReferencePoint( item->GetPosition() );
  1000. }
  1001. getViewControls()->SetCursorPosition( getViewControls()->GetMousePosition(), false );
  1002. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  1003. return m_toolMgr->RunSynchronousAction( PCB_ACTIONS::move, aCommit );
  1004. }
  1005. return true;
  1006. }
  1007. int PCB_CONTROL::AppendBoard( PCB_IO& pi, wxString& fileName )
  1008. {
  1009. PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  1010. if( !editFrame )
  1011. return 1;
  1012. BOARD* brd = board();
  1013. if( !brd )
  1014. return 1;
  1015. BOARD_COMMIT commit( editFrame );
  1016. // Mark existing items, in order to know what are the new items so we can select only
  1017. // the new items after loading
  1018. for( PCB_TRACK* track : brd->Tracks() )
  1019. track->SetFlags( SKIP_STRUCT );
  1020. for( FOOTPRINT* footprint : brd->Footprints() )
  1021. footprint->SetFlags( SKIP_STRUCT );
  1022. for( PCB_GROUP* group : brd->Groups() )
  1023. group->SetFlags( SKIP_STRUCT );
  1024. for( BOARD_ITEM* drawing : brd->Drawings() )
  1025. drawing->SetFlags( SKIP_STRUCT );
  1026. for( ZONE* zone : brd->Zones() )
  1027. zone->SetFlags( SKIP_STRUCT );
  1028. for( PCB_GENERATOR* generator : brd->Generators() )
  1029. generator->SetFlags( SKIP_STRUCT );
  1030. std::map<wxString, wxString> oldProperties = brd->GetProperties();
  1031. std::map<wxString, wxString> newProperties;
  1032. PAGE_INFO oldPageInfo = brd->GetPageSettings();
  1033. TITLE_BLOCK oldTitleBlock = brd->GetTitleBlock();
  1034. // Keep also the count of copper layers, to adjust if necessary
  1035. int initialCopperLayerCount = brd->GetCopperLayerCount();
  1036. LSET initialEnabledLayers = brd->GetEnabledLayers();
  1037. // Load the data
  1038. try
  1039. {
  1040. STRING_UTF8_MAP props;
  1041. // PCB_IO_EAGLE can use this info to center the BOARD, but it does not yet.
  1042. props["page_width"] = std::to_string( editFrame->GetPageSizeIU().x );
  1043. props["page_height"] = std::to_string( editFrame->GetPageSizeIU().y );
  1044. pi.SetQueryUserCallback(
  1045. [&]( wxString aTitle, int aIcon, wxString aMessage, wxString aAction ) -> bool
  1046. {
  1047. KIDIALOG dlg( editFrame, aMessage, aTitle, wxOK | wxCANCEL | aIcon );
  1048. if( !aAction.IsEmpty() )
  1049. dlg.SetOKLabel( aAction );
  1050. dlg.DoNotShowCheckbox( aMessage, 0 );
  1051. return dlg.ShowModal() == wxID_OK;
  1052. } );
  1053. WX_PROGRESS_REPORTER progressReporter( editFrame, _( "Loading PCB" ), 1 );
  1054. editFrame->GetDesignSettings().m_NetSettings->m_NetClasses.clear();
  1055. pi.SetProgressReporter( &progressReporter );
  1056. pi.LoadBoard( fileName, brd, &props, nullptr );
  1057. }
  1058. catch( const IO_ERROR& ioe )
  1059. {
  1060. wxString msg = wxString::Format( _( "Error loading board.\n%s" ), ioe.What() );
  1061. DisplayError( editFrame, msg );
  1062. return 0;
  1063. }
  1064. newProperties = brd->GetProperties();
  1065. for( const std::pair<const wxString, wxString>& prop : oldProperties )
  1066. newProperties[ prop.first ] = prop.second;
  1067. brd->SetProperties( newProperties );
  1068. brd->SetPageSettings( oldPageInfo );
  1069. brd->SetTitleBlock( oldTitleBlock );
  1070. // rebuild nets and ratsnest before any use of nets
  1071. brd->BuildListOfNets();
  1072. brd->SynchronizeNetsAndNetClasses( true );
  1073. brd->BuildConnectivity();
  1074. // Synchronize layers
  1075. // we should not ask PLUGINs to do these items:
  1076. int copperLayerCount = brd->GetCopperLayerCount();
  1077. if( copperLayerCount > initialCopperLayerCount )
  1078. brd->SetCopperLayerCount( copperLayerCount );
  1079. // Enable all used layers, and make them visible:
  1080. LSET enabledLayers = brd->GetEnabledLayers();
  1081. enabledLayers |= initialEnabledLayers;
  1082. brd->SetEnabledLayers( enabledLayers );
  1083. brd->SetVisibleLayers( enabledLayers );
  1084. if( placeBoardItems( &commit, brd, false, false /* Don't reannotate dupes on Append Board */ ) )
  1085. commit.Push( _( "Append Board" ) );
  1086. else
  1087. commit.Revert();
  1088. // Refresh the UI for the updated board properties
  1089. editFrame->GetAppearancePanel()->OnBoardChanged();
  1090. return 0;
  1091. }
  1092. int PCB_CONTROL::Undo( const TOOL_EVENT& aEvent )
  1093. {
  1094. PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
  1095. wxCommandEvent dummy;
  1096. if( editFrame )
  1097. editFrame->RestoreCopyFromUndoList( dummy );
  1098. return 0;
  1099. }
  1100. int PCB_CONTROL::Redo( const TOOL_EVENT& aEvent )
  1101. {
  1102. PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
  1103. wxCommandEvent dummy;
  1104. if( editFrame )
  1105. editFrame->RestoreCopyFromRedoList( dummy );
  1106. return 0;
  1107. }
  1108. int PCB_CONTROL::SnapMode( const TOOL_EVENT& aEvent )
  1109. {
  1110. MAGNETIC_SETTINGS& settings = m_isFootprintEditor
  1111. ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
  1112. : m_frame->GetPcbNewSettings()->m_MagneticItems;
  1113. bool& snapMode = settings.allLayers;
  1114. if( aEvent.IsAction( &PCB_ACTIONS::magneticSnapActiveLayer ) )
  1115. snapMode = false;
  1116. else if( aEvent.IsAction( &PCB_ACTIONS::magneticSnapAllLayers ) )
  1117. snapMode = true;
  1118. else
  1119. snapMode = !snapMode;
  1120. m_toolMgr->PostEvent( PCB_EVENTS::SnappingModeChangedByKeyEvent );
  1121. return 0;
  1122. }
  1123. int PCB_CONTROL::SnapModeFeedback( const TOOL_EVENT& aEvent )
  1124. {
  1125. if( !Pgm().GetCommonSettings()->m_Input.hotkey_feedback )
  1126. return 0;
  1127. wxArrayString labels;
  1128. labels.Add( _( "Active Layer" ) );
  1129. labels.Add( _( "All Layers" ) );
  1130. if( !m_frame->GetHotkeyPopup() )
  1131. m_frame->CreateHotkeyPopup();
  1132. HOTKEY_CYCLE_POPUP* popup = m_frame->GetHotkeyPopup();
  1133. MAGNETIC_SETTINGS& settings = m_isFootprintEditor
  1134. ? m_frame->GetFootprintEditorSettings()->m_MagneticItems
  1135. : m_frame->GetPcbNewSettings()->m_MagneticItems;
  1136. if( popup )
  1137. popup->Popup( _( "Object Snapping" ), labels, static_cast<int>( settings.allLayers ) );
  1138. return 0;
  1139. }
  1140. int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
  1141. {
  1142. PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1143. ROUTER_TOOL* routerTool = m_toolMgr->GetTool<ROUTER_TOOL>();
  1144. PCB_SELECTION& selection = selTool->GetSelection();
  1145. PCB_EDIT_FRAME* pcbFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  1146. std::shared_ptr<DRC_ENGINE> drcEngine = m_frame->GetBoard()->GetDesignSettings().m_DRCEngine;
  1147. DRC_CONSTRAINT constraint;
  1148. std::vector<MSG_PANEL_ITEM> msgItems;
  1149. if( routerTool && routerTool->RoutingInProgress() )
  1150. {
  1151. routerTool->UpdateMessagePanel();
  1152. return 0;
  1153. }
  1154. if( !pcbFrame && !m_frame->GetModel() )
  1155. return 0;
  1156. if( selection.Empty() )
  1157. {
  1158. if( !pcbFrame )
  1159. {
  1160. FOOTPRINT* fp = static_cast<FOOTPRINT*>( m_frame->GetModel() );
  1161. fp->GetMsgPanelInfo( m_frame, msgItems );
  1162. }
  1163. else
  1164. {
  1165. m_frame->SetMsgPanel( m_frame->GetBoard() );
  1166. }
  1167. }
  1168. else if( selection.GetSize() == 1 )
  1169. {
  1170. EDA_ITEM* item = selection.Front();
  1171. item->GetMsgPanelInfo( m_frame, msgItems );
  1172. PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
  1173. NETINFO_ITEM* net = track ? track->GetNet() : nullptr;
  1174. NETINFO_ITEM* coupledNet = net ? m_frame->GetBoard()->DpCoupledNet( net ) : nullptr;
  1175. if( coupledNet )
  1176. {
  1177. SEG trackSeg( track->GetStart(), track->GetEnd() );
  1178. PCB_TRACK* coupledItem = nullptr;
  1179. SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX;
  1180. for( PCB_TRACK* candidate : m_frame->GetBoard()->Tracks() )
  1181. {
  1182. if( candidate->GetNet() != coupledNet )
  1183. continue;
  1184. SEG::ecoord dist_sq = trackSeg.SquaredDistance( SEG( candidate->GetStart(),
  1185. candidate->GetEnd() ) );
  1186. if( !coupledItem || dist_sq < closestDist_sq )
  1187. {
  1188. coupledItem = candidate;
  1189. closestDist_sq = dist_sq;
  1190. }
  1191. }
  1192. constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, track, coupledItem,
  1193. track->GetLayer() );
  1194. wxString msg = m_frame->MessageTextFromMinOptMax( constraint.Value() );
  1195. if( !msg.IsEmpty() )
  1196. {
  1197. msgItems.emplace_back( wxString::Format( _( "DP Gap Constraints: %s" ), msg ),
  1198. wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
  1199. }
  1200. constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, track,
  1201. coupledItem, track->GetLayer() );
  1202. if( constraint.Value().HasMax() )
  1203. {
  1204. msg = m_frame->MessageTextFromValue( constraint.Value().Max() );
  1205. msgItems.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ), msg ),
  1206. wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
  1207. }
  1208. }
  1209. }
  1210. else if( pcbFrame && selection.GetSize() == 2 )
  1211. {
  1212. // Pair selection broken into multiple, optional data, starting with the selected item
  1213. // names
  1214. BOARD_ITEM* a = static_cast<BOARD_ITEM*>( selection[0] );
  1215. BOARD_ITEM* b = static_cast<BOARD_ITEM*>( selection[1] );
  1216. msgItems.emplace_back( MSG_PANEL_ITEM( a->GetItemDescription( m_frame ),
  1217. b->GetItemDescription( m_frame ) ) );
  1218. BOARD_CONNECTED_ITEM* a_conn = dyn_cast<BOARD_CONNECTED_ITEM*>( a );
  1219. BOARD_CONNECTED_ITEM* b_conn = dyn_cast<BOARD_CONNECTED_ITEM*>( b );
  1220. if( a_conn && b_conn )
  1221. {
  1222. LSET overlap = a_conn->GetLayerSet() & b_conn->GetLayerSet() & LSET::AllCuMask();
  1223. int a_netcode = a_conn->GetNetCode();
  1224. int b_netcode = b_conn->GetNetCode();
  1225. if( overlap.count() > 0
  1226. && ( a_netcode != b_netcode || a_netcode < 0 || b_netcode < 0 ) )
  1227. {
  1228. PCB_LAYER_ID layer = overlap.CuStack().front();
  1229. constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer );
  1230. msgItems.emplace_back( _( "Resolved clearance" ),
  1231. m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
  1232. std::shared_ptr<SHAPE> a_shape( a_conn->GetEffectiveShape( layer ) );
  1233. std::shared_ptr<SHAPE> b_shape( b_conn->GetEffectiveShape( layer ) );
  1234. int actual_clearance = a_shape->GetClearance( b_shape.get() );
  1235. if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
  1236. {
  1237. msgItems.emplace_back( _( "Actual clearance" ),
  1238. m_frame->MessageTextFromValue( actual_clearance ) );
  1239. }
  1240. }
  1241. }
  1242. if( ( a->HasHole() || b->HasHole() ) )
  1243. {
  1244. PCB_LAYER_ID active = m_frame->GetActiveLayer();
  1245. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  1246. if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
  1247. layer = active;
  1248. else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
  1249. layer = active;
  1250. else if( a->HasHole() && b->IsOnCopperLayer() )
  1251. layer = b->GetLayer();
  1252. else if( b->HasHole() && a->IsOnCopperLayer() )
  1253. layer = a->GetLayer();
  1254. if( IsCopperLayer( layer ) )
  1255. {
  1256. int actual = std::numeric_limits<int>::max();
  1257. if( a->HasHole() && b->IsOnCopperLayer() )
  1258. {
  1259. std::shared_ptr<SHAPE_SEGMENT> hole = a->GetEffectiveHoleShape();
  1260. std::shared_ptr<SHAPE> other( b->GetEffectiveShape( layer ) );
  1261. actual = std::min( actual, hole->GetClearance( other.get() ) );
  1262. }
  1263. if( b->HasHole() && a->IsOnCopperLayer() )
  1264. {
  1265. std::shared_ptr<SHAPE_SEGMENT> hole = b->GetEffectiveHoleShape();
  1266. std::shared_ptr<SHAPE> other( a->GetEffectiveShape( layer ) );
  1267. actual = std::min( actual, hole->GetClearance( other.get() ) );
  1268. }
  1269. if( actual < std::numeric_limits<int>::max() )
  1270. {
  1271. constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer );
  1272. msgItems.emplace_back( _( "Resolved hole clearance" ),
  1273. m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
  1274. if( actual > -1 && actual < std::numeric_limits<int>::max() )
  1275. {
  1276. msgItems.emplace_back( _( "Actual hole clearance" ),
  1277. m_frame->MessageTextFromValue( actual ) );
  1278. }
  1279. }
  1280. }
  1281. }
  1282. for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
  1283. {
  1284. PCB_LAYER_ID active = m_frame->GetActiveLayer();
  1285. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  1286. if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
  1287. {
  1288. if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
  1289. layer = active;
  1290. else if( IsCopperLayer( b->GetLayer() ) )
  1291. layer = b->GetLayer();
  1292. }
  1293. else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
  1294. {
  1295. if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
  1296. layer = active;
  1297. else if( IsCopperLayer( a->GetLayer() ) )
  1298. layer = a->GetLayer();
  1299. }
  1300. if( layer >= 0 )
  1301. {
  1302. constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer );
  1303. if( edgeLayer == Edge_Cuts )
  1304. {
  1305. msgItems.emplace_back( _( "Resolved edge clearance" ),
  1306. m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
  1307. }
  1308. else
  1309. {
  1310. msgItems.emplace_back( _( "Resolved margin clearance" ),
  1311. m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
  1312. }
  1313. }
  1314. }
  1315. }
  1316. if( msgItems.empty() )
  1317. {
  1318. if( selection.GetSize() )
  1319. {
  1320. msgItems.emplace_back( _( "Selected Items" ),
  1321. wxString::Format( wxT( "%d" ), selection.GetSize() ) );
  1322. if( m_isBoardEditor )
  1323. {
  1324. std::set<wxString> netNames;
  1325. std::set<wxString> netClasses;
  1326. for( EDA_ITEM* item : selection )
  1327. {
  1328. if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  1329. {
  1330. netNames.insert( UnescapeString( bci->GetNetname() ) );
  1331. netClasses.insert( UnescapeString( bci->GetEffectiveNetClass()->GetName() ) );
  1332. if( netNames.size() > 1 && netClasses.size() > 1 )
  1333. break;
  1334. }
  1335. }
  1336. if( netNames.size() == 1 )
  1337. msgItems.emplace_back( _( "Net" ), *netNames.begin() );
  1338. if( netClasses.size() == 1 )
  1339. msgItems.emplace_back( _( "Resolved Netclass" ), *netClasses.begin() );
  1340. }
  1341. }
  1342. else
  1343. {
  1344. m_frame->GetBoard()->GetMsgPanelInfo( m_frame, msgItems );
  1345. }
  1346. }
  1347. m_frame->SetMsgPanel( msgItems );
  1348. return 0;
  1349. }
  1350. int PCB_CONTROL::DdAppendBoard( const TOOL_EVENT& aEvent )
  1351. {
  1352. wxFileName fileName = wxFileName( *aEvent.Parameter<wxString*>() );
  1353. PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  1354. if( !editFrame )
  1355. return 1;
  1356. wxString filePath = fileName.GetFullPath();
  1357. PCB_IO_MGR::PCB_FILE_T pluginType = PCB_IO_MGR::FindPluginTypeFromBoardPath( filePath );
  1358. IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( pluginType ) );
  1359. if( !pi )
  1360. return 1;
  1361. return AppendBoard( *pi, filePath );
  1362. }
  1363. int PCB_CONTROL::FlipPcbView( const TOOL_EVENT& aEvent )
  1364. {
  1365. view()->SetMirror( !view()->IsMirroredX(), false );
  1366. view()->RecacheAllItems();
  1367. frame()->GetCanvas()->ForceRefresh();
  1368. frame()->OnDisplayOptionsChanged();
  1369. return 0;
  1370. }
  1371. void PCB_CONTROL::setTransitions()
  1372. {
  1373. Go( &PCB_CONTROL::AddLibrary, ACTIONS::newLibrary.MakeEvent() );
  1374. Go( &PCB_CONTROL::AddLibrary, ACTIONS::addLibrary.MakeEvent() );
  1375. Go( &PCB_CONTROL::Print, ACTIONS::print.MakeEvent() );
  1376. Go( &PCB_CONTROL::Quit, ACTIONS::quit.MakeEvent() );
  1377. // Display modes
  1378. Go( &PCB_CONTROL::TrackDisplayMode, PCB_ACTIONS::trackDisplayMode.MakeEvent() );
  1379. Go( &PCB_CONTROL::ToggleRatsnest, PCB_ACTIONS::showRatsnest.MakeEvent() );
  1380. Go( &PCB_CONTROL::ToggleRatsnest, PCB_ACTIONS::ratsnestLineMode.MakeEvent() );
  1381. Go( &PCB_CONTROL::ViaDisplayMode, PCB_ACTIONS::viaDisplayMode.MakeEvent() );
  1382. Go( &PCB_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayFilled.MakeEvent() );
  1383. Go( &PCB_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayOutline.MakeEvent() );
  1384. Go( &PCB_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayFractured.MakeEvent() );
  1385. Go( &PCB_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayTriangulated.MakeEvent() );
  1386. Go( &PCB_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayToggle.MakeEvent() );
  1387. Go( &PCB_CONTROL::HighContrastMode, ACTIONS::highContrastMode.MakeEvent() );
  1388. Go( &PCB_CONTROL::HighContrastModeCycle, ACTIONS::highContrastModeCycle.MakeEvent() );
  1389. Go( &PCB_CONTROL::ContrastModeFeedback, EVENTS::ContrastModeChangedByKeyEvent );
  1390. Go( &PCB_CONTROL::NetColorModeCycle, PCB_ACTIONS::netColorModeCycle.MakeEvent() );
  1391. Go( &PCB_CONTROL::RatsnestModeCycle, PCB_ACTIONS::ratsnestModeCycle.MakeEvent() );
  1392. Go( &PCB_CONTROL::FlipPcbView, PCB_ACTIONS::flipBoard.MakeEvent() );
  1393. // Layer control
  1394. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerTop.MakeEvent() );
  1395. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner1.MakeEvent() );
  1396. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner2.MakeEvent() );
  1397. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner3.MakeEvent() );
  1398. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner4.MakeEvent() );
  1399. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner5.MakeEvent() );
  1400. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner6.MakeEvent() );
  1401. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner7.MakeEvent() );
  1402. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner8.MakeEvent() );
  1403. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner9.MakeEvent() );
  1404. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner10.MakeEvent() );
  1405. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner11.MakeEvent() );
  1406. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner12.MakeEvent() );
  1407. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner13.MakeEvent() );
  1408. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner14.MakeEvent() );
  1409. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner15.MakeEvent() );
  1410. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner16.MakeEvent() );
  1411. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner17.MakeEvent() );
  1412. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner18.MakeEvent() );
  1413. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner19.MakeEvent() );
  1414. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner20.MakeEvent() );
  1415. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner21.MakeEvent() );
  1416. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner22.MakeEvent() );
  1417. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner23.MakeEvent() );
  1418. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner24.MakeEvent() );
  1419. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner25.MakeEvent() );
  1420. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner26.MakeEvent() );
  1421. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner27.MakeEvent() );
  1422. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner28.MakeEvent() );
  1423. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner29.MakeEvent() );
  1424. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner30.MakeEvent() );
  1425. Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerBottom.MakeEvent() );
  1426. Go( &PCB_CONTROL::LayerNext, PCB_ACTIONS::layerNext.MakeEvent() );
  1427. Go( &PCB_CONTROL::LayerPrev, PCB_ACTIONS::layerPrev.MakeEvent() );
  1428. Go( &PCB_CONTROL::LayerToggle, PCB_ACTIONS::layerToggle.MakeEvent() );
  1429. Go( &PCB_CONTROL::LayerAlphaInc, PCB_ACTIONS::layerAlphaInc.MakeEvent() );
  1430. Go( &PCB_CONTROL::LayerAlphaDec, PCB_ACTIONS::layerAlphaDec.MakeEvent() );
  1431. // Grid control
  1432. Go( &PCB_CONTROL::GridPlaceOrigin, ACTIONS::gridSetOrigin.MakeEvent() );
  1433. Go( &PCB_CONTROL::GridResetOrigin, ACTIONS::gridResetOrigin.MakeEvent() );
  1434. Go( &PCB_CONTROL::Undo, ACTIONS::undo.MakeEvent() );
  1435. Go( &PCB_CONTROL::Redo, ACTIONS::redo.MakeEvent() );
  1436. // Snapping control
  1437. Go( &PCB_CONTROL::SnapMode, PCB_ACTIONS::magneticSnapActiveLayer.MakeEvent() );
  1438. Go( &PCB_CONTROL::SnapMode, PCB_ACTIONS::magneticSnapAllLayers.MakeEvent() );
  1439. Go( &PCB_CONTROL::SnapMode, PCB_ACTIONS::magneticSnapToggle.MakeEvent() );
  1440. Go( &PCB_CONTROL::SnapModeFeedback, PCB_EVENTS::SnappingModeChangedByKeyEvent );
  1441. // Miscellaneous
  1442. Go( &PCB_CONTROL::InteractiveDelete, ACTIONS::deleteTool.MakeEvent() );
  1443. // Append control
  1444. Go( &PCB_CONTROL::AppendBoardFromFile, PCB_ACTIONS::appendBoard.MakeEvent() );
  1445. Go( &PCB_CONTROL::DdAppendBoard, PCB_ACTIONS::ddAppendBoard.MakeEvent() );
  1446. Go( &PCB_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
  1447. Go( &PCB_CONTROL::Paste, ACTIONS::pasteSpecial.MakeEvent() );
  1448. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::PointSelectedEvent );
  1449. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::SelectedEvent );
  1450. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::UnselectedEvent );
  1451. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::ClearedEvent );
  1452. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::SelectedItemsModified );
  1453. Go( &PCB_CONTROL::UpdateMessagePanel, EVENTS::ConnectivityChangedEvent );
  1454. // Add library by dropping file
  1455. Go( &PCB_CONTROL::DdAddLibrary, ACTIONS::ddAddLibrary.MakeEvent() );
  1456. Go( &PCB_CONTROL::DdImportFootprint, PCB_ACTIONS::ddImportFootprint.MakeEvent() );
  1457. }