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.

806 lines
25 KiB

5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2019 CERN
  5. * Copyright (C) 2019-2022 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 "footprint_editor_control.h"
  26. #include <wx/generic/textdlgg.h>
  27. #include <string_utils.h>
  28. #include <pgm_base.h>
  29. #include <tool/tool_manager.h>
  30. #include <tools/pcb_actions.h>
  31. #include <footprint_edit_frame.h>
  32. #include <pcbnew_id.h>
  33. #include <confirm.h>
  34. #include <widgets/wx_infobar.h>
  35. #include <footprint.h>
  36. #include <pad.h>
  37. #include <pcb_group.h>
  38. #include <zone.h>
  39. #include <fp_lib_table.h>
  40. #include <dialogs/dialog_cleanup_graphics.h>
  41. #include <dialogs/dialog_footprint_checker.h>
  42. #include <footprint_wizard_frame.h>
  43. #include <kiway.h>
  44. #include <drc/drc_item.h>
  45. #include <project_pcb.h>
  46. #include <memory>
  47. FOOTPRINT_EDITOR_CONTROL::FOOTPRINT_EDITOR_CONTROL() :
  48. PCB_TOOL_BASE( "pcbnew.ModuleEditor" ),
  49. m_frame( nullptr ),
  50. m_checkerDialog( nullptr )
  51. {
  52. }
  53. FOOTPRINT_EDITOR_CONTROL::~FOOTPRINT_EDITOR_CONTROL()
  54. {
  55. }
  56. void FOOTPRINT_EDITOR_CONTROL::Reset( RESET_REASON aReason )
  57. {
  58. m_frame = getEditFrame<FOOTPRINT_EDIT_FRAME>();
  59. if( m_checkerDialog )
  60. DestroyCheckerDialog();
  61. }
  62. bool FOOTPRINT_EDITOR_CONTROL::Init()
  63. {
  64. // Build a context menu for the footprint tree
  65. //
  66. CONDITIONAL_MENU& ctxMenu = m_menu.GetMenu();
  67. auto libSelectedCondition =
  68. [ this ]( const SELECTION& aSel )
  69. {
  70. LIB_ID sel = m_frame->GetTreeFPID();
  71. return !sel.GetLibNickname().empty() && sel.GetLibItemName().empty();
  72. };
  73. // The libInferredCondition allows you to do things like New Symbol and Paste with a
  74. // symbol selected (in other words, when we know the library context even if the library
  75. // itself isn't selected.
  76. auto libInferredCondition =
  77. [ this ]( const SELECTION& aSel )
  78. {
  79. LIB_ID sel = m_frame->GetTreeFPID();
  80. return !sel.GetLibNickname().empty();
  81. };
  82. auto pinnedLibSelectedCondition =
  83. [ this ]( const SELECTION& aSel )
  84. {
  85. LIB_TREE_NODE* current = m_frame->GetCurrentTreeNode();
  86. return current && current->m_Type == LIB_TREE_NODE::LIB && current->m_Pinned;
  87. };
  88. auto unpinnedLibSelectedCondition =
  89. [ this ](const SELECTION& aSel )
  90. {
  91. LIB_TREE_NODE* current = m_frame->GetCurrentTreeNode();
  92. return current && current->m_Type == LIB_TREE_NODE::LIB && !current->m_Pinned;
  93. };
  94. auto fpSelectedCondition =
  95. [ this ]( const SELECTION& aSel )
  96. {
  97. LIB_ID sel = m_frame->GetTreeFPID();
  98. return !sel.GetLibNickname().empty() && !sel.GetLibItemName().empty();
  99. };
  100. auto fpExportCondition =
  101. [ this ]( const SELECTION& aSel )
  102. {
  103. FOOTPRINT* fp = m_frame->GetBoard()->GetFirstFootprint();
  104. return fp != nullptr;
  105. };
  106. ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLibSelectedCondition );
  107. ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLibSelectedCondition );
  108. ctxMenu.AddSeparator();
  109. ctxMenu.AddItem( PCB_ACTIONS::newFootprint, libSelectedCondition );
  110. ctxMenu.AddItem( PCB_ACTIONS::createFootprint, libSelectedCondition );
  111. ctxMenu.AddSeparator();
  112. ctxMenu.AddItem( ACTIONS::save, SELECTION_CONDITIONS::ShowAlways );
  113. ctxMenu.AddItem( ACTIONS::saveAs, libSelectedCondition || fpSelectedCondition );
  114. ctxMenu.AddItem( ACTIONS::revert, libSelectedCondition || libInferredCondition );
  115. ctxMenu.AddSeparator();
  116. ctxMenu.AddItem( PCB_ACTIONS::cutFootprint, fpSelectedCondition );
  117. ctxMenu.AddItem( PCB_ACTIONS::copyFootprint, fpSelectedCondition );
  118. ctxMenu.AddItem( PCB_ACTIONS::pasteFootprint, libInferredCondition );
  119. ctxMenu.AddItem( PCB_ACTIONS::duplicateFootprint, fpSelectedCondition );
  120. ctxMenu.AddItem( PCB_ACTIONS::renameFootprint, fpSelectedCondition );
  121. ctxMenu.AddItem( PCB_ACTIONS::deleteFootprint, fpSelectedCondition );
  122. ctxMenu.AddSeparator();
  123. ctxMenu.AddItem( PCB_ACTIONS::importFootprint, libInferredCondition );
  124. ctxMenu.AddItem( PCB_ACTIONS::exportFootprint, fpExportCondition );
  125. // If we've got nothing else to show, at least show a hide tree option
  126. ctxMenu.AddItem( PCB_ACTIONS::hideFootprintTree, !libInferredCondition );
  127. return true;
  128. }
  129. int FOOTPRINT_EDITOR_CONTROL::NewFootprint( const TOOL_EVENT& aEvent )
  130. {
  131. LIB_ID selected = m_frame->GetTreeFPID();
  132. wxString libraryName = selected.GetUniStringLibNickname();
  133. FOOTPRINT* newFootprint = m_frame->CreateNewFootprint( wxEmptyString, libraryName, false );
  134. if( !newFootprint )
  135. return 0;
  136. if( !m_frame->Clear_Pcb( true ) )
  137. return 0;
  138. canvas()->GetViewControls()->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false );
  139. m_frame->AddFootprintToBoard( newFootprint );
  140. // Initialize data relative to nets and netclasses (for a new footprint the defaults are
  141. // used). This is mandatory to handle and draw pads.
  142. board()->BuildListOfNets();
  143. newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
  144. newFootprint->ClearFlags();
  145. m_frame->Zoom_Automatique( false );
  146. m_frame->GetScreen()->SetContentModified();
  147. // If selected from the library tree then go ahead and save it there
  148. if( !selected.GetLibNickname().empty() )
  149. {
  150. LIB_ID fpid = newFootprint->GetFPID();
  151. fpid.SetLibNickname( selected.GetLibNickname() );
  152. newFootprint->SetFPID( fpid );
  153. m_frame->SaveFootprint( newFootprint );
  154. m_frame->ClearModify();
  155. }
  156. m_frame->UpdateView();
  157. m_frame->Update3DView( true, true );
  158. m_frame->SyncLibraryTree( false );
  159. return 0;
  160. }
  161. int FOOTPRINT_EDITOR_CONTROL::CreateFootprint( const TOOL_EVENT& aEvent )
  162. {
  163. LIB_ID selected = m_frame->GetTreeFPID();
  164. if( m_frame->IsContentModified() )
  165. {
  166. if( !HandleUnsavedChanges( m_frame, _( "The current footprint has been modified. "
  167. "Save changes?" ),
  168. [&]() -> bool
  169. {
  170. return m_frame->SaveFootprint( footprint() );
  171. } ) )
  172. {
  173. return 0;
  174. }
  175. }
  176. auto* wizard = (FOOTPRINT_WIZARD_FRAME*) m_frame->Kiway().Player( FRAME_FOOTPRINT_WIZARD,
  177. true, m_frame );
  178. if( wizard->ShowModal( nullptr, m_frame ) )
  179. {
  180. // Creates the new footprint from python script wizard
  181. FOOTPRINT* newFootprint = wizard->GetBuiltFootprint();
  182. if( newFootprint ) // i.e. if create footprint command is OK
  183. {
  184. m_frame->Clear_Pcb( false );
  185. canvas()->GetViewControls()->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false );
  186. // Add the new object to board
  187. m_frame->AddFootprintToBoard( newFootprint );
  188. // Initialize data relative to nets and netclasses (for a new footprint the defaults
  189. // are used). This is mandatory to handle and draw pads.
  190. board()->BuildListOfNets();
  191. newFootprint->SetPosition( VECTOR2I( 0, 0 ) );
  192. newFootprint->ClearFlags();
  193. m_frame->Zoom_Automatique( false );
  194. m_frame->GetScreen()->SetContentModified();
  195. m_frame->OnModify();
  196. // If selected from the library tree then go ahead and save it there
  197. if( !selected.GetLibNickname().empty() )
  198. {
  199. LIB_ID fpid = newFootprint->GetFPID();
  200. fpid.SetLibNickname( selected.GetLibNickname() );
  201. newFootprint->SetFPID( fpid );
  202. m_frame->SaveFootprint( newFootprint );
  203. m_frame->ClearModify();
  204. }
  205. m_frame->UpdateView();
  206. canvas()->Refresh();
  207. m_frame->Update3DView( true, true );
  208. m_frame->SyncLibraryTree( false );
  209. }
  210. }
  211. wizard->Destroy();
  212. return 0;
  213. }
  214. int FOOTPRINT_EDITOR_CONTROL::Save( const TOOL_EVENT& aEvent )
  215. {
  216. if( !footprint() ) // no loaded footprint
  217. return 0;
  218. if( m_frame->GetTargetFPID() == m_frame->GetLoadedFPID() )
  219. {
  220. if( m_frame->SaveFootprint( footprint() ) )
  221. {
  222. view()->Update( footprint() );
  223. canvas()->ForceRefresh();
  224. m_frame->ClearModify();
  225. m_frame->UpdateTitle();
  226. }
  227. }
  228. m_frame->RefreshLibraryTree();
  229. return 0;
  230. }
  231. int FOOTPRINT_EDITOR_CONTROL::SaveAs( const TOOL_EVENT& aEvent )
  232. {
  233. if( m_frame->GetTargetFPID().GetLibItemName().empty() )
  234. {
  235. // Save Library As
  236. const wxString& src_libNickname = m_frame->GetTargetFPID().GetLibNickname();
  237. wxString src_libFullName = PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() )->GetFullURI( src_libNickname );
  238. if( m_frame->SaveLibraryAs( src_libFullName ) )
  239. m_frame->SyncLibraryTree( true );
  240. }
  241. else if( m_frame->GetTargetFPID() == m_frame->GetLoadedFPID() )
  242. {
  243. // Save Footprint As
  244. if( footprint() && m_frame->SaveFootprintAs( footprint() ) )
  245. {
  246. view()->Update( footprint() );
  247. m_frame->ClearModify();
  248. // Get rid of the save-will-update-board-only (or any other dismissable warning)
  249. WX_INFOBAR* infobar = m_frame->GetInfoBar();
  250. if( infobar->IsShown() && infobar->HasCloseButton() )
  251. infobar->Dismiss();
  252. canvas()->ForceRefresh();
  253. m_frame->SyncLibraryTree( true );
  254. }
  255. }
  256. else
  257. {
  258. // Save Selected Footprint As
  259. FOOTPRINT* footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
  260. if( footprint && m_frame->SaveFootprintAs( footprint ) )
  261. {
  262. m_frame->SyncLibraryTree( true );
  263. m_frame->FocusOnLibID( footprint->GetFPID() );
  264. }
  265. }
  266. m_frame->RefreshLibraryTree();
  267. return 0;
  268. }
  269. int FOOTPRINT_EDITOR_CONTROL::Revert( const TOOL_EVENT& aEvent )
  270. {
  271. getEditFrame<FOOTPRINT_EDIT_FRAME>()->RevertFootprint();
  272. return 0;
  273. }
  274. int FOOTPRINT_EDITOR_CONTROL::CutCopyFootprint( const TOOL_EVENT& aEvent )
  275. {
  276. LIB_ID fpID = m_frame->GetTreeFPID();
  277. if( fpID == m_frame->GetLoadedFPID() )
  278. {
  279. m_copiedFootprint = std::make_unique<FOOTPRINT>( *m_frame->GetBoard()->GetFirstFootprint() );
  280. m_copiedFootprint->SetParent( nullptr );
  281. }
  282. else
  283. {
  284. m_copiedFootprint.reset( m_frame->LoadFootprint( fpID ) );
  285. }
  286. if( aEvent.IsAction( &PCB_ACTIONS::cutFootprint ) )
  287. DeleteFootprint( aEvent );
  288. return 0;
  289. }
  290. int FOOTPRINT_EDITOR_CONTROL::PasteFootprint( const TOOL_EVENT& aEvent )
  291. {
  292. if( m_copiedFootprint && !m_frame->GetTreeFPID().GetLibNickname().empty() )
  293. {
  294. wxString newLib = m_frame->GetTreeFPID().GetLibNickname();
  295. wxString newName = m_copiedFootprint->GetFPID().GetLibItemName();
  296. while( PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() )->FootprintExists( newLib, newName ) )
  297. newName += _( "_copy" );
  298. m_copiedFootprint->SetFPID( LIB_ID( newLib, newName ) );
  299. m_frame->SaveFootprintInLibrary( m_copiedFootprint.get(), newLib );
  300. m_frame->SyncLibraryTree( true );
  301. m_frame->LoadFootprintFromLibrary( m_copiedFootprint->GetFPID() );
  302. m_frame->FocusOnLibID( m_copiedFootprint->GetFPID() );
  303. m_frame->RefreshLibraryTree();
  304. }
  305. return 0;
  306. }
  307. int FOOTPRINT_EDITOR_CONTROL::DuplicateFootprint( const TOOL_EVENT& aEvent )
  308. {
  309. LIB_ID fpID = m_frame->GetTreeFPID();
  310. FOOTPRINT* footprint;
  311. if( fpID == m_frame->GetLoadedFPID() )
  312. footprint = new FOOTPRINT( *m_frame->GetBoard()->GetFirstFootprint() );
  313. else
  314. footprint = m_frame->LoadFootprint( m_frame->GetTargetFPID() );
  315. if( footprint && m_frame->DuplicateFootprint( footprint ) )
  316. {
  317. m_frame->SyncLibraryTree( true );
  318. m_frame->LoadFootprintFromLibrary( footprint->GetFPID() );
  319. m_frame->FocusOnLibID( footprint->GetFPID() );
  320. m_frame->RefreshLibraryTree();
  321. }
  322. return 0;
  323. }
  324. class RENAME_DIALOG : public wxTextEntryDialog
  325. {
  326. public:
  327. RENAME_DIALOG( wxWindow* aParent, const wxString& aName,
  328. std::function<bool( wxString newName )> aValidator ) :
  329. wxTextEntryDialog( aParent, _( "New name:" ), _( "Change Footprint Name" ), aName ),
  330. m_validator( std::move( aValidator ) )
  331. { }
  332. wxString GetFPName()
  333. {
  334. wxString name = m_textctrl->GetValue();
  335. name.Trim( true ).Trim( false );
  336. return name;
  337. }
  338. protected:
  339. bool TransferDataFromWindow() override
  340. {
  341. return m_validator( GetFPName() );
  342. }
  343. private:
  344. std::function<bool( wxString newName )> m_validator;
  345. };
  346. int FOOTPRINT_EDITOR_CONTROL::RenameFootprint( const TOOL_EVENT& aEvent )
  347. {
  348. FP_LIB_TABLE* tbl = PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() );
  349. LIB_ID fpID = m_frame->GetTreeFPID();
  350. wxString libraryName = fpID.GetLibNickname();
  351. wxString oldName = fpID.GetLibItemName();
  352. wxString msg;
  353. RENAME_DIALOG dlg( m_frame, oldName,
  354. [&]( wxString newName )
  355. {
  356. if( newName.IsEmpty() )
  357. {
  358. wxMessageBox( _( "Footprint must have a name." ) );
  359. return false;
  360. }
  361. if( tbl->FootprintExists( libraryName, newName ) )
  362. {
  363. msg = wxString::Format( _( "Footprint '%s' already exists in library '%s'." ),
  364. newName, libraryName );
  365. KIDIALOG errorDlg( m_frame, msg, _( "Confirmation" ),
  366. wxOK | wxCANCEL | wxICON_WARNING );
  367. errorDlg.SetOKLabel( _( "Overwrite" ) );
  368. return errorDlg.ShowModal() == wxID_OK;
  369. }
  370. return true;
  371. } );
  372. if( dlg.ShowModal() != wxID_OK )
  373. return 0; // canceled by user
  374. wxString newName = dlg.GetFPName();
  375. FOOTPRINT* footprint = nullptr;
  376. if( fpID == m_frame->GetLoadedFPID() )
  377. {
  378. footprint = m_frame->GetBoard()->GetFirstFootprint();
  379. if( footprint )
  380. {
  381. footprint->SetFPID( LIB_ID( libraryName, newName ) );
  382. if( footprint->GetValue() == oldName )
  383. footprint->SetValue( newName );
  384. m_frame->OnModify();
  385. m_frame->UpdateView();
  386. }
  387. }
  388. else
  389. {
  390. footprint = m_frame->LoadFootprint( fpID );
  391. if( footprint )
  392. {
  393. try
  394. {
  395. footprint->SetFPID( LIB_ID( libraryName, newName ) );
  396. if( footprint->GetValue() == oldName )
  397. footprint->SetValue( newName );
  398. m_frame->SaveFootprintInLibrary( footprint, libraryName );
  399. PROJECT_PCB::PcbFootprintLibs( &m_frame->Prj() )->FootprintDelete( libraryName, oldName );
  400. }
  401. catch( const IO_ERROR& ioe )
  402. {
  403. DisplayError( m_frame, ioe.What() );
  404. }
  405. catch( ... )
  406. {
  407. // Best efforts...
  408. }
  409. }
  410. }
  411. wxDataViewItem treeItem = m_frame->GetLibTreeAdapter()->FindItem( fpID );
  412. m_frame->UpdateLibraryTree( treeItem, footprint );
  413. m_frame->FocusOnLibID( LIB_ID( libraryName, newName ) );
  414. return 0;
  415. }
  416. int FOOTPRINT_EDITOR_CONTROL::DeleteFootprint( const TOOL_EVENT& aEvent )
  417. {
  418. FOOTPRINT_EDIT_FRAME* frame = getEditFrame<FOOTPRINT_EDIT_FRAME>();
  419. if( frame->DeleteFootprintFromLibrary( frame->GetTargetFPID(), true ) )
  420. {
  421. if( frame->GetTargetFPID() == frame->GetLoadedFPID() )
  422. frame->Clear_Pcb( false );
  423. frame->SyncLibraryTree( true );
  424. }
  425. return 0;
  426. }
  427. int FOOTPRINT_EDITOR_CONTROL::ImportFootprint( const TOOL_EVENT& aEvent )
  428. {
  429. bool is_last_fp_from_brd = m_frame->IsCurrentFPFromBoard();
  430. if( !m_frame->Clear_Pcb( true ) )
  431. return -1; // this command is aborted
  432. getViewControls()->SetCrossHairCursorPosition( VECTOR2D( 0, 0 ), false );
  433. m_frame->ImportFootprint();
  434. if( m_frame->GetBoard()->GetFirstFootprint() )
  435. m_frame->GetBoard()->GetFirstFootprint()->ClearFlags();
  436. frame()->ClearUndoRedoList();
  437. // Update the save items if needed.
  438. if( is_last_fp_from_brd )
  439. {
  440. m_frame->ReCreateMenuBar();
  441. m_frame->ReCreateHToolbar();
  442. }
  443. m_toolMgr->RunAction( ACTIONS::zoomFitScreen );
  444. m_frame->OnModify();
  445. return 0;
  446. }
  447. int FOOTPRINT_EDITOR_CONTROL::ExportFootprint( const TOOL_EVENT& aEvent )
  448. {
  449. if( FOOTPRINT* fp = m_frame->GetBoard()->GetFirstFootprint() )
  450. m_frame->ExportFootprint( fp );
  451. return 0;
  452. }
  453. int FOOTPRINT_EDITOR_CONTROL::EditFootprint( const TOOL_EVENT& aEvent )
  454. {
  455. m_frame->LoadFootprintFromLibrary( m_frame->GetTreeFPID() );
  456. return 0;
  457. }
  458. int FOOTPRINT_EDITOR_CONTROL::PinLibrary( const TOOL_EVENT& aEvent )
  459. {
  460. LIB_TREE_NODE* currentNode = m_frame->GetCurrentTreeNode();
  461. if( currentNode && !currentNode->m_Pinned )
  462. {
  463. m_frame->Prj().PinLibrary( currentNode->m_LibId.GetLibNickname(), false );
  464. currentNode->m_Pinned = true;
  465. m_frame->RegenerateLibraryTree();
  466. }
  467. return 0;
  468. }
  469. int FOOTPRINT_EDITOR_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent )
  470. {
  471. LIB_TREE_NODE* currentNode = m_frame->GetCurrentTreeNode();
  472. if( currentNode && currentNode->m_Pinned )
  473. {
  474. m_frame->Prj().UnpinLibrary( currentNode->m_LibId.GetLibNickname(), false );
  475. currentNode->m_Pinned = false;
  476. m_frame->RegenerateLibraryTree();
  477. }
  478. return 0;
  479. }
  480. int FOOTPRINT_EDITOR_CONTROL::ToggleFootprintTree( const TOOL_EVENT& aEvent )
  481. {
  482. m_frame->ToggleSearchTree();
  483. return 0;
  484. }
  485. int FOOTPRINT_EDITOR_CONTROL::ToggleLayersManager( const TOOL_EVENT& aEvent )
  486. {
  487. m_frame->ToggleLayersManager();
  488. return 0;
  489. }
  490. int FOOTPRINT_EDITOR_CONTROL::ToggleProperties( const TOOL_EVENT& aEvent )
  491. {
  492. m_frame->ToggleProperties();
  493. return 0;
  494. }
  495. int FOOTPRINT_EDITOR_CONTROL::Properties( const TOOL_EVENT& aEvent )
  496. {
  497. if( FOOTPRINT* footprint = m_frame->GetBoard()->GetFirstFootprint() )
  498. {
  499. getEditFrame<FOOTPRINT_EDIT_FRAME>()->OnEditItemRequest( footprint );
  500. m_frame->GetCanvas()->Refresh();
  501. }
  502. return 0;
  503. }
  504. int FOOTPRINT_EDITOR_CONTROL::DefaultPadProperties( const TOOL_EVENT& aEvent )
  505. {
  506. getEditFrame<FOOTPRINT_EDIT_FRAME>()->ShowPadPropertiesDialog( nullptr );
  507. return 0;
  508. }
  509. int FOOTPRINT_EDITOR_CONTROL::CleanupGraphics( const TOOL_EVENT& aEvent )
  510. {
  511. FOOTPRINT_EDIT_FRAME* editFrame = getEditFrame<FOOTPRINT_EDIT_FRAME>();
  512. DIALOG_CLEANUP_GRAPHICS dlg( editFrame, true );
  513. dlg.ShowModal();
  514. return 0;
  515. }
  516. int FOOTPRINT_EDITOR_CONTROL::CheckFootprint( const TOOL_EVENT& aEvent )
  517. {
  518. if( !m_checkerDialog )
  519. {
  520. m_checkerDialog = new DIALOG_FOOTPRINT_CHECKER( m_frame );
  521. m_checkerDialog->Show( true );
  522. }
  523. else // The dialog is just not visible (because the user has double clicked on an error item)
  524. {
  525. m_checkerDialog->Show( true );
  526. }
  527. return 0;
  528. }
  529. void FOOTPRINT_EDITOR_CONTROL::CrossProbe( const PCB_MARKER* aMarker )
  530. {
  531. if( !m_checkerDialog )
  532. m_checkerDialog = new DIALOG_FOOTPRINT_CHECKER( m_frame );
  533. if( !m_checkerDialog->IsShown() )
  534. m_checkerDialog->Show( true );
  535. m_checkerDialog->SelectMarker( aMarker );
  536. }
  537. void FOOTPRINT_EDITOR_CONTROL::DestroyCheckerDialog()
  538. {
  539. if( m_checkerDialog )
  540. {
  541. m_checkerDialog->Destroy();
  542. m_checkerDialog = nullptr;
  543. }
  544. }
  545. int FOOTPRINT_EDITOR_CONTROL::RepairFootprint( const TOOL_EVENT& aEvent )
  546. {
  547. FOOTPRINT* footprint = board()->Footprints().front();
  548. int errors = 0;
  549. wxString details;
  550. // Repair duplicate IDs and missing nets.
  551. std::set<KIID> ids;
  552. int duplicates = 0;
  553. auto processItem =
  554. [&]( EDA_ITEM* aItem )
  555. {
  556. if( ids.count( aItem->m_Uuid ) )
  557. {
  558. duplicates++;
  559. const_cast<KIID&>( aItem->m_Uuid ) = KIID();
  560. }
  561. ids.insert( aItem->m_Uuid );
  562. };
  563. // Footprint IDs are the most important, so give them the first crack at "claiming" a
  564. // particular KIID.
  565. processItem( footprint );
  566. // After that the principal use is for DRC marker pointers, which are most likely to pads.
  567. for( PAD* pad : footprint->Pads() )
  568. processItem( pad );
  569. // From here out I don't think order matters much.
  570. processItem( &footprint->Reference() );
  571. processItem( &footprint->Value() );
  572. for( BOARD_ITEM* item : footprint->GraphicalItems() )
  573. processItem( item );
  574. for( ZONE* zone : footprint->Zones() )
  575. processItem( zone );
  576. for( PCB_GROUP* group : footprint->Groups() )
  577. processItem( group );
  578. if( duplicates )
  579. {
  580. errors += duplicates;
  581. details += wxString::Format( _( "%d duplicate IDs replaced.\n" ), duplicates );
  582. }
  583. /*******************************
  584. * Your test here
  585. */
  586. /*******************************
  587. * Inform the user
  588. */
  589. if( errors )
  590. {
  591. m_frame->OnModify();
  592. wxString msg = wxString::Format( _( "%d potential problems repaired." ), errors );
  593. DisplayInfoMessage( m_frame, msg, details );
  594. }
  595. else
  596. {
  597. DisplayInfoMessage( m_frame, _( "No footprint problems found." ) );
  598. }
  599. return 0;
  600. }
  601. void FOOTPRINT_EDITOR_CONTROL::setTransitions()
  602. {
  603. Go( &FOOTPRINT_EDITOR_CONTROL::NewFootprint, PCB_ACTIONS::newFootprint.MakeEvent() );
  604. Go( &FOOTPRINT_EDITOR_CONTROL::CreateFootprint, PCB_ACTIONS::createFootprint.MakeEvent() );
  605. Go( &FOOTPRINT_EDITOR_CONTROL::Save, ACTIONS::save.MakeEvent() );
  606. Go( &FOOTPRINT_EDITOR_CONTROL::SaveAs, ACTIONS::saveAs.MakeEvent() );
  607. Go( &FOOTPRINT_EDITOR_CONTROL::Revert, ACTIONS::revert.MakeEvent() );
  608. Go( &FOOTPRINT_EDITOR_CONTROL::DuplicateFootprint, PCB_ACTIONS::duplicateFootprint.MakeEvent() );
  609. Go( &FOOTPRINT_EDITOR_CONTROL::RenameFootprint, PCB_ACTIONS::renameFootprint.MakeEvent() );
  610. Go( &FOOTPRINT_EDITOR_CONTROL::DeleteFootprint, PCB_ACTIONS::deleteFootprint.MakeEvent() );
  611. Go( &FOOTPRINT_EDITOR_CONTROL::EditFootprint, PCB_ACTIONS::editFootprint.MakeEvent() );
  612. Go( &FOOTPRINT_EDITOR_CONTROL::CutCopyFootprint, PCB_ACTIONS::cutFootprint.MakeEvent() );
  613. Go( &FOOTPRINT_EDITOR_CONTROL::CutCopyFootprint, PCB_ACTIONS::copyFootprint.MakeEvent() );
  614. Go( &FOOTPRINT_EDITOR_CONTROL::PasteFootprint, PCB_ACTIONS::pasteFootprint.MakeEvent() );
  615. Go( &FOOTPRINT_EDITOR_CONTROL::ImportFootprint, PCB_ACTIONS::importFootprint.MakeEvent() );
  616. Go( &FOOTPRINT_EDITOR_CONTROL::ExportFootprint, PCB_ACTIONS::exportFootprint.MakeEvent() );
  617. Go( &FOOTPRINT_EDITOR_CONTROL::EditTextAndGraphics, PCB_ACTIONS::editTextAndGraphics.MakeEvent() );
  618. Go( &FOOTPRINT_EDITOR_CONTROL::CleanupGraphics, PCB_ACTIONS::cleanupGraphics.MakeEvent() );
  619. Go( &FOOTPRINT_EDITOR_CONTROL::CheckFootprint, PCB_ACTIONS::checkFootprint.MakeEvent() );
  620. Go( &FOOTPRINT_EDITOR_CONTROL::RepairFootprint, PCB_ACTIONS::repairFootprint.MakeEvent() );
  621. Go( &FOOTPRINT_EDITOR_CONTROL::PinLibrary, ACTIONS::pinLibrary.MakeEvent() );
  622. Go( &FOOTPRINT_EDITOR_CONTROL::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() );
  623. Go( &FOOTPRINT_EDITOR_CONTROL::ToggleFootprintTree, PCB_ACTIONS::showFootprintTree.MakeEvent() );
  624. Go( &FOOTPRINT_EDITOR_CONTROL::ToggleFootprintTree, PCB_ACTIONS::hideFootprintTree.MakeEvent() );
  625. Go( &FOOTPRINT_EDITOR_CONTROL::Properties, PCB_ACTIONS::footprintProperties.MakeEvent() );
  626. Go( &FOOTPRINT_EDITOR_CONTROL::DefaultPadProperties, PCB_ACTIONS::defaultPadProperties.MakeEvent() );
  627. Go( &FOOTPRINT_EDITOR_CONTROL::ToggleLayersManager, PCB_ACTIONS::showLayersManager.MakeEvent() );
  628. Go( &FOOTPRINT_EDITOR_CONTROL::ToggleProperties, PCB_ACTIONS::showProperties.MakeEvent() );
  629. }