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.

1182 lines
36 KiB

12 years ago
12 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 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 "edit_tool.h"
  26. #include "pcb_actions.h"
  27. #include "pcbnew_control.h"
  28. #include "pcbnew_picker_tool.h"
  29. #include "selection_tool.h"
  30. #include <3d_viewer/eda_3d_viewer.h>
  31. #include <bitmaps.h>
  32. #include <board_commit.h>
  33. #include <class_board.h>
  34. #include <class_board_item.h>
  35. #include <class_module.h>
  36. #include <class_track.h>
  37. #include <class_zone.h>
  38. #include <class_edge_mod.h>
  39. #include <confirm.h>
  40. #include <connectivity/connectivity_data.h>
  41. #include <gal/graphics_abstraction_layer.h>
  42. #include <io_mgr.h>
  43. #include <kicad_clipboard.h>
  44. #include <kicad_plugin.h>
  45. #include <kiway.h>
  46. #include <origin_viewitem.h>
  47. #include <pcb_edit_frame.h>
  48. #include <pcb_painter.h>
  49. #include <pcb_screen.h>
  50. #include <pcbnew_settings.h>
  51. #include <properties.h>
  52. #include <settings/color_settings.h>
  53. #include <tool/tool_manager.h>
  54. #include <view/view_controls.h>
  55. #include <functional>
  56. #include <footprint_viewer_frame.h>
  57. #include <footprint_edit_frame.h>
  58. #include <math/util.h> // for KiROUND
  59. using namespace std::placeholders;
  60. // files.cpp
  61. extern bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName,
  62. bool aKicadFilesOnly = false );
  63. extern IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl );
  64. PCBNEW_CONTROL::PCBNEW_CONTROL() :
  65. PCB_TOOL_BASE( "pcbnew.Control" ),
  66. m_frame( nullptr ),
  67. m_pickerItem( nullptr )
  68. {
  69. m_gridOrigin.reset( new KIGFX::ORIGIN_VIEWITEM() );
  70. }
  71. PCBNEW_CONTROL::~PCBNEW_CONTROL()
  72. {
  73. }
  74. void PCBNEW_CONTROL::Reset( RESET_REASON aReason )
  75. {
  76. m_frame = getEditFrame<PCB_BASE_FRAME>();
  77. if( aReason == MODEL_RELOAD || aReason == GAL_SWITCH )
  78. {
  79. m_gridOrigin->SetPosition( board()->GetGridOrigin() );
  80. m_gridOrigin->SetColor( m_frame->GetGridColor() );
  81. getView()->Remove( m_gridOrigin.get() );
  82. getView()->Add( m_gridOrigin.get() );
  83. }
  84. }
  85. int PCBNEW_CONTROL::AddLibrary( const TOOL_EVENT& aEvent )
  86. {
  87. if( m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) || m_frame->IsType( FRAME_PCB_EDITOR ) )
  88. {
  89. if( aEvent.IsAction( &ACTIONS::newLibrary ) )
  90. static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->CreateNewLibrary();
  91. else if( aEvent.IsAction( &ACTIONS::addLibrary ) )
  92. static_cast<PCB_BASE_EDIT_FRAME*>( m_frame )->AddLibrary();
  93. }
  94. return 0;
  95. }
  96. int PCBNEW_CONTROL::Quit( const TOOL_EVENT& aEvent )
  97. {
  98. m_frame->Close( false );
  99. return 0;
  100. }
  101. template<class T> void Flip( T& aValue )
  102. {
  103. aValue = !aValue;
  104. }
  105. int PCBNEW_CONTROL::TrackDisplayMode( const TOOL_EVENT& aEvent )
  106. {
  107. auto opts = displayOptions();
  108. Flip( opts.m_DisplayPcbTrackFill );
  109. m_frame->SetDisplayOptions( opts );
  110. view()->UpdateDisplayOptions( opts );
  111. for( auto track : board()->Tracks() )
  112. {
  113. if( track->Type() == PCB_TRACE_T )
  114. view()->Update( track, KIGFX::GEOMETRY );
  115. }
  116. canvas()->Refresh();
  117. return 0;
  118. }
  119. int PCBNEW_CONTROL::ToggleRatsnest( const TOOL_EVENT& aEvent )
  120. {
  121. auto opts = displayOptions();
  122. if( aEvent.IsAction( &PCB_ACTIONS::showRatsnest ) )
  123. {
  124. // N.B. Do not disable the Ratsnest layer here. We use it for local ratsnest
  125. Flip( opts.m_ShowGlobalRatsnest );
  126. m_frame->SetDisplayOptions( opts );
  127. view()->UpdateDisplayOptions( opts );
  128. getEditFrame<PCB_EDIT_FRAME>()->SetElementVisibility( LAYER_RATSNEST,
  129. opts.m_ShowGlobalRatsnest );
  130. }
  131. else if( aEvent.IsAction( &PCB_ACTIONS::ratsnestLineMode ) )
  132. {
  133. Flip( opts.m_DisplayRatsnestLinesCurved );
  134. m_frame->SetDisplayOptions( opts );
  135. view()->UpdateDisplayOptions( opts );
  136. }
  137. canvas()->RedrawRatsnest();
  138. canvas()->Refresh();
  139. return 0;
  140. }
  141. int PCBNEW_CONTROL::PadDisplayMode( const TOOL_EVENT& aEvent )
  142. {
  143. auto opts = displayOptions();
  144. Flip( opts.m_DisplayPadFill );
  145. m_frame->SetDisplayOptions( opts );
  146. view()->UpdateDisplayOptions( opts );
  147. for( auto module : board()->Modules() ) // fixme: move to PCB_VIEW
  148. {
  149. for( auto pad : module->Pads() )
  150. view()->Update( pad, KIGFX::GEOMETRY );
  151. }
  152. canvas()->Refresh();
  153. return 0;
  154. }
  155. int PCBNEW_CONTROL::ViaDisplayMode( const TOOL_EVENT& aEvent )
  156. {
  157. auto opts = displayOptions();
  158. Flip( opts.m_DisplayViaFill );
  159. view()->UpdateDisplayOptions( opts );
  160. m_frame->SetDisplayOptions( opts );
  161. for( auto track : board()->Tracks() )
  162. {
  163. if( track->Type() == PCB_TRACE_T || track->Type() == PCB_VIA_T )
  164. view()->Update( track, KIGFX::GEOMETRY );
  165. }
  166. canvas()->Refresh();
  167. return 0;
  168. }
  169. int PCBNEW_CONTROL::GraphicDisplayMode( const TOOL_EVENT& aEvent )
  170. {
  171. auto opts = displayOptions();
  172. Flip( opts.m_DisplayDrawItemsFill );
  173. m_frame->SetDisplayOptions( opts );
  174. view()->UpdateDisplayOptions( opts );
  175. for( auto item : board()->Drawings() )
  176. {
  177. view()->Update( item, KIGFX::GEOMETRY );
  178. }
  179. canvas()->Refresh();
  180. return 0;
  181. }
  182. int PCBNEW_CONTROL::ModuleEdgeOutlines( const TOOL_EVENT& aEvent )
  183. {
  184. auto opts = displayOptions();
  185. Flip( opts.m_DisplayModEdgeFill );
  186. m_frame->SetDisplayOptions( opts );
  187. view()->UpdateDisplayOptions( opts );
  188. for( auto module : board()->Modules() )
  189. {
  190. for( auto item : module->GraphicalItems() )
  191. {
  192. if( item->Type() == PCB_MODULE_EDGE_T )
  193. view()->Update( item, KIGFX::GEOMETRY );
  194. }
  195. }
  196. canvas()->Refresh();
  197. return 0;
  198. }
  199. int PCBNEW_CONTROL::ModuleTextOutlines( const TOOL_EVENT& aEvent )
  200. {
  201. auto opts = displayOptions();
  202. Flip( opts.m_DisplayModTextFill );
  203. m_frame->SetDisplayOptions( opts );
  204. view()->UpdateDisplayOptions( opts );
  205. for( auto module : board()->Modules() )
  206. {
  207. view()->Update( &module->Reference(), KIGFX::GEOMETRY );
  208. view()->Update( &module->Value(), KIGFX::GEOMETRY );
  209. for( auto item : module->GraphicalItems() )
  210. {
  211. if( item->Type() == PCB_MODULE_TEXT_T )
  212. view()->Update( item, KIGFX::GEOMETRY );
  213. }
  214. }
  215. canvas()->Refresh();
  216. return 0;
  217. }
  218. int PCBNEW_CONTROL::ZoneDisplayMode( const TOOL_EVENT& aEvent )
  219. {
  220. auto opts = displayOptions();
  221. // Apply new display options to the GAL canvas
  222. if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayEnable ) )
  223. opts.m_DisplayZonesMode = 0;
  224. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayDisable ) )
  225. opts.m_DisplayZonesMode = 1;
  226. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayOutlines ) )
  227. opts.m_DisplayZonesMode = 2;
  228. else if( aEvent.IsAction( &PCB_ACTIONS::zoneDisplayToggle ) )
  229. opts.m_DisplayZonesMode = ( opts.m_DisplayZonesMode + 1 ) % 3;
  230. else
  231. wxFAIL;
  232. m_frame->SetDisplayOptions( opts );
  233. view()->UpdateDisplayOptions( opts );
  234. for( int i = 0; i < board()->GetAreaCount(); ++i )
  235. view()->Update( board()->GetArea( i ), KIGFX::GEOMETRY );
  236. canvas()->Refresh();
  237. return 0;
  238. }
  239. int PCBNEW_CONTROL::HighContrastMode( const TOOL_EVENT& aEvent )
  240. {
  241. auto opts = displayOptions();
  242. Flip( opts.m_ContrastModeDisplay );
  243. m_frame->SetDisplayOptions( opts );
  244. view()->UpdateDisplayOptions( opts );
  245. canvas()->SetHighContrastLayer( m_frame->GetActiveLayer() );
  246. return 0;
  247. }
  248. // Layer control
  249. int PCBNEW_CONTROL::LayerSwitch( const TOOL_EVENT& aEvent )
  250. {
  251. m_frame->SwitchLayer( NULL, aEvent.Parameter<PCB_LAYER_ID>() );
  252. return 0;
  253. }
  254. int PCBNEW_CONTROL::LayerNext( const TOOL_EVENT& aEvent )
  255. {
  256. PCB_BASE_FRAME* editFrame = m_frame;
  257. BOARD* brd = board();
  258. LAYER_NUM layer = editFrame->GetActiveLayer();
  259. LAYER_NUM startLayer = layer;
  260. if( layer < F_Cu || layer > B_Cu )
  261. return 0;
  262. while( startLayer != ++layer )
  263. {
  264. if( brd->IsLayerVisible( static_cast<PCB_LAYER_ID>( layer ) ) && IsCopperLayer( layer ) )
  265. break;
  266. if( layer >= B_Cu )
  267. layer = F_Cu - 1;
  268. }
  269. wxCHECK( IsCopperLayer( layer ), 0 );
  270. editFrame->SwitchLayer( NULL, ToLAYER_ID( layer ) );
  271. return 0;
  272. }
  273. int PCBNEW_CONTROL::LayerPrev( const TOOL_EVENT& aEvent )
  274. {
  275. PCB_BASE_FRAME* editFrame = m_frame;
  276. BOARD* brd = board();
  277. LAYER_NUM layer = editFrame->GetActiveLayer();
  278. LAYER_NUM startLayer = layer;
  279. if( layer < F_Cu || layer > B_Cu )
  280. return 0;
  281. while( startLayer != --layer )
  282. {
  283. if( IsCopperLayer( layer ) // also test for valid layer id (layer >= F_Cu)
  284. && brd->IsLayerVisible( static_cast<PCB_LAYER_ID>( layer ) ) )
  285. break;
  286. if( layer <= F_Cu )
  287. layer = B_Cu + 1;
  288. }
  289. wxCHECK( IsCopperLayer( layer ), 0 );
  290. editFrame->SwitchLayer( NULL, ToLAYER_ID( layer ) );
  291. return 0;
  292. }
  293. int PCBNEW_CONTROL::LayerToggle( const TOOL_EVENT& aEvent )
  294. {
  295. LAYER_NUM currentLayer = m_frame->GetActiveLayer();
  296. PCB_SCREEN* screen = m_frame->GetScreen();
  297. if( currentLayer == screen->m_Route_Layer_TOP )
  298. m_frame->SwitchLayer( NULL, screen->m_Route_Layer_BOTTOM );
  299. else
  300. m_frame->SwitchLayer( NULL, screen->m_Route_Layer_TOP );
  301. return 0;
  302. }
  303. // It'd be nice to share the min/max with the DIALOG_COLOR_PICKER, but those are
  304. // set in wxFormBuilder.
  305. #define ALPHA_MIN 0.20
  306. #define ALPHA_MAX 1.00
  307. #define ALPHA_STEP 0.05
  308. int PCBNEW_CONTROL::LayerAlphaInc( const TOOL_EVENT& aEvent )
  309. {
  310. auto settings = m_frame->ColorSettings();
  311. LAYER_NUM currentLayer = m_frame->GetActiveLayer();
  312. KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
  313. if( currentColor.a <= ALPHA_MAX - ALPHA_STEP )
  314. {
  315. currentColor.a += ALPHA_STEP;
  316. settings->SetColor( currentLayer, currentColor );
  317. m_frame->GetCanvas()->UpdateColors();
  318. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  319. view->UpdateLayerColor( currentLayer );
  320. wxUpdateUIEvent dummy;
  321. static_cast<PCB_EDIT_FRAME*>( m_frame )->OnUpdateLayerAlpha( dummy );
  322. }
  323. else
  324. wxBell();
  325. return 0;
  326. }
  327. int PCBNEW_CONTROL::LayerAlphaDec( const TOOL_EVENT& aEvent )
  328. {
  329. auto settings = m_frame->ColorSettings();
  330. LAYER_NUM currentLayer = m_frame->GetActiveLayer();
  331. KIGFX::COLOR4D currentColor = settings->GetColor( currentLayer );
  332. if( currentColor.a >= ALPHA_MIN + ALPHA_STEP )
  333. {
  334. currentColor.a -= ALPHA_STEP;
  335. settings->SetColor( currentLayer, currentColor );
  336. m_frame->GetCanvas()->UpdateColors();
  337. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  338. view->UpdateLayerColor( currentLayer );
  339. wxUpdateUIEvent dummy;
  340. static_cast<PCB_BASE_FRAME*>( m_frame )->OnUpdateLayerAlpha( dummy );
  341. }
  342. else
  343. wxBell();
  344. return 0;
  345. }
  346. // Grid control
  347. int PCBNEW_CONTROL::GridFast1( const TOOL_EVENT& aEvent )
  348. {
  349. m_frame->SetFastGrid1();
  350. updateGrid();
  351. return 0;
  352. }
  353. int PCBNEW_CONTROL::GridFast2( const TOOL_EVENT& aEvent )
  354. {
  355. m_frame->SetFastGrid2();
  356. updateGrid();
  357. return 0;
  358. }
  359. void PCBNEW_CONTROL::DoSetGridOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame,
  360. BOARD_ITEM* originViewItem, const VECTOR2D& aPoint )
  361. {
  362. aFrame->SetGridOrigin( wxPoint( aPoint.x, aPoint.y ) );
  363. aView->GetGAL()->SetGridOrigin( aPoint );
  364. originViewItem->SetPosition( wxPoint( aPoint.x, aPoint.y ) );
  365. aView->MarkDirty();
  366. aFrame->OnModify();
  367. }
  368. int PCBNEW_CONTROL::GridSetOrigin( const TOOL_EVENT& aEvent )
  369. {
  370. VECTOR2D* origin = aEvent.Parameter<VECTOR2D*>();
  371. if( origin )
  372. {
  373. // We can't undo the other grid dialog settings, so no sense undoing just the origin
  374. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), *origin );
  375. delete origin;
  376. }
  377. else
  378. {
  379. if( m_editModules && !getEditFrame<PCB_BASE_EDIT_FRAME>()->GetModel() )
  380. return 0;
  381. std::string tool = aEvent.GetCommandStr().get();
  382. PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool<PCBNEW_PICKER_TOOL>();
  383. // Deactivate other tools; particularly important if another PICKER is currently running
  384. Activate();
  385. picker->SetClickHandler(
  386. [this] ( const VECTOR2D& pt ) -> bool
  387. {
  388. m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UR_GRIDORIGIN );
  389. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), pt );
  390. return false; // drill origin is a one-shot; don't continue with tool
  391. } );
  392. m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool );
  393. }
  394. return 0;
  395. }
  396. int PCBNEW_CONTROL::GridResetOrigin( const TOOL_EVENT& aEvent )
  397. {
  398. m_frame->SaveCopyInUndoList( m_gridOrigin.get(), UR_GRIDORIGIN );
  399. DoSetGridOrigin( getView(), m_frame, m_gridOrigin.get(), VECTOR2D( 0, 0 ) );
  400. return 0;
  401. }
  402. #define HITTEST_THRESHOLD_PIXELS 5
  403. int PCBNEW_CONTROL::DeleteItemCursor( const TOOL_EVENT& aEvent )
  404. {
  405. if( m_editModules && !m_frame->GetBoard()->GetFirstModule() )
  406. return 0;
  407. std::string tool = aEvent.GetCommandStr().get();
  408. PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool<PCBNEW_PICKER_TOOL>();
  409. m_pickerItem = nullptr;
  410. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  411. // Deactivate other tools; particularly important if another PICKER is currently running
  412. Activate();
  413. picker->SetCursor( wxStockCursor( wxCURSOR_BULLSEYE ) );
  414. picker->SetClickHandler(
  415. [this] ( const VECTOR2D& aPosition ) -> bool
  416. {
  417. if( m_pickerItem )
  418. {
  419. if( m_pickerItem && m_pickerItem->IsLocked() )
  420. {
  421. STATUS_TEXT_POPUP statusPopup( m_frame );
  422. statusPopup.SetText( _( "Item locked." ) );
  423. statusPopup.PopupFor( 2000 );
  424. statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
  425. return true;
  426. }
  427. SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  428. selectionTool->UnbrightenItem( m_pickerItem );
  429. selectionTool->AddItemToSel( m_pickerItem, true /*quiet mode*/ );
  430. m_toolMgr->RunAction( ACTIONS::doDelete, true );
  431. m_pickerItem = nullptr;
  432. }
  433. return true;
  434. } );
  435. picker->SetMotionHandler(
  436. [this] ( const VECTOR2D& aPos )
  437. {
  438. BOARD* board = m_frame->GetBoard();
  439. SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  440. GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
  441. GENERAL_COLLECTOR collector;
  442. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  443. if( m_editModules )
  444. collector.Collect( board, GENERAL_COLLECTOR::ModuleItems, (wxPoint) aPos, guide );
  445. else
  446. collector.Collect( board, GENERAL_COLLECTOR::BoardLevelItems, (wxPoint) aPos, guide );
  447. // Remove unselectable items
  448. for( int i = collector.GetCount() - 1; i >= 0; --i )
  449. {
  450. if( !selectionTool->Selectable( collector[ i ] ) )
  451. collector.Remove( i );
  452. }
  453. if( collector.GetCount() > 1 )
  454. selectionTool->GuessSelectionCandidates( collector, aPos );
  455. BOARD_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
  456. if( m_pickerItem != item )
  457. {
  458. if( m_pickerItem )
  459. selectionTool->UnbrightenItem( m_pickerItem );
  460. m_pickerItem = item;
  461. if( m_pickerItem )
  462. selectionTool->BrightenItem( m_pickerItem );
  463. }
  464. } );
  465. picker->SetFinalizeHandler(
  466. [this] ( const int& aFinalState )
  467. {
  468. if( m_pickerItem )
  469. m_toolMgr->GetTool<SELECTION_TOOL>()->UnbrightenItem( m_pickerItem );
  470. } );
  471. m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool );
  472. return 0;
  473. }
  474. int PCBNEW_CONTROL::Paste( const TOOL_EVENT& aEvent )
  475. {
  476. CLIPBOARD_IO pi;
  477. BOARD_ITEM* clipItem = pi.Parse();
  478. if( !clipItem )
  479. return 0;
  480. if( clipItem->Type() == PCB_T )
  481. static_cast<BOARD*>( clipItem )->MapNets( m_frame->GetBoard() );
  482. bool editModules = m_editModules || frame()->IsType( FRAME_FOOTPRINT_EDITOR );
  483. // The clipboard can contain two different things, an entire kicad_pcb
  484. // or a single module
  485. if( editModules && ( !board() || !module() ) )
  486. {
  487. wxLogDebug( wxT( "Attempting to paste to empty module editor window\n") );
  488. return 0;
  489. }
  490. switch( clipItem->Type() )
  491. {
  492. case PCB_T:
  493. {
  494. BOARD* clipBoard = static_cast<BOARD*>( clipItem );
  495. if( editModules )
  496. {
  497. MODULE* editModule = board()->GetFirstModule();
  498. std::vector<BOARD_ITEM*> pastedItems;
  499. for( MODULE* clipModule : clipBoard->Modules() )
  500. {
  501. clipModule->SetParent( board() );
  502. for( auto pad : clipModule->Pads() )
  503. {
  504. pad->SetParent( editModule );
  505. pastedItems.push_back( pad );
  506. }
  507. clipModule->Pads().clear();
  508. for( auto item : clipModule->GraphicalItems() )
  509. {
  510. item->SetParent( editModule );
  511. pastedItems.push_back( item );
  512. }
  513. clipModule->GraphicalItems().clear();
  514. }
  515. for( BOARD_ITEM* clipDrawItem : clipBoard->Drawings() )
  516. {
  517. if( clipDrawItem->Type() == PCB_LINE_T )
  518. {
  519. DRAWSEGMENT* clipDrawSeg = static_cast<DRAWSEGMENT*>( clipDrawItem );
  520. // Convert to PCB_MODULE_EDGE_T
  521. EDGE_MODULE* pastedDrawSeg = new EDGE_MODULE( editModule );
  522. static_cast<DRAWSEGMENT*>( pastedDrawSeg )->SwapData( clipDrawSeg );
  523. pastedDrawSeg->SetLocalCoord();
  524. pastedItems.push_back( pastedDrawSeg );
  525. }
  526. else if( clipDrawItem->Type() == PCB_TEXT_T )
  527. {
  528. TEXTE_PCB* clipTextItem = static_cast<TEXTE_PCB*>( clipDrawItem );
  529. // Convert to PCB_MODULE_TEXT_T
  530. TEXTE_MODULE* pastedTextItem = new TEXTE_MODULE( editModule );
  531. static_cast<EDA_TEXT*>( pastedTextItem )->SwapText( *clipTextItem );
  532. static_cast<EDA_TEXT*>( pastedTextItem )->SwapEffects( *clipTextItem );
  533. pastedItems.push_back( pastedTextItem );
  534. }
  535. }
  536. delete clipBoard;
  537. placeBoardItems( pastedItems, true, true );
  538. }
  539. else
  540. {
  541. placeBoardItems( clipBoard, true );
  542. m_frame->Compile_Ratsnest( true );
  543. m_frame->GetBoard()->BuildConnectivity();
  544. }
  545. break;
  546. }
  547. case PCB_MODULE_T:
  548. {
  549. MODULE* clipModule = static_cast<MODULE*>( clipItem );
  550. std::vector<BOARD_ITEM*> pastedItems;
  551. if( editModules )
  552. {
  553. MODULE* editModule = board()->GetFirstModule();
  554. for( auto pad : clipModule->Pads() )
  555. {
  556. pad->SetParent( editModule );
  557. pastedItems.push_back( pad );
  558. }
  559. clipModule->Pads().clear();
  560. for( auto item : clipModule->GraphicalItems() )
  561. {
  562. item->SetParent( editModule );
  563. pastedItems.push_back( item );
  564. }
  565. clipModule->GraphicalItems().clear();
  566. delete clipModule;
  567. }
  568. else
  569. {
  570. clipModule->SetParent( board() );
  571. pastedItems.push_back( clipModule );
  572. }
  573. placeBoardItems( pastedItems, true, true );
  574. break;
  575. }
  576. default:
  577. m_frame->DisplayToolMsg( _( "Invalid clipboard contents" ) );
  578. break;
  579. }
  580. return 1;
  581. }
  582. int PCBNEW_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent )
  583. {
  584. int open_ctl;
  585. wxString fileName;
  586. PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  587. if( !editFrame )
  588. return 1;
  589. // Pick a file to append
  590. if( !AskLoadBoardFileName( editFrame, &open_ctl, &fileName, true ) )
  591. return 1;
  592. IO_MGR::PCB_FILE_T pluginType = plugin_type( fileName, open_ctl );
  593. PLUGIN::RELEASER pi( IO_MGR::PluginFind( pluginType ) );
  594. return AppendBoard( *pi, fileName );
  595. }
  596. // Helper function for PCBNEW_CONTROL::placeBoardItems()
  597. template<typename T>
  598. static void moveNoFlagToVector( std::deque<T>& aList, std::vector<BOARD_ITEM*>& aTarget, bool aIsNew )
  599. {
  600. std::copy_if( aList.begin(), aList.end(), std::back_inserter( aTarget ),
  601. [](T aItem)
  602. {
  603. bool retval = ( aItem->GetFlags() & FLAG0 ) == 0;
  604. aItem->ClearFlags( FLAG0 );
  605. return retval;
  606. } );
  607. if( aIsNew )
  608. aList.clear();
  609. }
  610. static void moveNoFlagToVector( ZONE_CONTAINERS& aList, std::vector<BOARD_ITEM*>& aTarget, bool aIsNew )
  611. {
  612. if( aList.size() == 0 )
  613. return;
  614. auto obj = aList.front();
  615. int idx = 0;
  616. if( aIsNew )
  617. {
  618. obj = aList.back();
  619. aList.pop_back();
  620. }
  621. for( ; obj ; )
  622. {
  623. if( obj->HasFlag( FLAG0 ) )
  624. obj->ClearFlags( FLAG0 );
  625. else
  626. aTarget.push_back( obj );
  627. if( aIsNew )
  628. {
  629. if( aList.size() )
  630. {
  631. obj = aList.back();
  632. aList.pop_back();
  633. }
  634. else
  635. obj = nullptr;
  636. }
  637. else
  638. obj = idx < int(aList.size()-1) ? aList[++idx] : nullptr;
  639. }
  640. }
  641. int PCBNEW_CONTROL::placeBoardItems( BOARD* aBoard, bool aAnchorAtOrigin )
  642. {
  643. // items are new if the current board is not the board source
  644. bool isNew = board() != aBoard;
  645. std::vector<BOARD_ITEM*> items;
  646. moveNoFlagToVector( aBoard->Tracks(), items, isNew );
  647. moveNoFlagToVector( aBoard->Modules(), items, isNew );
  648. moveNoFlagToVector( aBoard->Drawings(), items, isNew );
  649. moveNoFlagToVector( aBoard->Zones(), items, isNew );
  650. return placeBoardItems( items, isNew, aAnchorAtOrigin );
  651. }
  652. int PCBNEW_CONTROL::placeBoardItems( std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
  653. bool aAnchorAtOrigin )
  654. {
  655. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  656. auto selectionTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  657. auto editTool = m_toolMgr->GetTool<EDIT_TOOL>();
  658. PCBNEW_SELECTION& selection = selectionTool->GetSelection();
  659. for( auto item : aItems )
  660. {
  661. item->SetSelected();
  662. selection.Add( item );
  663. // Add or just select items for the move/place command
  664. if( aIsNew )
  665. editTool->GetCurrentCommit()->Add( item );
  666. else
  667. editTool->GetCurrentCommit()->Added( item );
  668. }
  669. if( selection.Size() > 0 )
  670. {
  671. if( aAnchorAtOrigin )
  672. {
  673. selection.SetReferencePoint( VECTOR2I( 0, 0 ) );
  674. }
  675. else
  676. {
  677. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.GetTopLeftItem() );
  678. selection.SetReferencePoint( item->GetPosition() );
  679. }
  680. getViewControls()->SetCursorPosition( getViewControls()->GetMousePosition(), false );
  681. m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
  682. m_toolMgr->RunAction( PCB_ACTIONS::move, true );
  683. }
  684. return 0;
  685. }
  686. int PCBNEW_CONTROL::AppendBoard( PLUGIN& pi, wxString& fileName )
  687. {
  688. PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
  689. if( !editFrame )
  690. return 1;
  691. BOARD* brd = board();
  692. if( !brd )
  693. return 1;
  694. // Mark existing items, in order to know what are the new items so we can select only
  695. // the new items after loading
  696. for( auto track : brd->Tracks() )
  697. track->SetFlags( FLAG0 );
  698. for( auto module : brd->Modules() )
  699. module->SetFlags( FLAG0 );
  700. for( auto drawing : brd->Drawings() )
  701. drawing->SetFlags( FLAG0 );
  702. for( auto zone : brd->Zones() )
  703. zone->SetFlags( FLAG0 );
  704. // Keep also the count of copper layers, to adjust if necessary
  705. int initialCopperLayerCount = brd->GetCopperLayerCount();
  706. LSET initialEnabledLayers = brd->GetEnabledLayers();
  707. // Load the data
  708. try
  709. {
  710. PROPERTIES props;
  711. char xbuf[30];
  712. char ybuf[30];
  713. // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet.
  714. sprintf( xbuf, "%d", editFrame->GetPageSizeIU().x );
  715. sprintf( ybuf, "%d", editFrame->GetPageSizeIU().y );
  716. props["page_width"] = xbuf;
  717. props["page_height"] = ybuf;
  718. editFrame->GetDesignSettings().m_NetClasses.Clear();
  719. pi.Load( fileName, brd, &props );
  720. }
  721. catch( const IO_ERROR& ioe )
  722. {
  723. wxString msg = wxString::Format( _( "Error loading board.\n%s" ), GetChars( ioe.What() ));
  724. DisplayError( editFrame, msg );
  725. return 0;
  726. }
  727. // rebuild nets and ratsnest before any use of nets
  728. brd->BuildListOfNets();
  729. brd->SynchronizeNetsAndNetClasses();
  730. brd->BuildConnectivity();
  731. // Synchronize layers
  732. // we should not ask PLUGINs to do these items:
  733. int copperLayerCount = brd->GetCopperLayerCount();
  734. if( copperLayerCount > initialCopperLayerCount )
  735. brd->SetCopperLayerCount( copperLayerCount );
  736. // Enable all used layers, and make them visible:
  737. LSET enabledLayers = brd->GetEnabledLayers();
  738. enabledLayers |= initialEnabledLayers;
  739. brd->SetEnabledLayers( enabledLayers );
  740. brd->SetVisibleLayers( enabledLayers );
  741. return placeBoardItems( brd, false );
  742. }
  743. int PCBNEW_CONTROL::Undo( const TOOL_EVENT& aEvent )
  744. {
  745. PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
  746. wxCommandEvent dummy;
  747. if( editFrame )
  748. editFrame->RestoreCopyFromUndoList( dummy );
  749. return 0;
  750. }
  751. int PCBNEW_CONTROL::Redo( const TOOL_EVENT& aEvent )
  752. {
  753. PCB_BASE_EDIT_FRAME* editFrame = dynamic_cast<PCB_BASE_EDIT_FRAME*>( m_frame );
  754. wxCommandEvent dummy;
  755. if( editFrame )
  756. editFrame->RestoreCopyFromRedoList( dummy );
  757. return 0;
  758. }
  759. int PCBNEW_CONTROL::Show3DViewer( const TOOL_EVENT& aEvent )
  760. {
  761. EDA_3D_VIEWER* draw3DFrame = m_frame->CreateAndShow3D_Frame();
  762. // Suppress warnings on non-Mac systems
  763. [&draw3DFrame] {}();
  764. if( m_frame->IsType( FRAME_FOOTPRINT_VIEWER )
  765. || m_frame->IsType( FRAME_FOOTPRINT_VIEWER_MODAL )
  766. || m_frame->IsType( FRAME_FOOTPRINT_WIZARD ) )
  767. {
  768. m_frame->Update3DView( true );
  769. #ifdef __WXMAC__
  770. // A stronger version of Raise() which promotes the window to its parent's level.
  771. draw3DFrame->ReparentQuasiModal();
  772. #endif
  773. }
  774. return 0;
  775. }
  776. void PCBNEW_CONTROL::updateGrid()
  777. {
  778. BASE_SCREEN* screen = m_frame->GetScreen();
  779. getView()->GetGAL()->SetGridSize( VECTOR2D( screen->GetGridSize() ) );
  780. getView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
  781. }
  782. int PCBNEW_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
  783. {
  784. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  785. PCBNEW_SELECTION& selection = selTool->GetSelection();
  786. if( selection.GetSize() == 1 )
  787. {
  788. EDA_ITEM* item = selection.Front();
  789. MSG_PANEL_ITEMS msgItems;
  790. item->GetMsgPanelInfo( m_frame->GetUserUnits(), msgItems );
  791. m_frame->SetMsgPanel( msgItems );
  792. }
  793. else if( selection.GetSize() > 1 )
  794. {
  795. MSG_PANEL_ITEMS msgItems;
  796. wxString msg = wxString::Format( wxT( "%d" ), selection.GetSize() );
  797. msgItems.emplace_back( MSG_PANEL_ITEM( _( "Selected Items" ), msg, DARKCYAN ) );
  798. m_frame->SetMsgPanel( msgItems );
  799. }
  800. else if( auto editFrame = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame ) )
  801. {
  802. MODULE* footprint = static_cast<MODULE*>( editFrame->GetModel() );
  803. if( !footprint )
  804. return 0;
  805. MSG_PANEL_ITEMS msgItems;
  806. wxString msg;
  807. msg = footprint->GetFPID().GetLibNickname().wx_str();
  808. msgItems.emplace_back( MSG_PANEL_ITEM( _( "Library" ), msg, DARKCYAN ) );
  809. msg = footprint->GetFPID().GetLibItemName().wx_str();
  810. msgItems.emplace_back( MSG_PANEL_ITEM( _( "Footprint Name" ), msg, DARKCYAN ) );
  811. wxDateTime date( static_cast<time_t>( footprint->GetLastEditTime() ) );
  812. if( footprint->GetLastEditTime() && date.IsValid() )
  813. // Date format: see http://www.cplusplus.com/reference/ctime/strftime
  814. msg = date.Format( wxT( "%b %d, %Y" ) ); // Abbreviated_month_name Day, Year
  815. else
  816. msg = _( "Unknown" );
  817. msgItems.emplace_back( MSG_PANEL_ITEM( _( "Last Change" ), msg, BROWN ) );
  818. msg.Printf( wxT( "%zu" ), (size_t) footprint->GetPadCount( DO_NOT_INCLUDE_NPTH ) );
  819. msgItems.emplace_back( MSG_PANEL_ITEM( _( "Pads" ), msg, BLUE ) );
  820. wxString doc, keyword;
  821. doc.Printf( _( "Doc: %s" ), footprint->GetDescription() );
  822. keyword.Printf( _( "Key Words: %s" ), footprint->GetKeywords() );
  823. msgItems.emplace_back( MSG_PANEL_ITEM( doc, keyword, BLACK ) );
  824. m_frame->SetMsgPanel( msgItems );
  825. }
  826. else
  827. {
  828. m_frame->SetMsgPanel( m_frame->GetBoard() );
  829. }
  830. return 0;
  831. }
  832. void PCBNEW_CONTROL::setTransitions()
  833. {
  834. Go( &PCBNEW_CONTROL::AddLibrary, ACTIONS::newLibrary.MakeEvent() );
  835. Go( &PCBNEW_CONTROL::AddLibrary, ACTIONS::addLibrary.MakeEvent() );
  836. Go( &PCBNEW_CONTROL::Print, ACTIONS::print.MakeEvent() );
  837. Go( &PCBNEW_CONTROL::Quit, ACTIONS::quit.MakeEvent() );
  838. // Display modes
  839. Go( &PCBNEW_CONTROL::TrackDisplayMode, PCB_ACTIONS::trackDisplayMode.MakeEvent() );
  840. Go( &PCBNEW_CONTROL::ToggleRatsnest, PCB_ACTIONS::showRatsnest.MakeEvent() );
  841. Go( &PCBNEW_CONTROL::ToggleRatsnest, PCB_ACTIONS::ratsnestLineMode.MakeEvent() );
  842. Go( &PCBNEW_CONTROL::PadDisplayMode, PCB_ACTIONS::padDisplayMode.MakeEvent() );
  843. Go( &PCBNEW_CONTROL::ViaDisplayMode, PCB_ACTIONS::viaDisplayMode.MakeEvent() );
  844. Go( &PCBNEW_CONTROL::GraphicDisplayMode, PCB_ACTIONS::graphicDisplayMode.MakeEvent() );
  845. Go( &PCBNEW_CONTROL::ModuleEdgeOutlines, PCB_ACTIONS::moduleEdgeOutlines.MakeEvent() );
  846. Go( &PCBNEW_CONTROL::ModuleTextOutlines, PCB_ACTIONS::moduleTextOutlines.MakeEvent() );
  847. Go( &PCBNEW_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayEnable.MakeEvent() );
  848. Go( &PCBNEW_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayDisable.MakeEvent() );
  849. Go( &PCBNEW_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayOutlines.MakeEvent() );
  850. Go( &PCBNEW_CONTROL::ZoneDisplayMode, PCB_ACTIONS::zoneDisplayToggle.MakeEvent() );
  851. Go( &PCBNEW_CONTROL::HighContrastMode, ACTIONS::highContrastMode.MakeEvent() );
  852. // Layer control
  853. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerTop.MakeEvent() );
  854. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner1.MakeEvent() );
  855. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner2.MakeEvent() );
  856. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner3.MakeEvent() );
  857. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner4.MakeEvent() );
  858. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner5.MakeEvent() );
  859. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner6.MakeEvent() );
  860. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner7.MakeEvent() );
  861. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner8.MakeEvent() );
  862. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner9.MakeEvent() );
  863. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner10.MakeEvent() );
  864. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner11.MakeEvent() );
  865. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner12.MakeEvent() );
  866. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner13.MakeEvent() );
  867. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner14.MakeEvent() );
  868. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner15.MakeEvent() );
  869. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner16.MakeEvent() );
  870. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner17.MakeEvent() );
  871. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner18.MakeEvent() );
  872. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner19.MakeEvent() );
  873. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner20.MakeEvent() );
  874. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner21.MakeEvent() );
  875. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner22.MakeEvent() );
  876. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner23.MakeEvent() );
  877. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner24.MakeEvent() );
  878. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner25.MakeEvent() );
  879. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner26.MakeEvent() );
  880. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner27.MakeEvent() );
  881. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner28.MakeEvent() );
  882. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner29.MakeEvent() );
  883. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerInner30.MakeEvent() );
  884. Go( &PCBNEW_CONTROL::LayerSwitch, PCB_ACTIONS::layerBottom.MakeEvent() );
  885. Go( &PCBNEW_CONTROL::LayerNext, PCB_ACTIONS::layerNext.MakeEvent() );
  886. Go( &PCBNEW_CONTROL::LayerPrev, PCB_ACTIONS::layerPrev.MakeEvent() );
  887. Go( &PCBNEW_CONTROL::LayerToggle, PCB_ACTIONS::layerToggle.MakeEvent() );
  888. Go( &PCBNEW_CONTROL::LayerAlphaInc, PCB_ACTIONS::layerAlphaInc.MakeEvent() );
  889. Go( &PCBNEW_CONTROL::LayerAlphaDec, PCB_ACTIONS::layerAlphaDec.MakeEvent() );
  890. // Grid control
  891. Go( &PCBNEW_CONTROL::GridFast1, ACTIONS::gridFast1.MakeEvent() );
  892. Go( &PCBNEW_CONTROL::GridFast2, ACTIONS::gridFast2.MakeEvent() );
  893. Go( &PCBNEW_CONTROL::GridSetOrigin, ACTIONS::gridSetOrigin.MakeEvent() );
  894. Go( &PCBNEW_CONTROL::GridResetOrigin, ACTIONS::gridResetOrigin.MakeEvent() );
  895. Go( &PCBNEW_CONTROL::Undo, ACTIONS::undo.MakeEvent() );
  896. Go( &PCBNEW_CONTROL::Redo, ACTIONS::redo.MakeEvent() );
  897. // Miscellaneous
  898. Go( &PCBNEW_CONTROL::DeleteItemCursor, ACTIONS::deleteTool.MakeEvent() );
  899. Go( &PCBNEW_CONTROL::Show3DViewer, ACTIONS::show3DViewer.MakeEvent() );
  900. // Append control
  901. Go( &PCBNEW_CONTROL::AppendBoardFromFile, PCB_ACTIONS::appendBoard.MakeEvent() );
  902. Go( &PCBNEW_CONTROL::Paste, ACTIONS::paste.MakeEvent() );
  903. Go( &PCBNEW_CONTROL::UpdateMessagePanel, EVENTS::SelectedEvent );
  904. Go( &PCBNEW_CONTROL::UpdateMessagePanel, EVENTS::UnselectedEvent );
  905. Go( &PCBNEW_CONTROL::UpdateMessagePanel, EVENTS::ClearedEvent );
  906. Go( &PCBNEW_CONTROL::UpdateMessagePanel, EVENTS::SelectedItemsModified );
  907. }