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.

483 lines
14 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
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-2016 Wayne Stambaugh <stambaughw@verizon.net>
  7. * Copyright (C) 1992-2017 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 <wxPcbStruct.h>
  32. #include <macros.h>
  33. #include <board_commit.h>
  34. #include <class_board.h>
  35. #include <class_module.h>
  36. #include <project.h>
  37. #include <wx_html_report_panel.h>
  38. //#include <reporter.h>
  39. #include <pcbnew.h>
  40. #include <dialog_exchange_modules.h>
  41. #include <wildcards_and_files_ext.h>
  42. #include <kiway.h>
  43. static bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName );
  44. int DIALOG_EXCHANGE_MODULE::m_selectionMode = 0;
  45. DIALOG_EXCHANGE_MODULE::DIALOG_EXCHANGE_MODULE( PCB_EDIT_FRAME* parent, MODULE* Module ) :
  46. DIALOG_EXCHANGE_MODULE_BASE( parent ), m_commit( parent )
  47. {
  48. m_parent = parent;
  49. m_currentModule = Module;
  50. init();
  51. GetSizer()->Fit( this );
  52. GetSizer()->SetSizeHints( this );
  53. Center();
  54. }
  55. void DIALOG_EXCHANGE_MODULE::OnQuit( wxCommandEvent& event )
  56. {
  57. m_selectionMode = m_Selection->GetSelection();
  58. Show( false );
  59. EndQuasiModal( wxID_CANCEL );
  60. }
  61. void DIALOG_EXCHANGE_MODULE::init()
  62. {
  63. SetFocus();
  64. m_CurrentFootprintFPID->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  65. m_NewFootprintFPID->AppendText( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) );
  66. m_CmpValue->AppendText( m_currentModule->GetValue() );
  67. m_CmpReference->AppendText( m_currentModule->GetReference() );
  68. m_Selection->SetString( 0, wxString::Format(
  69. _( "Change footprint of '%s'" ),
  70. GetChars( m_currentModule->GetReference() ) ) );
  71. wxString fpname = m_CurrentFootprintFPID->GetValue().AfterLast( ':' );
  72. if( fpname.IsEmpty() ) // Happens for old fp names
  73. fpname = m_CurrentFootprintFPID->GetValue();
  74. m_Selection->SetString( 1, wxString::Format(
  75. _( "Change footprints '%s'" ),
  76. GetChars( fpname.Left( 12 ) ) ) );
  77. m_Selection->SetSelection( m_selectionMode );
  78. // Enable/disable widgets:
  79. wxCommandEvent event;
  80. OnSelectionClicked( event );
  81. }
  82. void DIALOG_EXCHANGE_MODULE::OnOkClick( wxCommandEvent& event )
  83. {
  84. m_selectionMode = m_Selection->GetSelection();
  85. bool result = false;
  86. m_MessageWindow->Clear();
  87. m_MessageWindow->Flush();
  88. switch( m_Selection->GetSelection() )
  89. {
  90. case 0:
  91. result = changeCurrentFootprint();
  92. break;
  93. case 1:
  94. result = changeSameFootprints( false );
  95. break;
  96. case 2:
  97. result = changeSameFootprints( true );
  98. break;
  99. case 3:
  100. result = changeAllFootprints();
  101. break;
  102. }
  103. if( result )
  104. {
  105. if( m_parent->GetBoard()->IsElementVisible( LAYER_RATSNEST ) )
  106. m_parent->Compile_Ratsnest( NULL, true );
  107. m_parent->GetCanvas()->Refresh();
  108. }
  109. m_commit.Push( wxT( "Changed footprint" ) );
  110. }
  111. void DIALOG_EXCHANGE_MODULE::OnSelectionClicked( wxCommandEvent& event )
  112. {
  113. bool enable = true;
  114. switch( m_Selection->GetSelection() )
  115. {
  116. case 0:
  117. case 1:
  118. case 2:
  119. break;
  120. case 3:
  121. enable = false;
  122. break;
  123. }
  124. m_NewFootprintFPID->Enable( enable );
  125. m_Browsebutton->Enable( enable );
  126. }
  127. void DIALOG_EXCHANGE_MODULE::RebuildCmpList( wxCommandEvent& event )
  128. {
  129. wxString msg;
  130. REPORTER& reporter = m_MessageWindow->Reporter();
  131. m_MessageWindow->Clear();
  132. m_MessageWindow->Flush();
  133. // Build the .cmp file name from the board name
  134. wxFileName fn = m_parent->GetBoard()->GetFileName();
  135. fn.SetExt( ComponentFileExtension );
  136. if( RecreateCmpFile( m_parent->GetBoard(), fn.GetFullPath() ) )
  137. {
  138. msg.Printf( _( "File '%s' created\n" ), GetChars( fn.GetFullPath() ) );
  139. reporter.Report( msg, REPORTER::RPT_INFO );
  140. }
  141. else
  142. {
  143. msg.Printf( _( "** Could not create file '%s' ***\n" ),
  144. GetChars( fn.GetFullPath() ) );
  145. reporter.Report( msg, REPORTER::RPT_ERROR );
  146. }
  147. }
  148. bool DIALOG_EXCHANGE_MODULE::changeCurrentFootprint()
  149. {
  150. wxString newmodulename = m_NewFootprintFPID->GetValue();
  151. if( newmodulename == wxEmptyString )
  152. return false;
  153. return change_1_Module( m_currentModule, newmodulename, true );
  154. }
  155. bool DIALOG_EXCHANGE_MODULE::changeSameFootprints( bool aUseValue )
  156. {
  157. wxString msg;
  158. MODULE* Module;
  159. MODULE* PtBack;
  160. bool change = false;
  161. wxString newmodulename = m_NewFootprintFPID->GetValue();
  162. wxString value;
  163. LIB_ID lib_reference;
  164. bool check_module_value = false;
  165. int ShowErr = 3; // Post 3 error messages max.
  166. if( m_parent->GetBoard()->m_Modules == NULL )
  167. return false;
  168. if( newmodulename == wxEmptyString )
  169. return false;
  170. lib_reference = m_currentModule->GetFPID();
  171. if( aUseValue )
  172. {
  173. check_module_value = true;
  174. value = m_currentModule->GetValue();
  175. msg.Printf( _( "Change footprint %s -> %s (for value = %s)?" ),
  176. GetChars( FROM_UTF8( m_currentModule->GetFPID().Format().c_str() ) ),
  177. GetChars( newmodulename ),
  178. GetChars( m_currentModule->GetValue() ) );
  179. }
  180. else
  181. {
  182. msg.Printf( _( "Change footprint %s -> %s ?" ),
  183. GetChars( FROM_UTF8( lib_reference.Format().c_str() ) ),
  184. GetChars( newmodulename ) );
  185. }
  186. if( !IsOK( this, msg ) )
  187. return false;
  188. /* The change is done from the last module because
  189. * change_1_Module () modifies the last item in the list.
  190. *
  191. * note: for the first module in chain (the last here), Module->Back()
  192. * points the board or is NULL
  193. */
  194. Module = m_parent->GetBoard()->m_Modules.GetLast();
  195. for( ; Module && ( Module->Type() == PCB_MODULE_T ); Module = PtBack )
  196. {
  197. PtBack = Module->Back();
  198. if( lib_reference != Module->GetFPID() )
  199. continue;
  200. if( check_module_value )
  201. {
  202. if( value.CmpNoCase( Module->GetValue() ) != 0 )
  203. continue;
  204. }
  205. if( change_1_Module( Module, newmodulename, ShowErr ) )
  206. change = true;
  207. else if( ShowErr )
  208. ShowErr--;
  209. }
  210. return change;
  211. }
  212. bool DIALOG_EXCHANGE_MODULE::changeAllFootprints()
  213. {
  214. MODULE* Module, * PtBack;
  215. bool change = false;
  216. int ShowErr = 3; // Post 3 error max.
  217. if( m_parent->GetBoard()->m_Modules == NULL )
  218. return false;
  219. if( !IsOK( this, _( "Are you sure you want to change all footprints?" ) ) )
  220. return false;
  221. /* The change is done from the last module because the function
  222. * change_1_Module () modifies the last module in the list
  223. *
  224. * note: for the first module in chain (the last here), Module->Back()
  225. * points the board or is NULL
  226. */
  227. Module = m_parent->GetBoard()->m_Modules.GetLast();
  228. for( ; Module && ( Module->Type() == PCB_MODULE_T ); Module = PtBack )
  229. {
  230. PtBack = Module->Back();
  231. if( change_1_Module( Module, Module->GetFPID(), ShowErr ) )
  232. change = true;
  233. else if( ShowErr )
  234. ShowErr--;
  235. }
  236. return change;
  237. }
  238. bool DIALOG_EXCHANGE_MODULE::change_1_Module( MODULE* aModule,
  239. const LIB_ID& aNewFootprintFPID,
  240. bool aShowError )
  241. {
  242. MODULE* newModule;
  243. wxString msg;
  244. if( aModule == NULL )
  245. return false;
  246. wxBusyCursor dummy;
  247. REPORTER& reporter = m_MessageWindow->Reporter();
  248. // Copy parameters from the old footprint.
  249. LIB_ID oldFootprintFPID = aModule->GetFPID();
  250. // Load module.
  251. msg.Printf( _( "Change footprint '%s' (from '%s') to '%s'" ),
  252. GetChars( aModule->GetReference() ),
  253. oldFootprintFPID.Format().c_str(),
  254. aNewFootprintFPID.Format().c_str() );
  255. newModule = m_parent->LoadFootprint( aNewFootprintFPID );
  256. if( newModule == NULL ) // New module not found.
  257. {
  258. msg << ": " << _( "footprint not found" );
  259. reporter.Report( msg, REPORTER::RPT_ERROR );
  260. return false;
  261. }
  262. m_parent->Exchange_Module( aModule, newModule, m_commit );
  263. if( aModule == m_currentModule )
  264. m_currentModule = newModule;
  265. msg += ": OK";
  266. reporter.Report( msg, REPORTER::RPT_ACTION );
  267. return true;
  268. }
  269. void PCB_EDIT_FRAME::Exchange_Module( MODULE* aOldModule,
  270. MODULE* aNewModule,
  271. BOARD_COMMIT& aCommit )
  272. {
  273. aNewModule->SetParent( GetBoard() );
  274. /* place module without ratsnest refresh: this will be made later
  275. * when all modules are on board */
  276. PlaceModule( aNewModule, NULL, true );
  277. // Copy full placement and pad net names (when possible)
  278. // but not local settings like clearances (use library values)
  279. aOldModule->CopyNetlistSettings( aNewModule, false );
  280. // Copy reference and value
  281. aNewModule->SetReference( aOldModule->GetReference() );
  282. aNewModule->SetValue( aOldModule->GetValue() );
  283. // Compare the footprint name only, in case the nickname is empty or in case
  284. // user moved the footprint to a new library. Chances are if footprint name is
  285. // same then the footprint is very nearly the same and the two texts should
  286. // be kept at same size, position, and rotation.
  287. if( aNewModule->GetFPID().GetLibItemName() == aOldModule->GetFPID().GetLibItemName() )
  288. {
  289. aNewModule->Reference().SetEffects( aOldModule->Reference() );
  290. aNewModule->Value().SetEffects( aOldModule->Value() );
  291. }
  292. // Updating other parameters
  293. aNewModule->SetTimeStamp( aOldModule->GetTimeStamp() );
  294. aNewModule->SetPath( aOldModule->GetPath() );
  295. aCommit.Remove( aOldModule );
  296. aCommit.Add( aNewModule );
  297. // @todo LEGACY should be unnecessary
  298. GetBoard()->m_Status_Pcb = 0;
  299. aNewModule->ClearFlags();
  300. }
  301. // Displays the list of available footprints in library name and select a footprint.
  302. void DIALOG_EXCHANGE_MODULE::BrowseAndSelectFootprint( wxCommandEvent& event )
  303. {
  304. wxString newname;
  305. newname = m_parent->SelectFootprint( m_parent, wxEmptyString, wxEmptyString, wxEmptyString,
  306. Prj().PcbFootprintLibs() );
  307. if( newname != wxEmptyString )
  308. m_NewFootprintFPID->SetValue( newname );
  309. }
  310. void DIALOG_EXCHANGE_MODULE::ViewAndSelectFootprint( wxCommandEvent& event )
  311. {
  312. wxString newname;
  313. KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_MODULE_VIEWER_MODAL, true );
  314. if( frame->ShowModal( &newname, this ) )
  315. {
  316. m_NewFootprintFPID->SetValue( newname );
  317. }
  318. frame->Destroy();
  319. }
  320. void PCB_EDIT_FRAME::RecreateCmpFileFromBoard( wxCommandEvent& aEvent )
  321. {
  322. wxFileName fn;
  323. MODULE* module = GetBoard()->m_Modules;
  324. wxString msg;
  325. wxString wildcard;
  326. if( module == NULL )
  327. {
  328. DisplayError( this, _( "No footprints!" ) );
  329. return;
  330. }
  331. // Build the .cmp file name from the board name
  332. fn = GetBoard()->GetFileName();
  333. fn.SetExt( ComponentFileExtension );
  334. wildcard = wxGetTranslation( ComponentFileWildcard );
  335. wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() );
  336. wxFileDialog dlg( this, _( "Save Footprint Association File" ), pro_dir,
  337. fn.GetFullName(), wildcard,
  338. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  339. if( dlg.ShowModal() == wxID_CANCEL )
  340. return;
  341. fn = dlg.GetPath();
  342. if( ! RecreateCmpFile( GetBoard(), fn.GetFullPath() ) )
  343. {
  344. msg.Printf( _( "Could not create file '%s'" ), GetChars(fn.GetFullPath() ) );
  345. DisplayError( this, msg );
  346. return;
  347. }
  348. }
  349. bool RecreateCmpFile( BOARD * aBrd, const wxString& aFullCmpFileName )
  350. {
  351. FILE* cmpFile;
  352. cmpFile = wxFopen( aFullCmpFileName, wxT( "wt" ) );
  353. if( cmpFile == NULL )
  354. return false;
  355. fprintf( cmpFile, "Cmp-Mod V01 Created by PcbNew date = %s\n", TO_UTF8( DateAndTime() ) );
  356. MODULE* module = aBrd->m_Modules;
  357. for( ; module != NULL; module = module->Next() )
  358. {
  359. fprintf( cmpFile, "\nBeginCmp\n" );
  360. fprintf( cmpFile, "TimeStamp = %8.8lX\n", (unsigned long)module->GetTimeStamp() );
  361. fprintf( cmpFile, "Path = %s\n", TO_UTF8( module->GetPath() ) );
  362. fprintf( cmpFile, "Reference = %s;\n",
  363. !module->GetReference().IsEmpty() ?
  364. TO_UTF8( module->GetReference() ) : "[NoRef]" );
  365. fprintf( cmpFile, "ValeurCmp = %s;\n",
  366. !module->GetValue().IsEmpty() ?
  367. TO_UTF8( module->GetValue() ) : "[NoVal]" );
  368. fprintf( cmpFile, "IdModule = %s;\n", module->GetFPID().Format().c_str() );
  369. fprintf( cmpFile, "EndCmp\n" );
  370. }
  371. fprintf( cmpFile, "\nEndListe\n" );
  372. fclose( cmpFile );
  373. return true;
  374. }