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.

849 lines
28 KiB

3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020-2021 CERN
  5. * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Wayne Stambaugh <stambaughw@gmail.com>
  8. *
  9. * This program is free software: you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by the
  11. * Free Software Foundation, either version 3 of the License, or (at your
  12. * option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include <algorithm>
  23. #include <bitmaps.h>
  24. #include <connection_graph.h>
  25. #include <string_utils.h> // WildCompareString
  26. #include <kiway.h>
  27. #include <refdes_utils.h>
  28. #include <core/kicad_algo.h>
  29. #include <dialog_change_symbols.h>
  30. #include <sch_symbol.h>
  31. #include <sch_edit_frame.h>
  32. #include <sch_screen.h>
  33. #include <schematic.h>
  34. #include <template_fieldnames.h>
  35. #include <widgets/wx_html_report_panel.h>
  36. #include <widgets/std_bitmap_button.h>
  37. #include <sch_commit.h>
  38. bool g_selectRefDes = false;
  39. bool g_selectValue = false;
  40. // { change, update }
  41. bool g_removeExtraFields[2] = { false, false };
  42. bool g_resetEmptyFields[2] = { false, false };
  43. bool g_resetFieldText[2] = { true, true };
  44. bool g_resetFieldVisibilities[2] = { true, false };
  45. bool g_resetFieldEffects[2] = { true, false };
  46. bool g_resetFieldPositions[2] = { true, false };
  47. bool g_resetAttributes[2] = { true, false };
  48. bool g_resetCustomPower[2] = { false, false };
  49. DIALOG_CHANGE_SYMBOLS::DIALOG_CHANGE_SYMBOLS( SCH_EDIT_FRAME* aParent, SCH_SYMBOL* aSymbol,
  50. MODE aMode ) :
  51. DIALOG_CHANGE_SYMBOLS_BASE( aParent ),
  52. m_symbol( aSymbol),
  53. m_mode( aMode )
  54. {
  55. wxASSERT( aParent );
  56. if( m_mode == MODE::UPDATE )
  57. {
  58. m_newIdSizer->Show( false );
  59. }
  60. else
  61. {
  62. m_matchAll->SetLabel( _( "Change all symbols in schematic" ) );
  63. SetTitle( _( "Change Symbols" ) );
  64. m_matchSizer->FindItem( m_matchAll )->Show( false );
  65. }
  66. if( m_symbol )
  67. {
  68. SCH_SHEET_PATH* currentSheet = &aParent->Schematic().CurrentSheet();
  69. if( m_mode == MODE::CHANGE )
  70. m_matchBySelection->SetLabel( _( "Change selected symbol(s)" ) );
  71. m_newId->ChangeValue( UnescapeString( m_symbol->GetLibId().Format() ) );
  72. m_specifiedReference->ChangeValue( m_symbol->GetRef( currentSheet ) );
  73. m_specifiedValue->ChangeValue( UnescapeString( m_symbol->GetField( VALUE_FIELD )->GetText() ) );
  74. m_specifiedId->ChangeValue( UnescapeString( m_symbol->GetLibId().Format() ) );
  75. }
  76. else
  77. {
  78. m_matchSizer->FindItem( m_matchBySelection )->Show( false );
  79. }
  80. m_matchIdBrowserButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
  81. m_newIdBrowserButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
  82. if( m_mode == MODE::CHANGE )
  83. {
  84. m_matchByReference->SetLabel( _( "Change symbols matching reference designator:" ) );
  85. m_matchByValue->SetLabel( _( "Change symbols matching value:" ) );
  86. m_matchById->SetLabel( _( "Change symbols matching library identifier:" ) );
  87. }
  88. m_matchSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
  89. m_matchSizer->Layout();
  90. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  91. {
  92. m_fieldsBox->Append( TEMPLATE_FIELDNAME::GetDefaultFieldName( i, DO_TRANSLATE ) );
  93. if( i == REFERENCE_FIELD )
  94. m_fieldsBox->Check( i, g_selectRefDes );
  95. else if( i == VALUE_FIELD )
  96. m_fieldsBox->Check( i, g_selectValue );
  97. else
  98. m_fieldsBox->Check( i, true );
  99. }
  100. m_messagePanel->SetLazyUpdate( true );
  101. m_messagePanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
  102. if( aSymbol && aSymbol->IsSelected() )
  103. {
  104. m_matchBySelection->SetValue( true );
  105. }
  106. else
  107. {
  108. if( aMode == MODE::UPDATE )
  109. m_matchAll->SetValue( true );
  110. else
  111. m_matchByReference->SetValue( true );
  112. }
  113. updateFieldsList();
  114. if( m_mode == MODE::CHANGE )
  115. {
  116. m_updateFieldsSizer->GetStaticBox()->SetLabel( _( "Update Fields" ) );
  117. m_removeExtraBox->SetLabel( _( "Remove fields if not in new symbol" ) );
  118. m_resetEmptyFields->SetLabel( _( "Reset fields if empty in new symbol" ) );
  119. m_resetFieldText->SetLabel( _( "Update field text" ) );
  120. m_resetFieldVisibilities->SetLabel( _( "Update field visibilities" ) );
  121. m_resetFieldEffects->SetLabel( _( "Update field sizes and styles" ) );
  122. m_resetFieldPositions->SetLabel( _( "Update field positions" ) );
  123. m_resetAttributes->SetLabel( _( "Update symbol attributes" ) );
  124. }
  125. m_removeExtraBox->SetValue( g_removeExtraFields[ (int) m_mode ] );
  126. m_resetEmptyFields->SetValue( g_resetEmptyFields[ (int) m_mode ] );
  127. m_resetFieldText->SetValue( g_resetFieldText[ (int) m_mode ] );
  128. m_resetFieldVisibilities->SetValue( g_resetFieldVisibilities[ (int) m_mode ] );
  129. m_resetFieldEffects->SetValue( g_resetFieldEffects[ (int) m_mode ] );
  130. m_resetFieldPositions->SetValue( g_resetFieldPositions[ (int) m_mode ] );
  131. m_resetAttributes->SetValue( g_resetAttributes[ (int) m_mode ] );
  132. m_resetCustomPower->SetValue( g_resetCustomPower[ (int) m_mode ] );
  133. // DIALOG_SHIM needs a unique hash_key because classname is not sufficient
  134. // because the update and change versions of this dialog have different controls.
  135. m_hash_key = TO_UTF8( GetTitle() );
  136. wxString okLabel = m_mode == MODE::CHANGE ? _( "Change" ) : _( "Update" );
  137. SetupStandardButtons( { { wxID_OK, okLabel },
  138. { wxID_CANCEL, _( "Close" ) } } );
  139. // Now all widgets have the size fixed, call FinishDialogSettings
  140. finishDialogSettings();
  141. }
  142. void DIALOG_CHANGE_SYMBOLS::onMatchByAll( wxCommandEvent& aEvent )
  143. {
  144. updateFieldsList();
  145. }
  146. void DIALOG_CHANGE_SYMBOLS::onMatchBySelected( wxCommandEvent& aEvent )
  147. {
  148. updateFieldsList();
  149. }
  150. void DIALOG_CHANGE_SYMBOLS::onMatchByReference( wxCommandEvent& aEvent )
  151. {
  152. updateFieldsList();
  153. m_specifiedReference->SetFocus();
  154. }
  155. void DIALOG_CHANGE_SYMBOLS::onMatchByValue( wxCommandEvent& aEvent )
  156. {
  157. updateFieldsList();
  158. m_specifiedValue->SetFocus();
  159. }
  160. void DIALOG_CHANGE_SYMBOLS::onMatchById( wxCommandEvent& aEvent )
  161. {
  162. updateFieldsList();
  163. m_specifiedId->SetFocus();
  164. }
  165. void DIALOG_CHANGE_SYMBOLS::onMatchTextKillFocus( wxFocusEvent& event )
  166. {
  167. updateFieldsList();
  168. event.Skip(); // Mandatory in wxFocusEvent
  169. }
  170. void DIALOG_CHANGE_SYMBOLS::onMatchIDKillFocus( wxFocusEvent& event )
  171. {
  172. updateFieldsList();
  173. event.Skip(); // Mandatory in wxFocusEvent
  174. }
  175. void DIALOG_CHANGE_SYMBOLS::onNewLibIDKillFocus( wxFocusEvent& event )
  176. {
  177. updateFieldsList();
  178. event.Skip(); // Mandatory in wxFocusEvent
  179. }
  180. DIALOG_CHANGE_SYMBOLS::~DIALOG_CHANGE_SYMBOLS()
  181. {
  182. g_selectRefDes = m_fieldsBox->IsChecked( REFERENCE_FIELD );
  183. g_selectValue = m_fieldsBox->IsChecked( VALUE_FIELD );
  184. g_removeExtraFields[ (int) m_mode ] = m_removeExtraBox->GetValue();
  185. g_resetEmptyFields[ (int) m_mode ] = m_resetEmptyFields->GetValue();
  186. g_resetFieldText[ (int) m_mode ] = m_resetFieldText->GetValue();
  187. g_resetFieldVisibilities[ (int) m_mode ] = m_resetFieldVisibilities->GetValue();
  188. g_resetFieldEffects[ (int) m_mode ] = m_resetFieldEffects->GetValue();
  189. g_resetFieldPositions[ (int) m_mode ] = m_resetFieldPositions->GetValue();
  190. g_resetAttributes[ (int) m_mode ] = m_resetAttributes->GetValue();
  191. g_resetCustomPower[ (int) m_mode ] = m_resetCustomPower->GetValue();
  192. }
  193. wxString getLibIdValue( const wxTextCtrl* aCtrl )
  194. {
  195. wxString rawValue = aCtrl->GetValue();
  196. wxString itemName;
  197. wxString libName = rawValue.BeforeFirst( ':', &itemName );
  198. return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
  199. }
  200. void DIALOG_CHANGE_SYMBOLS::launchMatchIdSymbolBrowser( wxCommandEvent& aEvent )
  201. {
  202. wxString newName = getLibIdValue( m_specifiedId );
  203. if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_SYMBOL_CHOOSER, true, this ) )
  204. {
  205. if( frame->ShowModal( &newName, this ) )
  206. {
  207. m_specifiedId->SetValue( UnescapeString( newName ) );
  208. updateFieldsList();
  209. }
  210. frame->Destroy();
  211. }
  212. }
  213. void DIALOG_CHANGE_SYMBOLS::launchNewIdSymbolBrowser( wxCommandEvent& aEvent )
  214. {
  215. wxString newName = getLibIdValue( m_newId );
  216. if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_SYMBOL_CHOOSER, true, this ) )
  217. {
  218. if( frame->ShowModal( &newName, this ) )
  219. {
  220. m_newId->SetValue( UnescapeString( newName ) );
  221. updateFieldsList();
  222. }
  223. frame->Destroy();
  224. }
  225. }
  226. void DIALOG_CHANGE_SYMBOLS::updateFieldsList()
  227. {
  228. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  229. wxCHECK( frame, /* void */ );
  230. // Load non-mandatory fields from all matching symbols and their library symbols
  231. std::vector<SCH_FIELD*> fields;
  232. std::vector<SCH_FIELD*> libFields;
  233. std::set<wxString> fieldNames;
  234. for( SCH_SHEET_PATH& instance : frame->Schematic().BuildUnorderedSheetList() )
  235. {
  236. SCH_SCREEN* screen = instance.LastScreen();
  237. wxCHECK2( screen, continue );
  238. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  239. {
  240. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  241. wxCHECK2( symbol, continue );
  242. if( !isMatch( symbol, &instance ) )
  243. continue;
  244. fields.clear();
  245. symbol->GetFields( fields, false );
  246. for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
  247. fieldNames.insert( fields[i]->GetName() );
  248. if( m_mode == MODE::UPDATE && symbol->GetLibId().IsValid() )
  249. {
  250. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( symbol->GetLibId() );
  251. if( libSymbol )
  252. {
  253. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  254. flattenedSymbol->GetFields( libFields );
  255. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  256. fieldNames.insert( libFields[i]->GetName() );
  257. libFields.clear(); // flattenedSymbol is about to go out of scope...
  258. }
  259. }
  260. }
  261. }
  262. // Load non-mandatory fields from the change-to library symbol
  263. if( m_mode == MODE::CHANGE )
  264. {
  265. LIB_ID newId;
  266. newId.Parse( getLibIdValue( m_newId ) );
  267. if( newId.IsValid() )
  268. {
  269. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( newId );
  270. if( libSymbol )
  271. {
  272. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  273. flattenedSymbol->GetFields( libFields );
  274. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  275. fieldNames.insert( libFields[i]->GetName() );
  276. libFields.clear(); // flattenedSymbol is about to go out of scope...
  277. }
  278. }
  279. }
  280. // Update the listbox widget
  281. wxArrayInt checkedItems;
  282. wxArrayString checkedNames;
  283. m_fieldsBox->GetCheckedItems( checkedItems );
  284. for( int ii : checkedItems )
  285. checkedNames.push_back( m_fieldsBox->GetString( ii ) );
  286. bool allChecked = true;
  287. for( unsigned ii = 0; ii < m_fieldsBox->GetCount(); ++ii )
  288. {
  289. if( ii == REFERENCE_FIELD || ii == VALUE_FIELD )
  290. continue;
  291. if( !m_fieldsBox->IsChecked( ii ) )
  292. allChecked = false;
  293. }
  294. for( unsigned ii = m_fieldsBox->GetCount() - 1; ii >= MANDATORY_FIELDS; --ii )
  295. m_fieldsBox->Delete( ii );
  296. for( const wxString& fieldName : fieldNames )
  297. {
  298. m_fieldsBox->Append( fieldName );
  299. if( allChecked || alg::contains( checkedNames, fieldName ) )
  300. m_fieldsBox->Check( m_fieldsBox->GetCount() - 1, true );
  301. }
  302. }
  303. void DIALOG_CHANGE_SYMBOLS::checkAll( bool aCheck )
  304. {
  305. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  306. m_fieldsBox->Check( i, aCheck );
  307. }
  308. void DIALOG_CHANGE_SYMBOLS::onOkButtonClicked( wxCommandEvent& aEvent )
  309. {
  310. SCH_EDIT_FRAME* parent = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  311. wxCHECK( parent, /* void */ );
  312. wxBusyCursor dummy;
  313. SCH_COMMIT commit( parent );
  314. m_messagePanel->Clear();
  315. m_messagePanel->Flush( false );
  316. // Create the set of fields to be updated. Use non translated (canonical) names
  317. // for mandatory fields
  318. m_updateFields.clear();
  319. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  320. {
  321. if( m_fieldsBox->IsChecked( i ) )
  322. {
  323. if( i < MANDATORY_FIELDS )
  324. {
  325. SCH_FIELD dummy_field( nullptr, i );
  326. m_updateFields.insert( dummy_field.GetCanonicalName() );
  327. }
  328. else
  329. {
  330. m_updateFields.insert( m_fieldsBox->GetString( i ) );
  331. }
  332. }
  333. }
  334. if( processMatchingSymbols( &commit) )
  335. commit.Push( m_mode == MODE::CHANGE ? _( "Change Symbols" ) : _( "Update Symbols" ) );
  336. m_messagePanel->Flush( false );
  337. }
  338. bool DIALOG_CHANGE_SYMBOLS::isMatch( SCH_SYMBOL* aSymbol, SCH_SHEET_PATH* aInstance )
  339. {
  340. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  341. wxCHECK( frame, false );
  342. if( !aSymbol )
  343. return false;
  344. if( m_matchAll->GetValue() )
  345. {
  346. return true;
  347. }
  348. else if( m_matchBySelection->GetValue() )
  349. {
  350. return aSymbol == m_symbol || aSymbol->IsSelected();
  351. }
  352. else if( m_matchByReference->GetValue() )
  353. {
  354. return WildCompareString( m_specifiedReference->GetValue(),
  355. UnescapeString( aSymbol->GetRef( aInstance, false ) ),
  356. false );
  357. }
  358. else if( m_matchByValue->GetValue() )
  359. {
  360. return WildCompareString( m_specifiedValue->GetValue(),
  361. UnescapeString( aSymbol->GetField( VALUE_FIELD )->GetText() ),
  362. false );
  363. }
  364. else if( m_matchById )
  365. {
  366. LIB_ID id;
  367. id.Parse( getLibIdValue( m_specifiedId ) );
  368. return aSymbol->GetLibId() == id;
  369. }
  370. return false;
  371. }
  372. int DIALOG_CHANGE_SYMBOLS::processMatchingSymbols( SCH_COMMIT* aCommit )
  373. {
  374. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  375. wxCHECK( frame, false );
  376. LIB_ID newId;
  377. wxString msg;
  378. int matchesProcessed = 0;
  379. SCH_SYMBOL* symbol = nullptr;
  380. if( m_mode == MODE::CHANGE )
  381. {
  382. newId.Parse( getLibIdValue( m_newId ) );
  383. if( !newId.IsValid() )
  384. return false;
  385. }
  386. std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO> symbols;
  387. for( SCH_SHEET_PATH& instance : frame->Schematic().BuildSheetListSortedByPageNumbers() )
  388. {
  389. SCH_SCREEN* screen = instance.LastScreen();
  390. wxCHECK2( screen, continue );
  391. // Fetch all the symbols that meet the change criteria.
  392. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  393. {
  394. symbol = static_cast<SCH_SYMBOL*>( item );
  395. wxCHECK2( symbol, continue );
  396. if( !isMatch( symbol, &instance ) )
  397. continue;
  398. if( m_mode == MODE::UPDATE )
  399. newId = symbol->GetLibId();
  400. auto it = symbols.find( symbol );
  401. if( it == symbols.end() )
  402. {
  403. SYMBOL_CHANGE_INFO info;
  404. info.m_Instances.emplace_back( instance );
  405. info.m_LibId = newId;
  406. symbols.insert( { symbol, info } );
  407. }
  408. else
  409. {
  410. it->second.m_Instances.emplace_back( instance );
  411. }
  412. }
  413. }
  414. if( symbols.size() > 0 )
  415. matchesProcessed += processSymbols( aCommit, symbols );
  416. else
  417. m_messagePanel->Report( _( "*** No symbols matching criteria found ***" ),
  418. RPT_SEVERITY_ERROR );
  419. frame->GetCurrentSheet().UpdateAllScreenReferences();
  420. return matchesProcessed;
  421. }
  422. int DIALOG_CHANGE_SYMBOLS::processSymbols( SCH_COMMIT* aCommit,
  423. const std::map<SCH_SYMBOL*,
  424. SYMBOL_CHANGE_INFO>& aSymbols )
  425. {
  426. wxCHECK( !aSymbols.empty(), 0 );
  427. int matchesProcessed = 0;
  428. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  429. wxString msg;
  430. wxCHECK( frame, 0 );
  431. std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO> symbols = aSymbols;
  432. std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO>::iterator it = symbols.begin();
  433. // Remove all symbols that don't have a valid library symbol link or enough units to
  434. // satify the library symbol update.
  435. while( it != symbols.end() )
  436. {
  437. SCH_SYMBOL* symbol = it->first;
  438. wxCHECK2( symbol && it->second.m_LibId.IsValid(), continue );
  439. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( it->second.m_LibId );
  440. if( !libSymbol )
  441. {
  442. msg = getSymbolReferences( *symbol, it->second.m_LibId );
  443. msg << wxT( ": " ) << _( "*** symbol not found ***" );
  444. m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
  445. it = symbols.erase( it );
  446. continue;
  447. }
  448. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  449. if( flattenedSymbol->GetUnitCount() < symbol->GetUnit() )
  450. {
  451. msg = getSymbolReferences( *symbol, it->second.m_LibId );
  452. msg << wxT( ": " ) << _( "*** new symbol has too few units ***" );
  453. m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
  454. it = symbols.erase( it );
  455. }
  456. else
  457. {
  458. ++it;
  459. }
  460. }
  461. // Removing the symbol needs to be done before the LIB_SYMBOL is changed to prevent stale
  462. // library symbols in the schematic file.
  463. for( const auto& [ symbol, symbol_change_info ] : symbols )
  464. {
  465. wxCHECK( symbol && !symbol_change_info.m_Instances.empty(), 0 );
  466. SCH_SCREEN* screen = symbol_change_info.m_Instances[0].LastScreen();
  467. wxCHECK( screen, 0 );
  468. screen->Remove( symbol );
  469. SCH_SYMBOL* symbol_copy = static_cast<SCH_SYMBOL*>( symbol->Clone() );
  470. aCommit->Modified( symbol, symbol_copy, screen );
  471. CONNECTION_GRAPH* connectionGraph = screen->Schematic()->ConnectionGraph();
  472. // When we replace the lib symbol below, we free the associated pins if the new symbol has
  473. // fewer than the original. This will cause the connection graph to be out of date unless
  474. // we replace references in the graph to the old symbol/pins with references to the ones stored
  475. // in the undo stack.
  476. if( connectionGraph )
  477. connectionGraph->ExchangeItem( symbol, symbol_copy );
  478. }
  479. for( const auto& [ symbol, symbol_change_info ] : symbols )
  480. {
  481. // Remember initial link before changing for diags purpose
  482. wxString initialLibLinkName = UnescapeString( symbol->GetLibId().Format() );
  483. if( symbol_change_info.m_LibId != symbol->GetLibId() )
  484. symbol->SetLibId( symbol_change_info.m_LibId );
  485. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( symbol_change_info.m_LibId );
  486. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  487. SCH_SCREEN* screen = symbol_change_info.m_Instances[0].LastScreen();
  488. symbol->SetLibSymbol( flattenedSymbol.release() );
  489. if( m_resetAttributes->GetValue() )
  490. {
  491. // Fetch the attributes from the *flattened* library symbol. They are not supported
  492. // in derived symbols.
  493. symbol->SetExcludedFromSim( symbol->GetLibSymbolRef()->GetExcludedFromSim() );
  494. symbol->SetExcludedFromBOM( symbol->GetLibSymbolRef()->GetExcludedFromBOM() );
  495. symbol->SetExcludedFromBoard( symbol->GetLibSymbolRef()->GetExcludedFromBoard() );
  496. }
  497. if( m_resetPinTextVisibility->GetValue() )
  498. {
  499. symbol->SetShowPinNames( symbol->GetLibSymbolRef()->GetShowPinNames() );
  500. symbol->SetShowPinNumbers( symbol->GetLibSymbolRef()->GetShowPinNumbers() );
  501. }
  502. bool removeExtras = m_removeExtraBox->GetValue();
  503. bool resetVis = m_resetFieldVisibilities->GetValue();
  504. bool resetEffects = m_resetFieldEffects->GetValue();
  505. bool resetPositions = m_resetFieldPositions->GetValue();
  506. for( unsigned i = 0; i < symbol->GetFields().size(); ++i )
  507. {
  508. SCH_FIELD& field = symbol->GetFields()[i];
  509. SCH_FIELD* libField = nullptr;
  510. // Mandatory fields always exist in m_updateFields, but these names can be translated.
  511. // so use GetCanonicalName().
  512. if( !alg::contains( m_updateFields, field.GetCanonicalName() ) )
  513. continue;
  514. if( i < MANDATORY_FIELDS )
  515. libField = symbol->GetLibSymbolRef()->GetFieldById( (int) i );
  516. else
  517. libField = symbol->GetLibSymbolRef()->FindField( field.GetName() );
  518. if( libField )
  519. {
  520. bool resetText = libField->GetText().IsEmpty() ? m_resetEmptyFields->GetValue()
  521. : m_resetFieldText->GetValue();
  522. if( resetText )
  523. {
  524. if( i == REFERENCE_FIELD )
  525. {
  526. wxString prefix = UTIL::GetRefDesPrefix( libField->GetText() );
  527. for( const SCH_SHEET_PATH& instance : symbol_change_info.m_Instances )
  528. {
  529. wxString ref = symbol->GetRef( &instance );
  530. int number = UTIL::GetRefDesNumber( ref );
  531. if( number >= 0 )
  532. ref.Printf( wxS( "%s%d" ), prefix, number );
  533. else
  534. ref = UTIL::GetRefDesUnannotated( prefix );
  535. symbol->SetRef( &instance, ref );
  536. }
  537. }
  538. else if( i == VALUE_FIELD )
  539. {
  540. if( ( symbol->IsPower() && m_resetCustomPower->IsChecked() )
  541. || !symbol->IsPower() )
  542. symbol->SetValueFieldText( UnescapeString( libField->GetText() ) );
  543. }
  544. else if( i == FOOTPRINT_FIELD )
  545. {
  546. symbol->SetFootprintFieldText( libField->GetText() );
  547. }
  548. else
  549. {
  550. field.SetText( libField->GetText() );
  551. }
  552. }
  553. if( resetVis )
  554. field.SetVisible( libField->IsVisible() );
  555. if( resetEffects )
  556. {
  557. // Careful: the visible bit and position are also set by SetAttributes()
  558. bool visible = field.IsVisible();
  559. VECTOR2I pos = field.GetPosition();
  560. field.SetAttributes( *libField );
  561. field.SetVisible( visible );
  562. field.SetPosition( pos );
  563. field.SetNameShown( libField->IsNameShown() );
  564. field.SetCanAutoplace( libField->CanAutoplace() );
  565. }
  566. if( resetPositions )
  567. field.SetTextPos( symbol->GetPosition() + libField->GetTextPos() );
  568. }
  569. else if( i >= MANDATORY_FIELDS && removeExtras )
  570. {
  571. symbol->RemoveField( field.GetName() );
  572. i--;
  573. }
  574. }
  575. std::vector<SCH_FIELD*> libFields;
  576. symbol->GetLibSymbolRef()->GetFields( libFields );
  577. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  578. {
  579. const SCH_FIELD& libField = *libFields[i];
  580. if( !alg::contains( m_updateFields, libField.GetCanonicalName() ) )
  581. continue;
  582. if( !symbol->FindField( libField.GetName(), false ) )
  583. {
  584. wxString fieldName = libField.GetCanonicalName();
  585. SCH_FIELD newField( VECTOR2I( 0, 0 ), symbol->GetFieldCount(), symbol,
  586. fieldName );
  587. SCH_FIELD* schField = symbol->AddField( newField );
  588. // Careful: the visible bit and position are also set by SetAttributes()
  589. schField->SetAttributes( libField );
  590. schField->SetText( libField.GetText() );
  591. schField->SetTextPos( symbol->GetPosition() + libField.GetTextPos() );
  592. }
  593. if( resetPositions && frame->eeconfig()->m_AutoplaceFields.enable )
  594. symbol->AutoAutoplaceFields( screen );
  595. }
  596. symbol->SetSchSymbolLibraryName( wxEmptyString );
  597. screen->Append( symbol );
  598. if( resetPositions )
  599. symbol->AutoAutoplaceFields( screen );
  600. frame->GetCanvas()->GetView()->Update( symbol );
  601. msg = getSymbolReferences( *symbol, symbol_change_info.m_LibId, &initialLibLinkName );
  602. msg += wxS( ": OK" );
  603. m_messagePanel->Report( msg, RPT_SEVERITY_ACTION );
  604. matchesProcessed +=1;
  605. }
  606. return matchesProcessed;
  607. }
  608. wxString DIALOG_CHANGE_SYMBOLS::getSymbolReferences( SCH_SYMBOL& aSymbol,
  609. const LIB_ID& aNewId,
  610. const wxString* aOldLibLinkName )
  611. {
  612. wxString msg;
  613. wxString references;
  614. LIB_ID oldId = aSymbol.GetLibId();
  615. wxString oldLibLinkName; // For report
  616. if( aOldLibLinkName )
  617. oldLibLinkName = *aOldLibLinkName;
  618. else
  619. oldLibLinkName = UnescapeString( oldId.Format() );
  620. SCH_EDIT_FRAME* parent = dynamic_cast< SCH_EDIT_FRAME* >( GetParent() );
  621. wxCHECK( parent, msg );
  622. SCH_SHEET_LIST sheets = parent->Schematic().BuildUnorderedSheetList();
  623. for( const SCH_SYMBOL_INSTANCE& instance : aSymbol.GetInstances() )
  624. {
  625. // Only include the symbol instances for the current project.
  626. if( !sheets.HasPath( instance.m_Path ) )
  627. continue;
  628. if( references.IsEmpty() )
  629. references = instance.m_Reference;
  630. else
  631. references += wxT( " " ) + instance.m_Reference;
  632. }
  633. if( m_mode == MODE::UPDATE )
  634. {
  635. if( aSymbol.GetInstances().size() == 1 )
  636. {
  637. msg.Printf( _( "Update symbol %s from '%s' to '%s'" ),
  638. references,
  639. oldLibLinkName,
  640. UnescapeString( aNewId.Format() ) );
  641. }
  642. else
  643. {
  644. msg.Printf( _( "Update symbols %s from '%s' to '%s'" ),
  645. references,
  646. oldLibLinkName,
  647. UnescapeString( aNewId.Format() ) );
  648. }
  649. }
  650. else // mode is MODE::CHANGE
  651. {
  652. if( aSymbol.GetInstances().size() == 1 )
  653. {
  654. msg.Printf( _( "Change symbol %s from '%s' to '%s'" ),
  655. references,
  656. oldLibLinkName,
  657. UnescapeString( aNewId.Format() ) );
  658. }
  659. else
  660. {
  661. msg.Printf( _( "Change symbols %s from '%s' to '%s'" ),
  662. references,
  663. oldLibLinkName,
  664. UnescapeString( aNewId.Format() ) );
  665. }
  666. }
  667. return msg;
  668. }