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.

759 lines
25 KiB

3 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 Alexander Shuklin <Jasuramme@gmail.com>
  5. * Copyright (C) 2004-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <backannotate.h>
  25. #include <boost/property_tree/ptree.hpp>
  26. #include <confirm.h>
  27. #include <dsnlexer.h>
  28. #include <ptree.h>
  29. #include <reporter.h>
  30. #include <sch_edit_frame.h>
  31. #include <sch_sheet_path.h>
  32. #include <sch_label.h>
  33. #include <schematic.h>
  34. #include <sch_commit.h>
  35. #include <string_utils.h>
  36. #include <kiface_base.h>
  37. #include <wildcards_and_files_ext.h>
  38. #include <connection_graph.h>
  39. #include <wx/log.h>
  40. BACK_ANNOTATE::BACK_ANNOTATE( SCH_EDIT_FRAME* aFrame, REPORTER& aReporter, bool aRelinkFootprints,
  41. bool aProcessFootprints, bool aProcessValues,
  42. bool aProcessReferences, bool aProcessNetNames,
  43. bool aProcessAttributes, bool aProcessOtherFields,
  44. bool aDryRun ) :
  45. m_reporter( aReporter ),
  46. m_matchByReference( aRelinkFootprints ),
  47. m_processFootprints( aProcessFootprints ),
  48. m_processValues( aProcessValues ),
  49. m_processReferences( aProcessReferences ),
  50. m_processNetNames( aProcessNetNames ),
  51. m_processAttributes( aProcessAttributes ),
  52. m_processOtherFields( aProcessOtherFields ),
  53. m_dryRun( aDryRun ),
  54. m_frame( aFrame ),
  55. m_changesCount( 0 )
  56. {
  57. }
  58. BACK_ANNOTATE::~BACK_ANNOTATE()
  59. {
  60. }
  61. bool BACK_ANNOTATE::BackAnnotateSymbols( const std::string& aNetlist )
  62. {
  63. m_changesCount = 0;
  64. if( !m_matchByReference && !m_processValues && !m_processFootprints && !m_processReferences
  65. && !m_processNetNames && !m_processAttributes )
  66. {
  67. m_reporter.ReportTail( _( "Select at least one property to back annotate." ),
  68. RPT_SEVERITY_ERROR );
  69. return false;
  70. }
  71. getPcbModulesFromString( aNetlist );
  72. SCH_SHEET_LIST sheets = m_frame->Schematic().GetSheets();
  73. sheets.GetSymbols( m_refs, false );
  74. sheets.GetMultiUnitSymbols( m_multiUnitsRefs );
  75. getChangeList();
  76. checkForUnusedSymbols();
  77. applyChangelist();
  78. return true;
  79. }
  80. bool BACK_ANNOTATE::FetchNetlistFromPCB( std::string& aNetlist )
  81. {
  82. if( Kiface().IsSingle() )
  83. {
  84. DisplayErrorMessage( m_frame, _( "Cannot fetch PCB netlist because Schematic Editor is opened "
  85. "in stand-alone mode.\n"
  86. "You must launch the KiCad project manager and create "
  87. "a project." ) );
  88. return false;
  89. }
  90. KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
  91. if( !frame )
  92. {
  93. wxFileName fn( m_frame->Prj().GetProjectFullName() );
  94. fn.SetExt( PcbFileExtension );
  95. frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
  96. frame->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) );
  97. }
  98. m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_GET_NETLIST, aNetlist );
  99. return true;
  100. }
  101. void BACK_ANNOTATE::PushNewLinksToPCB()
  102. {
  103. std::string nullPayload;
  104. m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_UPDATE_LINKS, nullPayload );
  105. }
  106. void BACK_ANNOTATE::getPcbModulesFromString( const std::string& aPayload )
  107. {
  108. auto getStr = []( const PTREE& pt ) -> wxString
  109. {
  110. return UTF8( pt.front().first );
  111. };
  112. DSNLEXER lexer( aPayload, From_UTF8( __func__ ) );
  113. PTREE doc;
  114. // NOTE: KiCad's PTREE scanner constructs a property *name* tree, not a property tree.
  115. // Every token in the s-expr is stored as a property name; the property's value is then
  116. // either the nested s-exprs or an empty PTREE; there are *no* literal property values.
  117. Scan( &doc, &lexer );
  118. PTREE& tree = doc.get_child( "pcb_netlist" );
  119. wxString msg;
  120. m_pcbFootprints.clear();
  121. for( const std::pair<const std::string, PTREE>& item : tree )
  122. {
  123. wxString path, value, footprint;
  124. bool dnp = false, exBOM = false;
  125. std::map<wxString, wxString> pinNetMap, fieldsMap;
  126. wxASSERT( item.first == "ref" );
  127. wxString ref = getStr( item.second );
  128. try
  129. {
  130. if( m_matchByReference )
  131. path = ref;
  132. else
  133. path = getStr( item.second.get_child( "timestamp" ) );
  134. if( path == "" )
  135. {
  136. msg.Printf( _( "Footprint '%s' has no assigned symbol." ), ref );
  137. m_reporter.ReportHead( msg, RPT_SEVERITY_WARNING );
  138. continue;
  139. }
  140. footprint = getStr( item.second.get_child( "fpid" ) );
  141. value = getStr( item.second.get_child( "value" ) );
  142. // Get child PTREE of fields
  143. boost::optional<const PTREE&> fields = item.second.get_child_optional( "fields" );
  144. // Parse each field out of the fields string
  145. if( fields )
  146. {
  147. for( const std::pair<const std::string, PTREE>& field : fields.get() )
  148. {
  149. if( field.first != "field" )
  150. continue;
  151. // Fields are of the format "(field (name "name") "12345")
  152. const auto& fieldName = field.second.get_child_optional( "name" );
  153. const auto& fieldValue = field.second.back().first;
  154. if( !fieldName )
  155. continue;
  156. fieldsMap[getStr( fieldName.get() )] = fieldValue;
  157. }
  158. }
  159. // Get DNP and Exclude from BOM out of the properties if they exist
  160. for( const auto& child : item.second )
  161. {
  162. if( child.first != "property" )
  163. continue;
  164. auto property = child.second;
  165. auto name = property.get_child_optional( "name" );
  166. if( !name )
  167. continue;
  168. if( name.get().front().first == "dnp" )
  169. {
  170. dnp = true;
  171. }
  172. else if( name.get().front().first == "exclude_from_bom" )
  173. {
  174. exBOM = true;
  175. }
  176. }
  177. boost::optional<const PTREE&> nets = item.second.get_child_optional( "nets" );
  178. if( nets )
  179. {
  180. for( const std::pair<const std::string, PTREE>& pin_net : nets.get() )
  181. {
  182. wxASSERT( pin_net.first == "pin_net" );
  183. wxString pinNumber = UTF8( pin_net.second.front().first );
  184. wxString netName = UTF8( pin_net.second.back().first );
  185. pinNetMap[ pinNumber ] = netName;
  186. }
  187. }
  188. }
  189. catch( ... )
  190. {
  191. wxLogWarning( "Cannot parse PCB netlist for back-annotation." );
  192. }
  193. // Use lower_bound for not to iterate over map twice
  194. auto nearestItem = m_pcbFootprints.lower_bound( path );
  195. if( nearestItem != m_pcbFootprints.end() && nearestItem->first == path )
  196. {
  197. // Module with this path already exists - generate error
  198. msg.Printf( _( "Footprints '%s' and '%s' linked to same symbol." ),
  199. nearestItem->second->m_ref,
  200. ref );
  201. m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
  202. }
  203. else
  204. {
  205. // Add footprint to the map
  206. auto data = std::make_shared<PCB_FP_DATA>( ref, footprint, value, dnp, exBOM,
  207. pinNetMap, fieldsMap );
  208. m_pcbFootprints.insert( nearestItem, std::make_pair( path, data ) );
  209. }
  210. }
  211. }
  212. void BACK_ANNOTATE::getChangeList()
  213. {
  214. for( std::pair<const wxString, std::shared_ptr<PCB_FP_DATA>>& fpData : m_pcbFootprints )
  215. {
  216. const wxString& pcbPath = fpData.first;
  217. auto& pcbData = fpData.second;
  218. int refIndex;
  219. bool foundInMultiunit = false;
  220. for( std::pair<const wxString, SCH_REFERENCE_LIST>& item : m_multiUnitsRefs )
  221. {
  222. SCH_REFERENCE_LIST& refList = item.second;
  223. if( m_matchByReference )
  224. refIndex = refList.FindRef( pcbPath );
  225. else
  226. refIndex = refList.FindRefByFullPath( pcbPath );
  227. if( refIndex >= 0 )
  228. {
  229. // If footprint linked to multi unit symbol, we add all symbol's units to
  230. // the change list
  231. foundInMultiunit = true;
  232. for( size_t i = 0; i < refList.GetCount(); ++i )
  233. {
  234. refList[ i ].GetSymbol()->ClearFlags(SKIP_STRUCT );
  235. m_changelist.emplace_back( CHANGELIST_ITEM( refList[i], pcbData ) );
  236. }
  237. break;
  238. }
  239. }
  240. if( foundInMultiunit )
  241. continue;
  242. if( m_matchByReference )
  243. refIndex = m_refs.FindRef( pcbPath );
  244. else
  245. refIndex = m_refs.FindRefByFullPath( pcbPath );
  246. if( refIndex >= 0 )
  247. {
  248. m_refs[ refIndex ].GetSymbol()->ClearFlags( SKIP_STRUCT );
  249. m_changelist.emplace_back( CHANGELIST_ITEM( m_refs[refIndex], pcbData ) );
  250. }
  251. else
  252. {
  253. // Haven't found linked symbol in multiunits or common refs. Generate error
  254. wxString msg = wxString::Format( _( "Cannot find symbol for footprint '%s'." ),
  255. pcbData->m_ref );
  256. m_reporter.ReportTail( msg, RPT_SEVERITY_ERROR );
  257. }
  258. }
  259. }
  260. void BACK_ANNOTATE::checkForUnusedSymbols()
  261. {
  262. m_refs.SortByTimeStamp();
  263. std::sort( m_changelist.begin(), m_changelist.end(),
  264. []( const CHANGELIST_ITEM& a, const CHANGELIST_ITEM& b )
  265. {
  266. return SCH_REFERENCE_LIST::sortByTimeStamp( a.first, b.first );
  267. } );
  268. size_t i = 0;
  269. for( const std::pair<SCH_REFERENCE, std::shared_ptr<PCB_FP_DATA>>& item : m_changelist )
  270. {
  271. // Refs and changelist are both sorted by paths, so we just go over m_refs and
  272. // generate errors before we will find m_refs member to which item linked
  273. while( i < m_refs.GetCount() && m_refs[i].GetPath() != item.first.GetPath() )
  274. {
  275. const SCH_REFERENCE& ref = m_refs[i];
  276. if( ref.GetSymbol()->GetExcludedFromBoard() )
  277. {
  278. wxString msg = wxString::Format( _( "Footprint '%s' is not present on PCB. "
  279. "Corresponding symbols in schematic must be "
  280. "manually deleted (if desired)." ),
  281. m_refs[i].GetRef() );
  282. m_reporter.ReportTail( msg, RPT_SEVERITY_WARNING );
  283. }
  284. ++i;
  285. }
  286. ++i;
  287. }
  288. if( m_matchByReference && !m_frame->ReadyToNetlist( _( "Re-linking footprints requires a fully "
  289. "annotated schematic." ) ) )
  290. {
  291. m_reporter.ReportTail( _( "Footprint re-linking cancelled by user." ), RPT_SEVERITY_ERROR );
  292. }
  293. }
  294. void BACK_ANNOTATE::applyChangelist()
  295. {
  296. SCH_COMMIT commit( m_frame );
  297. wxString msg;
  298. // Apply changes from change list
  299. for( CHANGELIST_ITEM& item : m_changelist )
  300. {
  301. SCH_REFERENCE& ref = item.first;
  302. PCB_FP_DATA& fpData = *item.second;
  303. SCH_SYMBOL* symbol = ref.GetSymbol();
  304. SCH_SCREEN* screen = ref.GetSheetPath().LastScreen();
  305. wxString oldFootprint = ref.GetFootprint();
  306. wxString oldValue = ref.GetValue();
  307. bool oldDNP = ref.GetSymbol()->GetDNP();
  308. bool oldExBOM = ref.GetSymbol()->GetExcludedFromBOM();
  309. bool skip = ( ref.GetSymbol()->GetFlags() & SKIP_STRUCT ) > 0;
  310. auto boolString = []( bool b ) -> wxString
  311. {
  312. return b ? _( "true" ) : _( "false" );
  313. };
  314. if( m_processReferences && ref.GetRef() != fpData.m_ref && !skip )
  315. {
  316. ++m_changesCount;
  317. msg.Printf( _( "Change '%s' reference designator to '%s'." ),
  318. ref.GetRef(),
  319. fpData.m_ref );
  320. if( !m_dryRun )
  321. {
  322. commit.Modify( symbol, screen );
  323. symbol->SetRef( &ref.GetSheetPath(), fpData.m_ref );
  324. }
  325. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  326. }
  327. if( m_processFootprints && oldFootprint != fpData.m_footprint && !skip )
  328. {
  329. ++m_changesCount;
  330. msg.Printf( _( "Change %s footprint assignment from '%s' to '%s'." ),
  331. ref.GetRef(),
  332. oldFootprint,
  333. fpData.m_footprint );
  334. if( !m_dryRun )
  335. {
  336. commit.Modify( symbol, screen );
  337. symbol->SetFootprintFieldText( fpData.m_footprint );
  338. }
  339. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  340. }
  341. if( m_processValues && oldValue != fpData.m_value && !skip )
  342. {
  343. ++m_changesCount;
  344. msg.Printf( _( "Change %s value from '%s' to '%s'." ),
  345. ref.GetRef(),
  346. oldValue,
  347. fpData.m_value );
  348. if( !m_dryRun )
  349. {
  350. commit.Modify( symbol, screen );
  351. symbol->SetValueFieldText( fpData.m_value );
  352. }
  353. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  354. }
  355. if( m_processAttributes && oldDNP != fpData.m_DNP && !skip )
  356. {
  357. ++m_changesCount;
  358. msg.Printf( _( "Change %s 'Do not populate' from '%s' to '%s'." ), ref.GetRef(),
  359. boolString( oldDNP ), boolString( fpData.m_DNP ) );
  360. if( !m_dryRun )
  361. {
  362. commit.Modify( symbol, screen );
  363. symbol->SetDNP( fpData.m_DNP );
  364. }
  365. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  366. }
  367. if( m_processAttributes && oldExBOM != fpData.m_excludeFromBOM && !skip )
  368. {
  369. ++m_changesCount;
  370. msg.Printf( _( "Change %s 'Exclude from bill of materials' from '%s' to '%s'." ),
  371. ref.GetRef(), boolString( oldExBOM ),
  372. boolString( fpData.m_excludeFromBOM ) );
  373. if( !m_dryRun )
  374. {
  375. commit.Modify( symbol, screen );
  376. symbol->SetExcludedFromBOM( fpData.m_excludeFromBOM );
  377. }
  378. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  379. }
  380. if( m_processNetNames )
  381. {
  382. for( const std::pair<const wxString, wxString>& entry : fpData.m_pinMap )
  383. {
  384. const wxString& pinNumber = entry.first;
  385. const wxString& shortNetName = entry.second;
  386. SCH_PIN* pin = symbol->GetPin( pinNumber );
  387. if( !pin )
  388. {
  389. msg.Printf( _( "Cannot find %s pin '%s'." ),
  390. ref.GetRef(),
  391. pinNumber );
  392. m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
  393. continue;
  394. }
  395. SCH_CONNECTION* connection = pin->Connection( &ref.GetSheetPath() );
  396. if( connection && connection->Name( true ) != shortNetName )
  397. {
  398. processNetNameChange( &commit, ref.GetRef(), pin, connection,
  399. connection->Name( true ), shortNetName );
  400. }
  401. }
  402. }
  403. if( m_processOtherFields )
  404. {
  405. // Need to handle three cases: existing field, new field, deleted field
  406. for( const std::pair<const wxString, wxString>& field : fpData.m_fieldsMap )
  407. {
  408. const wxString& fpFieldName = field.first;
  409. const wxString& fpFieldValue = field.second;
  410. SCH_FIELD* symField = symbol->FindField( fpFieldName );
  411. // Skip fields that are individually controlled
  412. if( fpFieldName == GetCanonicalFieldName( REFERENCE_FIELD )
  413. || fpFieldName == GetCanonicalFieldName( VALUE_FIELD )
  414. || fpFieldName == GetCanonicalFieldName( FOOTPRINT_FIELD ) )
  415. {
  416. continue;
  417. }
  418. // 1. Existing fields has changed value
  419. // PCB Field value is checked against the shown text because this is the value
  420. // with all the variables resolved. The footprints field value gets the symbol's
  421. // resolved value when the PCB is updated from the schematic.
  422. if( symField
  423. && symField->GetShownText( &ref.GetSheetPath(), false ) != fpFieldValue )
  424. {
  425. m_changesCount++;
  426. msg.Printf( _( "Change field '%s' value to '%s'." ),
  427. symField->GetCanonicalName(), fpFieldValue );
  428. if( !m_dryRun )
  429. {
  430. commit.Modify( symbol, screen );
  431. symField->SetText( fpFieldValue );
  432. }
  433. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  434. }
  435. // 2. New field has been added to footprint and needs to be added to symbol
  436. if( symField == nullptr )
  437. {
  438. m_changesCount++;
  439. msg.Printf( _( "Add field '%s' with value '%s'." ), fpFieldName, fpFieldValue );
  440. if( !m_dryRun )
  441. {
  442. commit.Modify( symbol, screen );
  443. SCH_FIELD newField( VECTOR2I( 0, 0 ), symbol->GetFieldCount(), symbol,
  444. fpFieldName );
  445. newField.SetText( fpFieldValue );
  446. symbol->AddField( newField );
  447. }
  448. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  449. }
  450. }
  451. // 3. Existing field has been deleted from footprint and needs to be deleted from symbol
  452. // Check all symbol fields for existence in the footprint field map
  453. for( SCH_FIELD& field : symbol->GetFields() )
  454. {
  455. // Never delete mandatory fields
  456. if( field.GetId() < MANDATORY_FIELDS )
  457. continue;
  458. if( fpData.m_fieldsMap.find( field.GetCanonicalName() )
  459. == fpData.m_fieldsMap.end() )
  460. {
  461. // Field not found in footprint field map, delete it
  462. m_changesCount++;
  463. msg.Printf( _( "Delete field '%s.'" ), field.GetCanonicalName() );
  464. if( !m_dryRun )
  465. {
  466. commit.Modify( symbol, screen );
  467. symbol->RemoveField( &field );
  468. }
  469. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  470. }
  471. }
  472. }
  473. // TODO: back-annotate netclass changes?
  474. }
  475. if( !m_dryRun )
  476. {
  477. m_frame->RecalculateConnections( &commit, NO_CLEANUP );
  478. m_frame->UpdateNetHighlightStatus();
  479. commit.Push( _( "Update Schematic from PCB" ) );
  480. }
  481. }
  482. static SPIN_STYLE orientLabel( SCH_PIN* aPin )
  483. {
  484. SPIN_STYLE spin = SPIN_STYLE::RIGHT;
  485. // Initial orientation from the pin
  486. switch( aPin->GetLibPin()->GetOrientation() )
  487. {
  488. case PIN_ORIENTATION::PIN_UP: spin = SPIN_STYLE::BOTTOM; break;
  489. case PIN_ORIENTATION::PIN_DOWN: spin = SPIN_STYLE::UP; break;
  490. case PIN_ORIENTATION::PIN_LEFT: spin = SPIN_STYLE::RIGHT; break;
  491. case PIN_ORIENTATION::PIN_RIGHT: spin = SPIN_STYLE::LEFT; break;
  492. }
  493. // Reorient based on the actual symbol orientation now
  494. struct ORIENT
  495. {
  496. int flag;
  497. int n_rots;
  498. int mirror_x;
  499. int mirror_y;
  500. }
  501. orientations[] =
  502. {
  503. { SYM_ORIENT_0, 0, 0, 0 },
  504. { SYM_ORIENT_90, 1, 0, 0 },
  505. { SYM_ORIENT_180, 2, 0, 0 },
  506. { SYM_ORIENT_270, 3, 0, 0 },
  507. { SYM_MIRROR_X + SYM_ORIENT_0, 0, 1, 0 },
  508. { SYM_MIRROR_X + SYM_ORIENT_90, 1, 1, 0 },
  509. { SYM_MIRROR_Y, 0, 0, 1 },
  510. { SYM_MIRROR_X + SYM_ORIENT_270, 3, 1, 0 },
  511. { SYM_MIRROR_Y + SYM_ORIENT_0, 0, 0, 1 },
  512. { SYM_MIRROR_Y + SYM_ORIENT_90, 1, 0, 1 },
  513. { SYM_MIRROR_Y + SYM_ORIENT_180, 2, 0, 1 },
  514. { SYM_MIRROR_Y + SYM_ORIENT_270, 3, 0, 1 }
  515. };
  516. ORIENT o = orientations[ 0 ];
  517. SCH_SYMBOL* parentSymbol = aPin->GetParentSymbol();
  518. if( !parentSymbol )
  519. return spin;
  520. int symbolOrientation = parentSymbol->GetOrientation();
  521. for( auto& i : orientations )
  522. {
  523. if( i.flag == symbolOrientation )
  524. {
  525. o = i;
  526. break;
  527. }
  528. }
  529. for( int i = 0; i < o.n_rots; i++ )
  530. spin = spin.RotateCCW();
  531. if( o.mirror_x )
  532. spin = spin.MirrorX();
  533. if( o.mirror_y )
  534. spin = spin.MirrorY();
  535. return spin;
  536. }
  537. void addConnections( SCH_ITEM* aItem, const SCH_SHEET_PATH& aSheetPath,
  538. std::set<SCH_ITEM*>& connectedItems )
  539. {
  540. if( connectedItems.insert( aItem ).second )
  541. {
  542. for( SCH_ITEM* connectedItem : aItem->ConnectedItems( aSheetPath ) )
  543. addConnections( connectedItem, aSheetPath, connectedItems );
  544. }
  545. }
  546. void BACK_ANNOTATE::processNetNameChange( SCH_COMMIT* aCommit, const wxString& aRef, SCH_PIN* aPin,
  547. const SCH_CONNECTION* aConnection,
  548. const wxString& aOldName, const wxString& aNewName )
  549. {
  550. wxString msg;
  551. // Find a physically-connected driver. We can't use the SCH_CONNECTION's m_driver because
  552. // it has already been resolved by merging subgraphs with the same label, etc., and our
  553. // name change may cause that resolution to change.
  554. std::set<SCH_ITEM*> connectedItems;
  555. SCH_ITEM* driver = nullptr;
  556. CONNECTION_SUBGRAPH::PRIORITY driverPriority = CONNECTION_SUBGRAPH::PRIORITY::NONE;
  557. addConnections( aPin, aConnection->Sheet(), connectedItems );
  558. for( SCH_ITEM* item : connectedItems )
  559. {
  560. CONNECTION_SUBGRAPH::PRIORITY priority = CONNECTION_SUBGRAPH::GetDriverPriority( item );
  561. if( priority > driverPriority )
  562. {
  563. driver = item;
  564. driverPriority = priority;
  565. }
  566. }
  567. switch( driver->Type() )
  568. {
  569. case SCH_LABEL_T:
  570. case SCH_GLOBAL_LABEL_T:
  571. case SCH_HIER_LABEL_T:
  572. case SCH_SHEET_PIN_T:
  573. ++m_changesCount;
  574. msg.Printf( _( "Change %s pin %s net label from '%s' to '%s'." ),
  575. aRef,
  576. aPin->GetShownNumber(),
  577. aOldName,
  578. aNewName );
  579. if( !m_dryRun )
  580. {
  581. aCommit->Modify( driver, aConnection->Sheet().LastScreen() );
  582. static_cast<SCH_LABEL_BASE*>( driver )->SetText( aNewName );
  583. }
  584. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  585. break;
  586. case SCH_PIN_T:
  587. {
  588. SCH_PIN* schPin = static_cast<SCH_PIN*>( driver );
  589. SPIN_STYLE spin = orientLabel( schPin );
  590. if( schPin->IsGlobalPower() )
  591. {
  592. msg.Printf( _( "Net %s cannot be changed to %s because it is driven by a power pin." ),
  593. aOldName,
  594. aNewName );
  595. m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
  596. break;
  597. }
  598. ++m_changesCount;
  599. msg.Printf( _( "Add label '%s' to %s pin %s net." ),
  600. aNewName,
  601. aRef,
  602. aPin->GetShownNumber() );
  603. if( !m_dryRun )
  604. {
  605. SCHEMATIC_SETTINGS& settings = m_frame->Schematic().Settings();
  606. SCH_LABEL* label = new SCH_LABEL( driver->GetPosition(), aNewName );
  607. label->SetParent( &m_frame->Schematic() );
  608. label->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
  609. label->SetSpinStyle( spin );
  610. label->SetFlags( IS_NEW );
  611. SCH_SCREEN* screen = aConnection->Sheet().LastScreen();
  612. aCommit->Add( label, screen );
  613. }
  614. m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
  615. }
  616. break;
  617. default:
  618. break;
  619. }
  620. }