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.

594 lines
18 KiB

17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <fctsys.h>
  27. #include <class_drawpanel.h>
  28. #include <class_draw_panel_gal.h>
  29. #include <confirm.h>
  30. #include <kicad_string.h>
  31. #include <pcb_edit_frame.h>
  32. #include <macros.h>
  33. #include <board_commit.h>
  34. #include <bitmaps.h>
  35. #include <class_board.h>
  36. #include <class_module.h>
  37. #include <project.h>
  38. #include <wx_html_report_panel.h>
  39. #include <pcbnew.h>
  40. #include <dialog_exchange_footprints.h>
  41. #include <wildcards_and_files_ext.h>
  42. #include <kiway.h>
  43. static bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName );
  44. #define ID_MATCH_FP_ALL 4200
  45. #define ID_MATCH_FP_REF 4201
  46. #define ID_MATCH_FP_VAL 4202
  47. #define ID_MATCH_FP_ID 4203
  48. int DIALOG_EXCHANGE_FOOTPRINTS::m_matchModeForUpdate = ID_MATCH_FP_ALL;
  49. int DIALOG_EXCHANGE_FOOTPRINTS::m_matchModeForExchange = ID_MATCH_FP_REF;
  50. int DIALOG_EXCHANGE_FOOTPRINTS::m_matchModeForUpdateSelected = ID_MATCH_FP_REF;
  51. int DIALOG_EXCHANGE_FOOTPRINTS::m_matchModeForExchangeSelected = ID_MATCH_FP_REF;
  52. DIALOG_EXCHANGE_FOOTPRINTS::DIALOG_EXCHANGE_FOOTPRINTS( PCB_EDIT_FRAME* parent, MODULE* Module,
  53. bool updateMode ) :
  54. DIALOG_EXCHANGE_FOOTPRINTS_BASE( parent ), m_commit( parent )
  55. {
  56. m_parent = parent;
  57. m_currentModule = Module;
  58. m_updateMode = updateMode;
  59. init( m_updateMode );
  60. // DIALOG_SHIM needs a unique hash_key because classname is not sufficient
  61. // because the update and change versions of this dialog have different controls.
  62. m_hash_key = TO_UTF8( GetTitle() );
  63. // Ensure m_closeButton (with id = wxID_CANCEL) has the right label
  64. // (to fix automatic renaming of button label )
  65. m_closeButton->SetLabel( _( "Close" ) );
  66. // Now all widgets have the size fixed, call FinishDialogSettings
  67. FinishDialogSettings();
  68. }
  69. void DIALOG_EXCHANGE_FOOTPRINTS::OnQuit( wxCommandEvent& event )
  70. {
  71. Show( false );
  72. EndQuasiModal( wxID_CANCEL );
  73. }
  74. void DIALOG_EXCHANGE_FOOTPRINTS::init( bool updateMode )
  75. {
  76. SetFocus();
  77. wxString title = updateMode ? _( "Update Footprints from Library" ) : _( "Change Footprints" );
  78. wxString verb = updateMode ? _( "Update" ) : _( "Change" );
  79. wxString label;
  80. SetTitle( title );
  81. if( updateMode )
  82. {
  83. label.Printf( m_matchAll->GetLabel(), verb );
  84. m_matchAll->SetLabel( label );
  85. m_middleSizer->Show( false );
  86. }
  87. else
  88. {
  89. m_upperSizer->FindItem( m_matchAll )->Show( false );
  90. if( m_currentModule )
  91. m_newID->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  92. m_newIDBrowseButton->SetBitmap( KiBitmap( library_browse_xpm ) );
  93. }
  94. if( m_currentModule )
  95. {
  96. m_upperSizer->FindItem( m_matchSpecifiedRef )->Show( false );
  97. m_upperSizer->FindItem( m_specifiedRef )->Show( false );
  98. label.Printf( m_matchCurrentRef->GetLabel(), verb, m_currentModule->GetReference() );
  99. m_matchCurrentRef->SetLabel( label );
  100. m_upperSizer->FindItem( m_matchSpecifiedValue )->Show( false );
  101. m_upperSizer->FindItem( m_specifiedValue )->Show( false );
  102. label.Printf( m_matchCurrentValue->GetLabel(), verb, m_currentModule->GetValue() );
  103. m_matchCurrentValue->SetLabel( label );
  104. }
  105. else
  106. {
  107. m_upperSizer->FindItem( m_matchCurrentRef )->Show( false );
  108. label.Printf( m_matchSpecifiedRef->GetLabel(), verb );
  109. m_matchSpecifiedRef->SetLabel( label );
  110. m_upperSizer->FindItem( m_matchCurrentValue )->Show( false );
  111. label.Printf( m_matchSpecifiedValue->GetLabel(), verb );
  112. m_matchSpecifiedValue->SetLabel( label );
  113. }
  114. label.Printf( m_matchSpecifiedID->GetLabel(), verb );
  115. m_matchSpecifiedID->SetLabel( label );
  116. if( m_currentModule )
  117. m_specifiedID->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  118. m_specifiedIDBrowseButton->SetBitmap( KiBitmap( library_browse_xpm ) );
  119. m_upperSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
  120. m_upperSizer->RecalcSizes();
  121. // initialize match-mode
  122. wxCommandEvent event;
  123. switch( getMatchMode() )
  124. {
  125. case ID_MATCH_FP_ALL:
  126. if( m_currentModule )
  127. OnMatchRefClicked( event );
  128. else
  129. OnMatchAllClicked( event );
  130. break;
  131. case ID_MATCH_FP_REF:
  132. OnMatchRefClicked( event );
  133. break;
  134. case ID_MATCH_FP_VAL:
  135. OnMatchValueClicked( event );
  136. break;
  137. case ID_MATCH_FP_ID:
  138. OnMatchIDClicked( event );
  139. }
  140. }
  141. int DIALOG_EXCHANGE_FOOTPRINTS::getMatchMode()
  142. {
  143. if( m_updateMode )
  144. return( m_currentModule ? m_matchModeForUpdateSelected : m_matchModeForUpdate );
  145. else
  146. return( m_currentModule ? m_matchModeForExchangeSelected : m_matchModeForExchange );
  147. }
  148. void DIALOG_EXCHANGE_FOOTPRINTS::setMatchMode( int aMatchMode )
  149. {
  150. if( m_updateMode )
  151. {
  152. if( m_currentModule )
  153. m_matchModeForUpdateSelected = aMatchMode;
  154. else
  155. m_matchModeForUpdate = aMatchMode;
  156. }
  157. else
  158. {
  159. if( m_currentModule )
  160. m_matchModeForExchangeSelected = aMatchMode;
  161. else
  162. m_matchModeForExchange = aMatchMode;
  163. }
  164. }
  165. bool DIALOG_EXCHANGE_FOOTPRINTS::isMatch( MODULE* aModule )
  166. {
  167. switch( getMatchMode() )
  168. {
  169. case ID_MATCH_FP_ALL:
  170. return true;
  171. case ID_MATCH_FP_REF:
  172. // currentModule case goes through changeCurrentFootprint, so we only have
  173. // to handle specifiedRef case
  174. return aModule->GetReference() == m_specifiedRef->GetValue();
  175. case ID_MATCH_FP_VAL:
  176. // currentValue must also check FPID so we don't get accidental matches that
  177. // the user didn't intend
  178. if( m_currentModule )
  179. return aModule->GetValue() == m_currentModule->GetValue()
  180. && aModule->GetFPID() == m_currentModule->GetFPID();
  181. else
  182. return aModule->GetValue() == m_specifiedValue->GetValue();
  183. case ID_MATCH_FP_ID:
  184. return aModule->GetFPID() == m_specifiedID->GetValue();
  185. }
  186. return false; // just to quiet compiler warnings....
  187. }
  188. wxRadioButton* DIALOG_EXCHANGE_FOOTPRINTS::getRadioButtonForMode()
  189. {
  190. switch( getMatchMode() )
  191. {
  192. case ID_MATCH_FP_ALL:
  193. return( m_matchAll );
  194. case ID_MATCH_FP_REF:
  195. return( m_matchCurrentRef->IsShown() ? m_matchCurrentRef : m_matchSpecifiedRef );
  196. case ID_MATCH_FP_VAL:
  197. return( m_matchCurrentValue->IsShown() ? m_matchCurrentValue : m_matchSpecifiedValue );
  198. case ID_MATCH_FP_ID:
  199. return( m_matchSpecifiedID );
  200. default:
  201. return nullptr;
  202. }
  203. }
  204. void DIALOG_EXCHANGE_FOOTPRINTS::updateMatchModeRadioButtons( wxUpdateUIEvent& )
  205. {
  206. wxRadioButton* rb_button = getRadioButtonForMode();
  207. wxRadioButton* rb_butt_list[] =
  208. {
  209. m_matchCurrentRef, m_matchSpecifiedRef,
  210. m_matchCurrentValue, m_matchCurrentValue,
  211. m_matchSpecifiedValue, m_matchSpecifiedValue,
  212. m_matchSpecifiedID, m_matchSpecifiedID,
  213. nullptr // end of list
  214. };
  215. // Ensure the button state is ok. Only one button can be checked
  216. // Change button state only if its state is incorrect, otherwise
  217. // we have issues depending on the platform.
  218. for( int ii = 0; rb_butt_list[ii]; ++ii )
  219. {
  220. bool state = rb_butt_list[ii] == rb_button;
  221. if( rb_butt_list[ii]->GetValue() != state )
  222. rb_butt_list[ii]->SetValue( state );
  223. }
  224. }
  225. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchAllClicked( wxCommandEvent& event )
  226. {
  227. setMatchMode( ID_MATCH_FP_ALL );
  228. m_matchAll->SetFocus();
  229. }
  230. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchRefClicked( wxCommandEvent& event )
  231. {
  232. setMatchMode( ID_MATCH_FP_REF );
  233. if( m_specifiedRef->IsShown() && event.GetEventObject() != m_specifiedRef )
  234. m_specifiedRef->SetFocus();
  235. }
  236. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchValueClicked( wxCommandEvent& event )
  237. {
  238. setMatchMode( ID_MATCH_FP_VAL );
  239. if( m_specifiedValue->IsShown() && event.GetEventObject() != m_specifiedValue )
  240. m_specifiedValue->SetFocus();
  241. }
  242. void DIALOG_EXCHANGE_FOOTPRINTS::OnMatchIDClicked( wxCommandEvent& event )
  243. {
  244. setMatchMode( ID_MATCH_FP_ID );
  245. if( m_specifiedID->IsShown() && event.GetEventObject() != m_specifiedID )
  246. m_specifiedID->SetFocus();
  247. }
  248. void DIALOG_EXCHANGE_FOOTPRINTS::OnApplyClick( wxCommandEvent& event )
  249. {
  250. bool result = false;
  251. m_MessageWindow->Clear();
  252. m_MessageWindow->Flush( true );
  253. if( getMatchMode() == ID_MATCH_FP_REF && m_currentModule )
  254. result = changeCurrentFootprint();
  255. else
  256. result = changeSameFootprints();
  257. if( result )
  258. {
  259. if( m_parent->GetBoard()->IsElementVisible( LAYER_RATSNEST ) )
  260. m_parent->Compile_Ratsnest( NULL, true );
  261. m_parent->GetCanvas()->Refresh();
  262. }
  263. m_commit.Push( wxT( "Changed footprint" ) );
  264. }
  265. void DIALOG_EXCHANGE_FOOTPRINTS::RebuildCmpList( wxCommandEvent& event )
  266. {
  267. wxString msg;
  268. REPORTER& reporter = m_MessageWindow->Reporter();
  269. m_MessageWindow->Clear();
  270. m_MessageWindow->Flush( true );
  271. // Build the .cmp file name from the board name
  272. wxFileName fn = m_parent->GetBoard()->GetFileName();
  273. fn.SetExt( ComponentFileExtension );
  274. if( RecreateCmpFile( m_parent->GetBoard(), fn.GetFullPath() ) )
  275. {
  276. msg.Printf( _( "File \"%s\" created\n" ), GetChars( fn.GetFullPath() ) );
  277. reporter.Report( msg, REPORTER::RPT_INFO );
  278. }
  279. else
  280. {
  281. msg.Printf( _( "** Could not create file \"%s\" ***\n" ),
  282. GetChars( fn.GetFullPath() ) );
  283. reporter.Report( msg, REPORTER::RPT_ERROR );
  284. }
  285. }
  286. bool DIALOG_EXCHANGE_FOOTPRINTS::changeCurrentFootprint()
  287. {
  288. if( m_updateMode )
  289. return change_1_Module( m_currentModule, m_currentModule->GetFPID(), true );
  290. wxString newFPID = m_newID->GetValue();
  291. if( newFPID == wxEmptyString )
  292. return false;
  293. return change_1_Module( m_currentModule, newFPID, true );
  294. }
  295. bool DIALOG_EXCHANGE_FOOTPRINTS::changeSameFootprints()
  296. {
  297. MODULE* Module;
  298. MODULE* PtBack;
  299. bool change = false;
  300. wxString newFPID = m_newID->GetValue();
  301. wxString value;
  302. int ShowErr = 3; // Post 3 error messages max.
  303. if( m_parent->GetBoard()->m_Modules == NULL )
  304. return false;
  305. if( !m_updateMode && newFPID == wxEmptyString )
  306. return false;
  307. /* The change is done from the last module because
  308. * change_1_Module () modifies the last item in the list.
  309. *
  310. * note: for the first module in chain (the last here), Module->Back()
  311. * points the board or is NULL
  312. */
  313. Module = m_parent->GetBoard()->m_Modules.GetLast();
  314. for( ; Module && ( Module->Type() == PCB_MODULE_T ); Module = PtBack )
  315. {
  316. PtBack = Module->Back();
  317. if( !isMatch( Module ) )
  318. continue;
  319. bool result;
  320. if( m_updateMode )
  321. result = change_1_Module( Module, Module->GetFPID(), ShowErr );
  322. else
  323. result = change_1_Module( Module, newFPID, ShowErr );
  324. if( result )
  325. change = true;
  326. else if( ShowErr )
  327. ShowErr--;
  328. }
  329. return change;
  330. }
  331. bool DIALOG_EXCHANGE_FOOTPRINTS::change_1_Module( MODULE* aModule,
  332. const LIB_ID& aNewFootprintFPID,
  333. bool aShowError )
  334. {
  335. MODULE* newModule;
  336. wxString msg;
  337. if( aModule == NULL )
  338. return false;
  339. wxBusyCursor dummy;
  340. REPORTER& reporter = m_MessageWindow->Reporter();
  341. // Copy parameters from the old footprint.
  342. LIB_ID oldFootprintFPID = aModule->GetFPID();
  343. // Load module.
  344. msg.Printf( _( "Change footprint \"%s\" (from \"%s\") to \"%s\"" ),
  345. GetChars( aModule->GetReference() ),
  346. oldFootprintFPID.Format().c_str(),
  347. aNewFootprintFPID.Format().c_str() );
  348. newModule = m_parent->LoadFootprint( aNewFootprintFPID );
  349. if( newModule == NULL ) // New module not found.
  350. {
  351. msg << ": " << _( "footprint not found" );
  352. reporter.Report( msg, REPORTER::RPT_ERROR );
  353. return false;
  354. }
  355. m_parent->Exchange_Module( aModule, newModule, m_commit );
  356. if( aModule == m_currentModule )
  357. m_currentModule = newModule;
  358. if( aModule == m_parent->GetCurItem() )
  359. m_parent->SetCurItem( newModule );
  360. msg += ": OK";
  361. reporter.Report( msg, REPORTER::RPT_ACTION );
  362. return true;
  363. }
  364. void PCB_EDIT_FRAME::Exchange_Module( MODULE* aOldModule,
  365. MODULE* aNewModule,
  366. BOARD_COMMIT& aCommit )
  367. {
  368. aNewModule->SetParent( GetBoard() );
  369. /* place module without ratsnest refresh: this will be made later
  370. * when all modules are on board */
  371. PlaceModule( aNewModule, NULL, false );
  372. // Copy full placement and pad net names (when possible)
  373. // but not local settings like clearances (use library values)
  374. aOldModule->CopyNetlistSettings( aNewModule, false );
  375. // Copy reference
  376. aNewModule->SetReference( aOldModule->GetReference() );
  377. // Copy value unless it is a proxy for the footprint ID (a good example is replacing a
  378. // footprint with value "MoutingHole-2.5mm" with one of value "MountingHole-4mm").
  379. if( aOldModule->GetValue() != aOldModule->GetFPID().GetLibItemName() )
  380. aNewModule->SetValue( aOldModule->GetValue() );
  381. // Compare the footprint name only, in case the nickname is empty or in case
  382. // user moved the footprint to a new library. Chances are if footprint name is
  383. // same then the footprint is very nearly the same and the two texts should
  384. // be kept at same size, position, and rotation.
  385. if( aNewModule->GetFPID().GetLibItemName() == aOldModule->GetFPID().GetLibItemName() )
  386. {
  387. aNewModule->Reference().SetEffects( aOldModule->Reference() );
  388. aNewModule->Value().SetEffects( aOldModule->Value() );
  389. }
  390. // Updating other parameters
  391. aNewModule->SetTimeStamp( aOldModule->GetTimeStamp() );
  392. aNewModule->SetPath( aOldModule->GetPath() );
  393. aCommit.Remove( aOldModule );
  394. aCommit.Add( aNewModule );
  395. // @todo LEGACY should be unnecessary
  396. GetBoard()->m_Status_Pcb = 0;
  397. aNewModule->ClearFlags();
  398. }
  399. void DIALOG_EXCHANGE_FOOTPRINTS::ViewAndSelectFootprint( wxCommandEvent& event )
  400. {
  401. wxString newname;
  402. KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_MODULE_VIEWER_MODAL, true );
  403. if( frame->ShowModal( &newname, this ) )
  404. {
  405. if( event.GetEventObject() == m_newIDBrowseButton )
  406. m_newID->SetValue( newname );
  407. else
  408. m_specifiedID->SetValue( newname );
  409. }
  410. frame->Destroy();
  411. }
  412. void PCB_EDIT_FRAME::RecreateCmpFileFromBoard( wxCommandEvent& aEvent )
  413. {
  414. wxFileName fn;
  415. MODULE* module = GetBoard()->m_Modules;
  416. wxString msg;
  417. wxString wildcard;
  418. if( module == NULL )
  419. {
  420. DisplayError( this, _( "No footprints!" ) );
  421. return;
  422. }
  423. // Build the .cmp file name from the board name
  424. fn = GetBoard()->GetFileName();
  425. fn.SetExt( ComponentFileExtension );
  426. wildcard = ComponentFileWildcard();
  427. wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() );
  428. wxFileDialog dlg( this, _( "Save Footprint Association File" ), pro_dir,
  429. fn.GetFullName(), wildcard,
  430. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  431. if( dlg.ShowModal() == wxID_CANCEL )
  432. return;
  433. fn = dlg.GetPath();
  434. if( ! RecreateCmpFile( GetBoard(), fn.GetFullPath() ) )
  435. {
  436. msg.Printf( _( "Could not create file \"%s\"" ), GetChars(fn.GetFullPath() ) );
  437. DisplayError( this, msg );
  438. return;
  439. }
  440. }
  441. bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName )
  442. {
  443. FILE* cmpFile;
  444. cmpFile = wxFopen( aFullCmpFileName, wxT( "wt" ) );
  445. if( cmpFile == NULL )
  446. return false;
  447. fprintf( cmpFile, "Cmp-Mod V01 Created by PcbNew date = %s\n", TO_UTF8( DateAndTime() ) );
  448. MODULE* module = aBrd->m_Modules;
  449. for( ; module != NULL; module = module->Next() )
  450. {
  451. fprintf( cmpFile, "\nBeginCmp\n" );
  452. fprintf( cmpFile, "TimeStamp = %8.8lX\n", (unsigned long)module->GetTimeStamp() );
  453. fprintf( cmpFile, "Path = %s\n", TO_UTF8( module->GetPath() ) );
  454. fprintf( cmpFile, "Reference = %s;\n",
  455. !module->GetReference().IsEmpty() ?
  456. TO_UTF8( module->GetReference() ) : "[NoRef]" );
  457. fprintf( cmpFile, "ValeurCmp = %s;\n",
  458. !module->GetValue().IsEmpty() ?
  459. TO_UTF8( module->GetValue() ) : "[NoVal]" );
  460. fprintf( cmpFile, "IdModule = %s;\n", module->GetFPID().Format().c_str() );
  461. fprintf( cmpFile, "EndCmp\n" );
  462. }
  463. fprintf( cmpFile, "\nEndListe\n" );
  464. fclose( cmpFile );
  465. return true;
  466. }