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.

661 lines
20 KiB

  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 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 <kicad_string.h> // WildCompareString
  25. #include <kiway.h>
  26. #include <lib_id.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 <sch_sheet_path.h>
  34. #include <schematic.h>
  35. #include <template_fieldnames.h>
  36. #include <wx_html_report_panel.h>
  37. bool g_selectRefDes = false;
  38. bool g_selectValue = false;
  39. // { change, update }
  40. bool g_removeExtraFields[2] = { false, false };
  41. bool g_resetEmptyFields[2] = { false, false };
  42. bool g_resetFieldText[2] = { true, true };
  43. bool g_resetFieldVisibilities[2] = { true, false };
  44. bool g_resetFieldEffects[2] = { true, false };
  45. bool g_resetFieldPositions[2] = { true, false };
  46. bool g_resetAttributes[2] = { true, false };
  47. DIALOG_CHANGE_SYMBOLS::DIALOG_CHANGE_SYMBOLS( SCH_EDIT_FRAME* aParent, SCH_SYMBOL* aSymbol,
  48. MODE aMode ) :
  49. DIALOG_CHANGE_SYMBOLS_BASE( aParent ),
  50. m_symbol( aSymbol),
  51. m_mode( aMode )
  52. {
  53. wxASSERT( aParent );
  54. if( m_mode == MODE::UPDATE )
  55. {
  56. m_newIdSizer->Show( false );
  57. }
  58. else
  59. {
  60. m_matchAll->SetLabel( _( "Change all symbols in schematic" ) );
  61. SetTitle( _( "Change Symbols" ) );
  62. m_matchSizer->FindItem( m_matchAll )->Show( false );
  63. }
  64. if( m_symbol )
  65. {
  66. SCH_SHEET_PATH* currentSheet = &aParent->Schematic().CurrentSheet();
  67. if( m_mode == MODE::CHANGE )
  68. m_matchBySelection->SetLabel( _( "Change selected symbol(s)" ) );
  69. m_newId->AppendText( FROM_UTF8( m_symbol->GetLibId().Format().c_str() ) );
  70. m_specifiedReference->ChangeValue( m_symbol->GetRef( currentSheet ) );
  71. m_specifiedValue->ChangeValue( m_symbol->GetValue( currentSheet, false ) );
  72. m_specifiedId->ChangeValue( FROM_UTF8( m_symbol->GetLibId().Format().c_str() ) );
  73. }
  74. else
  75. {
  76. m_matchSizer->FindItem( m_matchBySelection )->Show( false );
  77. }
  78. m_matchIdBrowserButton->SetBitmap( KiBitmap( BITMAPS::small_library ) );
  79. m_newIdBrowserButton->SetBitmap( KiBitmap( BITMAPS::small_library ) );
  80. if( m_mode == MODE::CHANGE )
  81. {
  82. m_matchByReference->SetLabel( _( "Change symbols matching reference designator:" ) );
  83. m_matchByValue->SetLabel( _( "Change symbols matching value:" ) );
  84. m_matchById->SetLabel( _( "Change symbols matching library identifier:" ) );
  85. }
  86. m_matchSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
  87. m_matchSizer->Layout();
  88. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  89. {
  90. m_fieldsBox->Append( TEMPLATE_FIELDNAME::GetDefaultFieldName( i ) );
  91. if( i == REFERENCE_FIELD )
  92. m_fieldsBox->Check( i, g_selectRefDes );
  93. else if( i == VALUE_FIELD )
  94. m_fieldsBox->Check( i, g_selectValue );
  95. else
  96. m_fieldsBox->Check( i, true );
  97. }
  98. m_messagePanel->SetLazyUpdate( true );
  99. m_messagePanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
  100. if( aSymbol && aSymbol->IsSelected() )
  101. {
  102. m_matchBySelection->SetValue( true );
  103. }
  104. else
  105. {
  106. if( aMode == MODE::UPDATE )
  107. m_matchAll->SetValue( true );
  108. else
  109. m_matchByReference->SetValue( true );
  110. }
  111. updateFieldsList();
  112. if( m_mode == MODE::CHANGE )
  113. {
  114. m_updateFieldsSizer->GetStaticBox()->SetLabel( _( "Update Fields" ) );
  115. m_removeExtraBox->SetLabel( _( "Remove fields if not in new symbol" ) );
  116. m_resetEmptyFields->SetLabel( _( "Reset fields if empty in new symbol" ) );
  117. m_resetFieldText->SetLabel( _( "Update field text" ) );
  118. m_resetFieldVisibilities->SetLabel( _( "Update field visibilities" ) );
  119. m_resetFieldEffects->SetLabel( _( "Update field sizes and styles" ) );
  120. m_resetFieldPositions->SetLabel( _( "Update field positions" ) );
  121. m_resetAttributes->SetLabel( _( "Update symbol attributes" ) );
  122. }
  123. m_removeExtraBox->SetValue( g_removeExtraFields[ (int) m_mode ] );
  124. m_resetEmptyFields->SetValue( g_resetEmptyFields[ (int) m_mode ] );
  125. m_resetFieldText->SetValue( g_resetFieldText[ (int) m_mode ] );
  126. m_resetFieldVisibilities->SetValue( g_resetFieldVisibilities[ (int) m_mode ] );
  127. m_resetFieldEffects->SetValue( g_resetFieldEffects[ (int) m_mode ] );
  128. m_resetFieldPositions->SetValue( g_resetFieldPositions[ (int) m_mode ] );
  129. m_resetAttributes->SetValue( g_resetAttributes[ (int) m_mode ] );
  130. // DIALOG_SHIM needs a unique hash_key because classname is not sufficient
  131. // because the update and change versions of this dialog have different controls.
  132. m_hash_key = TO_UTF8( GetTitle() );
  133. // Ensure m_closeButton (with id = wxID_CANCEL) has the right label
  134. // (to fix automatic renaming of button label )
  135. m_sdbSizerCancel->SetLabel( _( "Close" ) );
  136. if( m_mode == MODE::CHANGE )
  137. m_sdbSizerOK->SetLabel( _( "Change" ) );
  138. else
  139. m_sdbSizerOK->SetLabel( _( "Update" ) );
  140. m_sdbSizerOK->SetDefault();
  141. // Now all widgets have the size fixed, call FinishDialogSettings
  142. finishDialogSettings();
  143. }
  144. void DIALOG_CHANGE_SYMBOLS::onMatchByAll( wxCommandEvent& aEvent )
  145. {
  146. updateFieldsList();
  147. }
  148. void DIALOG_CHANGE_SYMBOLS::onMatchBySelected( wxCommandEvent& aEvent )
  149. {
  150. updateFieldsList();
  151. }
  152. void DIALOG_CHANGE_SYMBOLS::onMatchByReference( wxCommandEvent& aEvent )
  153. {
  154. updateFieldsList();
  155. m_specifiedReference->SetFocus();
  156. }
  157. void DIALOG_CHANGE_SYMBOLS::onMatchByValue( wxCommandEvent& aEvent )
  158. {
  159. updateFieldsList();
  160. m_specifiedValue->SetFocus();
  161. }
  162. void DIALOG_CHANGE_SYMBOLS::onMatchById( wxCommandEvent& aEvent )
  163. {
  164. updateFieldsList();
  165. m_specifiedId->SetFocus();
  166. }
  167. void DIALOG_CHANGE_SYMBOLS::onMatchTextKillFocus( wxFocusEvent& event )
  168. {
  169. updateFieldsList();
  170. }
  171. DIALOG_CHANGE_SYMBOLS::~DIALOG_CHANGE_SYMBOLS()
  172. {
  173. g_selectRefDes = m_fieldsBox->IsChecked( REFERENCE_FIELD );
  174. g_selectValue = m_fieldsBox->IsChecked( VALUE_FIELD );
  175. g_removeExtraFields[ (int) m_mode ] = m_removeExtraBox->GetValue();
  176. g_resetEmptyFields[ (int) m_mode ] = m_resetEmptyFields->GetValue();
  177. g_resetFieldText[ (int) m_mode ] = m_resetFieldText->GetValue();
  178. g_resetFieldVisibilities[ (int) m_mode ] = m_resetFieldVisibilities->GetValue();
  179. g_resetFieldEffects[ (int) m_mode ] = m_resetFieldEffects->GetValue();
  180. g_resetFieldPositions[ (int) m_mode ] = m_resetFieldPositions->GetValue();
  181. g_resetAttributes[ (int) m_mode ] = m_resetAttributes->GetValue();
  182. }
  183. void DIALOG_CHANGE_SYMBOLS::launchMatchIdSymbolBrowser( wxCommandEvent& aEvent )
  184. {
  185. wxString newName = m_specifiedId->GetValue();
  186. KIWAY_PLAYER* frame = Kiway().Player( FRAME_SCH_VIEWER_MODAL, true );
  187. if( frame->ShowModal( &newName, this ) )
  188. {
  189. m_specifiedId->SetValue( newName );
  190. updateFieldsList();
  191. }
  192. frame->Destroy();
  193. }
  194. void DIALOG_CHANGE_SYMBOLS::launchNewIdSymbolBrowser( wxCommandEvent& aEvent )
  195. {
  196. wxString newName = m_newId->GetValue();
  197. KIWAY_PLAYER* frame = Kiway().Player( FRAME_SCH_VIEWER_MODAL, true );
  198. if( frame->ShowModal( &newName, this ) )
  199. {
  200. m_newId->SetValue( newName );
  201. updateFieldsList();
  202. }
  203. frame->Destroy();
  204. }
  205. void DIALOG_CHANGE_SYMBOLS::updateFieldsList()
  206. {
  207. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  208. wxCHECK( frame, /* void */ );
  209. SCH_SHEET_LIST hierarchy = frame->Schematic().GetSheets();
  210. // Load non-mandatory fields from all matching symbols and their library symbols
  211. std::vector<SCH_FIELD*> fields;
  212. std::vector<LIB_FIELD*> libFields;
  213. std::set<wxString> fieldNames;
  214. for( SCH_SHEET_PATH& instance : hierarchy )
  215. {
  216. SCH_SCREEN* screen = instance.LastScreen();
  217. wxCHECK2( screen, continue );
  218. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  219. {
  220. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
  221. wxCHECK2( symbol, continue );
  222. if( !isMatch( symbol, &instance ) )
  223. continue;
  224. fields.clear();
  225. symbol->GetFields( fields, false );
  226. for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
  227. fieldNames.insert( fields[i]->GetName() );
  228. if( m_mode == MODE::UPDATE && symbol->GetLibId().IsValid() )
  229. {
  230. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( symbol->GetLibId() );
  231. if( libSymbol )
  232. {
  233. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  234. flattenedSymbol->GetFields( libFields );
  235. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  236. fieldNames.insert( libFields[i]->GetName() );
  237. libFields.clear(); // flattenedSymbol is about to go out of scope...
  238. }
  239. }
  240. }
  241. }
  242. // Load non-mandatory fields from the change-to library symbol
  243. if( m_mode == MODE::CHANGE )
  244. {
  245. LIB_ID newId;
  246. newId.Parse( m_newId->GetValue() );
  247. if( newId.IsValid() )
  248. {
  249. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( newId );
  250. if( libSymbol )
  251. {
  252. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  253. flattenedSymbol->GetFields( libFields );
  254. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  255. fieldNames.insert( libFields[i]->GetName() );
  256. libFields.clear(); // flattenedSymbol is about to go out of scope...
  257. }
  258. }
  259. }
  260. // Update the listbox widget
  261. for( unsigned i = m_fieldsBox->GetCount() - 1; i >= MANDATORY_FIELDS; --i )
  262. m_fieldsBox->Delete( i );
  263. for( const wxString& fieldName : fieldNames )
  264. m_fieldsBox->Append( fieldName );
  265. for( unsigned i = MANDATORY_FIELDS; i < m_fieldsBox->GetCount(); ++i )
  266. m_fieldsBox->Check( i, true );
  267. }
  268. void DIALOG_CHANGE_SYMBOLS::checkAll( bool aCheck )
  269. {
  270. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  271. m_fieldsBox->Check( i, aCheck );
  272. }
  273. void DIALOG_CHANGE_SYMBOLS::onOkButtonClicked( wxCommandEvent& aEvent )
  274. {
  275. wxBusyCursor dummy;
  276. SCH_EDIT_FRAME* parent = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  277. wxCHECK( parent, /* void */ );
  278. m_messagePanel->Clear();
  279. m_messagePanel->Flush( false );
  280. // Create the set of fields to be updated
  281. m_updateFields.clear();
  282. for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
  283. {
  284. if( m_fieldsBox->IsChecked( i ) )
  285. m_updateFields.insert( m_fieldsBox->GetString( i ) );
  286. }
  287. if( processMatchingSymbols() )
  288. {
  289. parent->TestDanglingEnds(); // This will also redraw the changed symbols.
  290. parent->OnModify();
  291. }
  292. m_messagePanel->Flush( false );
  293. }
  294. bool DIALOG_CHANGE_SYMBOLS::isMatch( SCH_SYMBOL* aSymbol, SCH_SHEET_PATH* aInstance )
  295. {
  296. LIB_ID id;
  297. wxCHECK( aSymbol, false );
  298. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  299. wxCHECK( frame, false );
  300. if( m_matchAll->GetValue() )
  301. {
  302. return true;
  303. }
  304. else if( m_matchBySelection->GetValue() )
  305. {
  306. return aSymbol == m_symbol || aSymbol->IsSelected();
  307. }
  308. else if( m_matchByReference->GetValue() )
  309. {
  310. return WildCompareString( m_specifiedReference->GetValue(),
  311. aSymbol->GetRef( aInstance, false ), false );
  312. }
  313. else if( m_matchByValue->GetValue() )
  314. {
  315. return WildCompareString( m_specifiedValue->GetValue(),
  316. aSymbol->GetValue( aInstance, false ), false );
  317. }
  318. else if( m_matchById )
  319. {
  320. id.Parse( m_specifiedId->GetValue() );
  321. return aSymbol->GetLibId() == id;
  322. }
  323. return false;
  324. }
  325. bool DIALOG_CHANGE_SYMBOLS::processMatchingSymbols()
  326. {
  327. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  328. wxCHECK( frame, false );
  329. LIB_ID newId;
  330. bool appendToUndo = false;
  331. bool changed = false;
  332. SCH_SHEET_LIST hierarchy = frame->Schematic().GetSheets();
  333. if( m_mode == MODE::CHANGE )
  334. {
  335. newId.Parse( m_newId->GetValue() );
  336. if( !newId.IsValid() )
  337. return false;
  338. }
  339. for( SCH_SHEET_PATH& instance : hierarchy )
  340. {
  341. SCH_SCREEN* screen = instance.LastScreen();
  342. wxCHECK2( screen, continue );
  343. std::vector<SCH_SYMBOL*> symbols;
  344. for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
  345. symbols.push_back( static_cast<SCH_SYMBOL*>( item ) );
  346. for( SCH_SYMBOL* symbol : symbols )
  347. {
  348. if( !isMatch( symbol, &instance ) )
  349. continue;
  350. if( m_mode == MODE::UPDATE )
  351. {
  352. if( processSymbol( symbol, &instance, symbol->GetLibId(), appendToUndo ) )
  353. changed = true;
  354. }
  355. else
  356. {
  357. if( processSymbol( symbol, &instance, newId, appendToUndo ) )
  358. changed = true;
  359. }
  360. if( changed )
  361. appendToUndo = true;
  362. }
  363. }
  364. frame->GetCurrentSheet().UpdateAllScreenReferences();
  365. return changed;
  366. }
  367. bool DIALOG_CHANGE_SYMBOLS::processSymbol( SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH* aInstance,
  368. const LIB_ID& aNewId, bool aAppendToUndo )
  369. {
  370. wxCHECK( aSymbol, false );
  371. wxCHECK( aNewId.IsValid(), false );
  372. SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
  373. SCH_SCREEN* screen = aInstance->LastScreen();
  374. wxCHECK( frame, false );
  375. LIB_ID oldId = aSymbol->GetLibId();
  376. wxString msg;
  377. wxString references;
  378. for( SYMBOL_INSTANCE_REFERENCE instance : aSymbol->GetInstanceReferences() )
  379. {
  380. if( references.IsEmpty() )
  381. references = instance.m_Reference;
  382. else
  383. references += " " + instance.m_Reference;
  384. }
  385. if( m_mode == MODE::UPDATE )
  386. {
  387. if( aSymbol->GetInstanceReferences().size() == 1 )
  388. {
  389. msg.Printf( _( "Update symbol %s from '%s' to '%s'" ),
  390. references,
  391. oldId.Format().c_str(),
  392. aNewId.Format().c_str() );
  393. }
  394. else
  395. {
  396. msg.Printf( _( "Update symbols %s from '%s' to '%s'" ),
  397. references,
  398. oldId.Format().c_str(),
  399. aNewId.Format().c_str() );
  400. }
  401. }
  402. else
  403. {
  404. if( aSymbol->GetInstanceReferences().size() == 1 )
  405. {
  406. msg.Printf( _( "Change symbol %s from '%s' to '%s'" ),
  407. references,
  408. oldId.Format().c_str(),
  409. aNewId.Format().c_str() );
  410. }
  411. else
  412. {
  413. msg.Printf( _( "Change symbols %s from '%s' to '%s'" ),
  414. references,
  415. oldId.Format().c_str(),
  416. aNewId.Format().c_str() );
  417. }
  418. }
  419. LIB_SYMBOL* libSymbol = frame->GetLibSymbol( aNewId );
  420. if( !libSymbol )
  421. {
  422. msg << ": " << _( "*** symbol not found ***" );
  423. m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
  424. return false;
  425. }
  426. std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
  427. if( flattenedSymbol->GetUnitCount() < aSymbol->GetUnit() )
  428. {
  429. msg << ": " << _( "*** new symbol has too few units ***" );
  430. m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
  431. return false;
  432. }
  433. // Removing the symbol needs to be done before the LIB_SYMBOL is changed to prevent stale
  434. // library symbols in the schematic file.
  435. screen->Remove( aSymbol );
  436. frame->SaveCopyInUndoList( screen, aSymbol, UNDO_REDO::CHANGED, aAppendToUndo );
  437. if( aNewId != aSymbol->GetLibId() )
  438. aSymbol->SetLibId( aNewId );
  439. aSymbol->SetLibSymbol( flattenedSymbol.release() );
  440. if( m_resetAttributes->GetValue() )
  441. {
  442. aSymbol->SetIncludeInBom( libSymbol->GetIncludeInBom() );
  443. aSymbol->SetIncludeOnBoard( libSymbol->GetIncludeOnBoard() );
  444. }
  445. bool removeExtras = m_removeExtraBox->GetValue();
  446. bool resetVis = m_resetFieldVisibilities->GetValue();
  447. bool resetEffects = m_resetFieldEffects->GetValue();
  448. bool resetPositions = m_resetFieldPositions->GetValue();
  449. for( unsigned i = 0; i < aSymbol->GetFields().size(); ++i )
  450. {
  451. SCH_FIELD& field = aSymbol->GetFields()[i];
  452. LIB_FIELD* libField = nullptr;
  453. if( !alg::contains( m_updateFields, field.GetName() ) )
  454. continue;
  455. if( i < MANDATORY_FIELDS )
  456. libField = libSymbol->GetFieldById( (int) i );
  457. else
  458. libField = libSymbol->FindField( field.GetName() );
  459. if( libField )
  460. {
  461. bool resetText = libField->GetText().IsEmpty() ? m_resetEmptyFields->GetValue()
  462. : m_resetFieldText->GetValue();
  463. if( resetText )
  464. {
  465. if( i == REFERENCE_FIELD )
  466. aSymbol->SetRef( aInstance, UTIL::GetRefDesUnannotated( libField->GetText() ) );
  467. else if( i == VALUE_FIELD )
  468. aSymbol->SetValue( aInstance, libField->GetText() );
  469. else if( i == FOOTPRINT_FIELD )
  470. aSymbol->SetFootprint( aInstance, libField->GetText() );
  471. else
  472. field.SetText( libField->GetText() );
  473. }
  474. if( resetVis )
  475. field.SetVisible( libField->IsVisible() );
  476. if( resetEffects )
  477. {
  478. // Careful: the visible bit and position are also in Effects
  479. bool visible = field.IsVisible();
  480. wxPoint pos = field.GetPosition();
  481. field.SetEffects( *libField );
  482. field.SetVisible( visible );
  483. field.SetPosition( pos );
  484. }
  485. if( resetPositions )
  486. field.SetTextPos( aSymbol->GetPosition() + libField->GetTextPos() );
  487. }
  488. else if( i >= MANDATORY_FIELDS && removeExtras )
  489. {
  490. aSymbol->RemoveField( field.GetName() );
  491. i--;
  492. }
  493. }
  494. std::vector<LIB_FIELD*> libFields;
  495. libSymbol->GetFields( libFields );
  496. for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
  497. {
  498. const LIB_FIELD& libField = *libFields[i];
  499. if( !alg::contains( m_updateFields, libField.GetName() ) )
  500. continue;
  501. if( !aSymbol->FindField( libField.GetName(), false ) )
  502. {
  503. wxString fieldName = libField.GetCanonicalName();
  504. SCH_FIELD newField( wxPoint( 0, 0), aSymbol->GetFieldCount(), aSymbol, fieldName );
  505. SCH_FIELD* schField = aSymbol->AddField( newField );
  506. schField->SetEffects( libField );
  507. schField->SetText( libField.GetText() );
  508. schField->SetTextPos( aSymbol->GetPosition() + libField.GetTextPos() );
  509. }
  510. }
  511. aSymbol->SetSchSymbolLibraryName( wxEmptyString );
  512. screen->Append( aSymbol );
  513. frame->GetCanvas()->GetView()->Update( aSymbol );
  514. msg += ": OK";
  515. m_messagePanel->Report( msg, RPT_SEVERITY_ACTION );
  516. return true;
  517. }