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.

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