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.

740 lines
23 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <dialogs/dialog_edit_component_in_schematic.h>
  24. #include <wx/tooltip.h>
  25. #include <confirm.h>
  26. #include <grid_tricks.h>
  27. #include <kiface_i.h>
  28. #include <menus_helpers.h>
  29. #include <pgm_base.h>
  30. #include <widgets/wx_grid.h>
  31. #include <class_library.h>
  32. #include <eeschema_settings.h>
  33. #include <fields_grid_table.h>
  34. #include <invoke_sch_dialog.h>
  35. #include <sch_draw_panel.h>
  36. #include <sch_edit_frame.h>
  37. #include <sch_reference_list.h>
  38. #include <sch_validators.h>
  39. #include <symbol_lib_table.h>
  40. #ifdef KICAD_SPICE
  41. #include <dialog_spice_model.h>
  42. #include <netlist_exporter_pspice.h>
  43. #endif /* KICAD_SPICE */
  44. DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::DIALOG_EDIT_COMPONENT_IN_SCHEMATIC( SCH_EDIT_FRAME* aParent,
  45. SCH_COMPONENT* aComponent ) :
  46. DIALOG_EDIT_COMPONENT_IN_SCHEMATIC_BASE( aParent )
  47. {
  48. m_cmp = aComponent;
  49. m_part = m_cmp->GetPartRef().get();
  50. m_fields = new FIELDS_GRID_TABLE<SCH_FIELD>( this, aParent, m_part );
  51. m_width = 0;
  52. m_delayedFocusRow = REFERENCE;
  53. m_delayedFocusColumn = FDC_VALUE;
  54. #ifndef KICAD_SPICE
  55. m_spiceFieldsButton->Hide();
  56. #endif /* not KICAD_SPICE */
  57. // disable some options inside the edit dialog which can cause problems while dragging
  58. if( m_cmp->IsDragging() )
  59. {
  60. m_rbOrientation->Disable();
  61. m_rbMirror->Disable();
  62. m_libraryNameTextCtrl->Disable();
  63. }
  64. // Give a bit more room for combobox editors
  65. m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
  66. m_grid->SetTable( m_fields );
  67. m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this ) );
  68. // Show/hide columns according to user's preference
  69. auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
  70. m_shownColumns = cfg->m_Appearance.edit_component_visible_columns;
  71. m_grid->ShowHideColumns( m_shownColumns );
  72. wxToolTip::Enable( true );
  73. m_stdDialogButtonSizerOK->SetDefault();
  74. // Configure button logos
  75. m_buttonBrowseLibrary->SetBitmap( KiBitmap( small_library_xpm ) );
  76. m_bpAdd->SetBitmap( KiBitmap( small_plus_xpm ) );
  77. m_bpDelete->SetBitmap( KiBitmap( trash_xpm ) );
  78. m_bpMoveUp->SetBitmap( KiBitmap( small_up_xpm ) );
  79. m_bpMoveDown->SetBitmap( KiBitmap( small_down_xpm ) );
  80. // wxFormBuilder doesn't include this event...
  81. m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
  82. wxGridEventHandler( DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnGridCellChanging ),
  83. NULL, this );
  84. FinishDialogSettings();
  85. }
  86. DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::~DIALOG_EDIT_COMPONENT_IN_SCHEMATIC()
  87. {
  88. auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
  89. cfg->m_Appearance.edit_component_visible_columns = m_grid->GetShownColumns();
  90. // Prevents crash bug in wxGrid's d'tor
  91. m_grid->DestroyTable( m_fields );
  92. m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
  93. wxGridEventHandler( DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnGridCellChanging ),
  94. NULL, this );
  95. // Delete the GRID_TRICKS.
  96. m_grid->PopEventHandler( true );
  97. }
  98. SCH_EDIT_FRAME* DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::GetParent()
  99. {
  100. return dynamic_cast<SCH_EDIT_FRAME*>( wxDialog::GetParent() );
  101. }
  102. bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::TransferDataToWindow()
  103. {
  104. if( !wxDialog::TransferDataToWindow() )
  105. return false;
  106. std::set<wxString> defined;
  107. // Push a copy of each field into m_fields
  108. for( int i = 0; i < m_cmp->GetFieldCount(); ++i )
  109. {
  110. SCH_FIELD field( *m_cmp->GetField( i ) );
  111. // change offset to be symbol-relative
  112. field.Offset( -m_cmp->GetPosition() );
  113. defined.insert( field.GetName() );
  114. m_fields->push_back( field );
  115. }
  116. // Add in any template fieldnames not yet defined:
  117. for( const TEMPLATE_FIELDNAME& templateFieldname : GetParent()->GetTemplateFieldNames() )
  118. {
  119. if( defined.count( templateFieldname.m_Name ) <= 0 )
  120. {
  121. SCH_FIELD field( wxPoint( 0, 0 ), -1, m_cmp, templateFieldname.m_Name );
  122. field.SetVisible( templateFieldname.m_Visible );
  123. m_fields->push_back( field );
  124. }
  125. }
  126. // notify the grid
  127. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->size() );
  128. m_grid->ProcessTableMessage( msg );
  129. AdjustGridColumns( m_grid->GetRect().GetWidth() );
  130. // If a multi-unit component, set up the unit selector and interchangeable checkbox.
  131. if( m_cmp->GetUnitCount() > 1 )
  132. {
  133. for( int ii = 1; ii <= m_cmp->GetUnitCount(); ii++ )
  134. m_unitChoice->Append( LIB_PART::SubReference( ii, false ) );
  135. if( m_cmp->GetUnit() <= ( int )m_unitChoice->GetCount() )
  136. m_unitChoice->SetSelection( m_cmp->GetUnit() - 1 );
  137. }
  138. else
  139. {
  140. m_unitLabel->Enable( false );
  141. m_unitChoice->Enable( false );
  142. }
  143. if( m_part != nullptr && m_part->HasConversion() )
  144. {
  145. if( m_cmp->GetConvert() > LIB_ITEM::LIB_CONVERT::BASE )
  146. m_cbAlternateSymbol->SetValue( true );
  147. }
  148. else
  149. m_cbAlternateSymbol->Enable( false );
  150. // Set the symbol orientation and mirroring.
  151. int orientation = m_cmp->GetOrientation() & ~( CMP_MIRROR_X | CMP_MIRROR_Y );
  152. if( orientation == CMP_ORIENT_90 )
  153. m_rbOrientation->SetSelection( 1 );
  154. else if( orientation == CMP_ORIENT_180 )
  155. m_rbOrientation->SetSelection( 2 );
  156. else if( orientation == CMP_ORIENT_270 )
  157. m_rbOrientation->SetSelection( 3 );
  158. else
  159. m_rbOrientation->SetSelection( 0 );
  160. int mirror = m_cmp->GetOrientation() & ( CMP_MIRROR_X | CMP_MIRROR_Y );
  161. if( mirror == CMP_MIRROR_X )
  162. m_rbMirror->SetSelection( 1 );
  163. else if( mirror == CMP_MIRROR_Y )
  164. m_rbMirror->SetSelection( 2 );
  165. else
  166. m_rbMirror->SetSelection( 0 );
  167. // Set the component's unique ID time stamp.
  168. m_textCtrlTimeStamp->SetValue( wxString::Format( wxT( "%8.8lX" ),
  169. (unsigned long) m_cmp->GetTimeStamp() ) );
  170. // Set the component's library name.
  171. m_libraryNameTextCtrl->SetValue( m_cmp->GetLibId().Format() );
  172. Layout();
  173. return true;
  174. }
  175. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnBrowseLibrary( wxCommandEvent& event )
  176. {
  177. SCH_BASE_FRAME::HISTORY_LIST dummy;
  178. LIB_ID id;
  179. id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH );
  180. auto sel = GetParent()->SelectCompFromLibTree( nullptr, dummy, true, 0, 0, false, &id );
  181. if( !sel.LibId.IsValid() )
  182. return;
  183. m_libraryNameTextCtrl->SetValue( sel.LibId.Format() );
  184. LIB_PART* entry = GetParent()->GetLibPart( sel.LibId );
  185. if( entry )
  186. {
  187. // Update the value field for Power symbols
  188. if( entry->IsPower() )
  189. m_grid->SetCellValue( VALUE, FDC_VALUE, sel.LibId.GetLibItemName() );
  190. // Update the units control
  191. int unit = m_unitChoice->GetSelection();
  192. m_unitChoice->Clear();
  193. if( entry->GetUnitCount() > 1 )
  194. {
  195. for( int ii = 1; ii <= entry->GetUnitCount(); ii++ )
  196. m_unitChoice->Append( LIB_PART::SubReference( ii, false ) );
  197. if( unit < 0 || static_cast<unsigned>( unit ) >= m_unitChoice->GetCount() )
  198. unit = 0;
  199. m_unitChoice->SetSelection( unit );
  200. m_unitLabel->Enable( true );
  201. m_unitChoice->Enable( true );
  202. }
  203. else
  204. {
  205. m_unitChoice->SetSelection( -1 );
  206. m_unitLabel->Enable( false );
  207. m_unitChoice->Enable( false );
  208. }
  209. // Update the deMorgan conversion controls
  210. bool conversion = m_cbAlternateSymbol->GetValue();
  211. m_cbAlternateSymbol->SetValue( conversion && entry->HasConversion() );
  212. m_cbAlternateSymbol->Enable( entry->HasConversion() );
  213. }
  214. }
  215. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnEditSpiceModel( wxCommandEvent& event )
  216. {
  217. #ifdef KICAD_SPICE
  218. int diff = m_fields->size();
  219. DIALOG_SPICE_MODEL dialog( this, *m_cmp, m_fields );
  220. if( dialog.ShowModal() != wxID_OK )
  221. return;
  222. diff = m_fields->size() - diff;
  223. if( diff > 0 )
  224. {
  225. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, diff );
  226. m_grid->ProcessTableMessage( msg );
  227. }
  228. else if( diff < 0 )
  229. {
  230. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, -diff );
  231. m_grid->ProcessTableMessage( msg );
  232. }
  233. m_grid->ForceRefresh();
  234. #endif /* KICAD_SPICE */
  235. }
  236. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnCancelButtonClick( wxCommandEvent& event )
  237. {
  238. // Running the Footprint Browser gums up the works and causes the automatic cancel
  239. // stuff to no longer work. So we do it here ourselves.
  240. EndQuasiModal( wxID_CANCEL );
  241. }
  242. bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::Validate()
  243. {
  244. wxString msg;
  245. LIB_ID id;
  246. if( !m_grid->CommitPendingChanges() || !m_grid->Validate() )
  247. return false;
  248. if( !SCH_COMPONENT::IsReferenceStringValid( m_fields->at( REFERENCE ).GetText() ) )
  249. {
  250. DisplayErrorMessage( this, _( "References must start with a letter." ) );
  251. m_delayedFocusColumn = FDC_VALUE;
  252. m_delayedFocusRow = REFERENCE;
  253. return false;
  254. }
  255. id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH );
  256. if( !id.IsValid() )
  257. {
  258. DisplayErrorMessage( this, _( "Library reference is not valid." ) );
  259. m_libraryNameTextCtrl->SetFocus();
  260. return false;
  261. }
  262. else if( id != m_cmp->GetLibId() )
  263. {
  264. LIB_PART* alias = nullptr;
  265. try
  266. {
  267. alias = Prj().SchSymbolLibTable()->LoadSymbol( id );
  268. }
  269. catch( ... )
  270. {
  271. }
  272. if( !alias )
  273. {
  274. msg.Printf( _( "Symbol \"%s\" not found in library \"%s\"." ),
  275. id.GetLibItemName().wx_str(),
  276. id.GetLibNickname().wx_str() );
  277. DisplayErrorMessage( this, msg );
  278. m_libraryNameTextCtrl->SetFocus();
  279. return false;
  280. }
  281. }
  282. m_libraryNameTextCtrl->SetValue( id.Format() );
  283. // Check for missing field names.
  284. for( size_t i = MANDATORY_FIELDS; i < m_fields->size(); ++i )
  285. {
  286. SCH_FIELD& field = m_fields->at( i );
  287. wxString fieldName = field.GetName( false );
  288. if( fieldName.IsEmpty() )
  289. {
  290. DisplayErrorMessage( this, _( "Fields must have a name." ) );
  291. m_delayedFocusColumn = FDC_NAME;
  292. m_delayedFocusRow = i;
  293. return false;
  294. }
  295. }
  296. return true;
  297. }
  298. bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::TransferDataFromWindow()
  299. {
  300. if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
  301. return false;
  302. // save old cmp in undo list if not already in edit, or moving ...
  303. if( m_cmp->GetEditFlags() == 0 )
  304. GetParent()->SaveCopyInUndoList( m_cmp, UR_CHANGED );
  305. // Save current flags which could be modified by next change settings
  306. STATUS_FLAGS flags = m_cmp->GetFlags();
  307. // Library symbol identifier
  308. LIB_ID id;
  309. id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true );
  310. m_cmp->SetLibId( id, Prj().SchSymbolLibTable(), Prj().SchLibs()->GetCacheLibrary() );
  311. // For symbols with multiple shapes (De Morgan representation) Set the selected shape:
  312. if( m_cbAlternateSymbol->IsEnabled() && m_cbAlternateSymbol->GetValue() )
  313. m_cmp->SetConvert( LIB_ITEM::LIB_CONVERT::DEMORGAN );
  314. else
  315. m_cmp->SetConvert( LIB_ITEM::LIB_CONVERT::BASE );
  316. //Set the part selection in multiple part per package
  317. int unit_selection = m_unitChoice->IsEnabled()
  318. ? m_unitChoice->GetSelection() + 1
  319. : 1;
  320. m_cmp->SetUnitSelection( &GetParent()->GetCurrentSheet(), unit_selection );
  321. m_cmp->SetUnit( unit_selection );
  322. switch( m_rbOrientation->GetSelection() )
  323. {
  324. case 0: m_cmp->SetOrientation( CMP_ORIENT_0 ); break;
  325. case 1: m_cmp->SetOrientation( CMP_ORIENT_90 ); break;
  326. case 2: m_cmp->SetOrientation( CMP_ORIENT_180 ); break;
  327. case 3: m_cmp->SetOrientation( CMP_ORIENT_270 ); break;
  328. }
  329. switch( m_rbMirror->GetSelection() )
  330. {
  331. case 0: break;
  332. case 1: m_cmp->SetOrientation( CMP_MIRROR_X ); break;
  333. case 2: m_cmp->SetOrientation( CMP_MIRROR_Y ); break;
  334. }
  335. // Restore m_Flag modified by SetUnit() and other change settings
  336. m_cmp->ClearFlags();
  337. m_cmp->SetFlags( flags );
  338. // change all field positions from relative to absolute
  339. for( unsigned i = 0; i < m_fields->size(); ++i )
  340. m_fields->at( i ).Offset( m_cmp->GetPosition() );
  341. LIB_PART* entry = GetParent()->GetLibPart( m_cmp->GetLibId() );
  342. if( entry && entry->IsPower() )
  343. m_fields->at( VALUE ).SetText( m_cmp->GetLibId().GetLibItemName() );
  344. // Remove any TEMPLATE_FIELDNAMES which were not set (given values).
  345. TEMPLATE_FIELDNAMES templateFieldnames = GetParent()->GetTemplateFieldNames();
  346. for( size_t i = MANDATORY_FIELDS; i < m_fields->size(); ++i )
  347. {
  348. SCH_FIELD& field = m_fields->at( i );
  349. for( const auto& fieldname : templateFieldnames )
  350. {
  351. if( field.GetName() == fieldname.m_Name && field.GetText().IsEmpty() )
  352. {
  353. m_fields->erase( m_fields->begin() + i );
  354. // grid needs to be notified about the size change,
  355. // as it still accesses the data on close (size event)
  356. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, i, 1 );
  357. m_grid->ProcessTableMessage( msg );
  358. i--;
  359. break;
  360. }
  361. }
  362. }
  363. m_cmp->SetFields( *m_fields );
  364. // Reference has a specific initialization, depending on the current active sheet
  365. // because for a given component, in a complex hierarchy, there are more than one
  366. // reference.
  367. m_cmp->SetRef( &GetParent()->GetCurrentSheet(), m_fields->at( REFERENCE ).GetText() );
  368. // The value, footprint and datasheet fields should be kept in sync in multi-unit
  369. // parts.
  370. if( m_cmp->GetUnitCount() > 1 )
  371. {
  372. const LIB_ID thisLibId = m_cmp->GetLibId();
  373. const wxString thisRef = m_cmp->GetRef( &( GetParent()->GetCurrentSheet() ) );
  374. int thisUnit = m_cmp->GetUnit();
  375. SCH_REFERENCE_LIST components;
  376. GetParent()->GetCurrentSheet().GetComponents( components );
  377. for( unsigned i = 0; i < components.GetCount(); i++ )
  378. {
  379. SCH_REFERENCE component = components[i];
  380. if( component.GetLibPart()->GetLibId() == thisLibId
  381. && component.GetRef() == thisRef
  382. && component.GetUnit() != thisUnit )
  383. {
  384. SCH_COMPONENT* otherUnit = component.GetComp();
  385. GetParent()->SaveCopyInUndoList( otherUnit, UR_CHANGED, true /* append */);
  386. otherUnit->GetField( VALUE )->SetText( m_fields->at( VALUE ).GetText() );
  387. otherUnit->GetField( FOOTPRINT )->SetText( m_fields->at( FOOTPRINT ).GetText() );
  388. otherUnit->GetField( DATASHEET )->SetText( m_fields->at( DATASHEET ).GetText() );
  389. }
  390. }
  391. }
  392. m_cmp->UpdatePins();
  393. GetParent()->TestDanglingEnds();
  394. GetParent()->RefreshItem( m_cmp );
  395. GetParent()->OnModify();
  396. return true;
  397. }
  398. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnGridCellChanging( wxGridEvent& event )
  399. {
  400. wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
  401. wxControl* control = editor->GetControl();
  402. if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
  403. {
  404. event.Veto();
  405. m_delayedFocusRow = event.GetRow();
  406. m_delayedFocusColumn = event.GetCol();
  407. }
  408. editor->DecRef();
  409. }
  410. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnAddField( wxCommandEvent& event )
  411. {
  412. if( !m_grid->CommitPendingChanges() )
  413. return;
  414. int fieldID = m_fields->size();
  415. SCH_FIELD newField( wxPoint( 0, 0 ), fieldID, m_cmp );
  416. newField.SetName( TEMPLATE_FIELDNAME::GetDefaultFieldName( fieldID ) );
  417. newField.SetTextAngle( m_fields->at( REFERENCE ).GetTextAngle() );
  418. m_fields->push_back( newField );
  419. // notify the grid
  420. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
  421. m_grid->ProcessTableMessage( msg );
  422. m_grid->MakeCellVisible( m_fields->size() - 1, 0 );
  423. m_grid->SetGridCursor( m_fields->size() - 1, 0 );
  424. m_grid->EnableCellEditControl();
  425. m_grid->ShowCellEditControl();
  426. }
  427. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnDeleteField( wxCommandEvent& event )
  428. {
  429. int curRow = m_grid->GetGridCursorRow();
  430. if( curRow < 0 )
  431. return;
  432. else if( curRow < MANDATORY_FIELDS )
  433. {
  434. DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
  435. MANDATORY_FIELDS ) );
  436. return;
  437. }
  438. m_grid->CommitPendingChanges( true /* quiet mode */ );
  439. m_fields->erase( m_fields->begin() + curRow );
  440. // notify the grid
  441. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, curRow, 1 );
  442. m_grid->ProcessTableMessage( msg );
  443. if( m_grid->GetNumberRows() > 0 )
  444. {
  445. m_grid->MakeCellVisible( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
  446. m_grid->SetGridCursor( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
  447. }
  448. }
  449. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnMoveUp( wxCommandEvent& event )
  450. {
  451. if( !m_grid->CommitPendingChanges() )
  452. return;
  453. int i = m_grid->GetGridCursorRow();
  454. if( i > MANDATORY_FIELDS )
  455. {
  456. SCH_FIELD tmp = m_fields->at( (unsigned) i );
  457. m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
  458. m_fields->insert( m_fields->begin() + i - 1, tmp );
  459. m_grid->ForceRefresh();
  460. m_grid->SetGridCursor( i - 1, m_grid->GetGridCursorCol() );
  461. m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
  462. }
  463. else
  464. wxBell();
  465. }
  466. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnMoveDown( wxCommandEvent& event )
  467. {
  468. if( !m_grid->CommitPendingChanges() )
  469. return;
  470. int i = m_grid->GetGridCursorRow();
  471. if( i >= MANDATORY_FIELDS && i < m_grid->GetNumberRows() - 1 )
  472. {
  473. SCH_FIELD tmp = m_fields->at( (unsigned) i );
  474. m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
  475. m_fields->insert( m_fields->begin() + i + 1, tmp );
  476. m_grid->ForceRefresh();
  477. m_grid->SetGridCursor( i + 1, m_grid->GetGridCursorCol() );
  478. m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
  479. }
  480. else
  481. wxBell();
  482. }
  483. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::UpdateFieldsFromLibrary( wxCommandEvent& event )
  484. {
  485. if( !m_grid->CommitPendingChanges() )
  486. return;
  487. SCH_COMPONENT copy( *m_cmp );
  488. copy.SetFields( *m_fields );
  489. LIB_ID id;
  490. id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true );
  491. copy.SetLibId( id, Prj().SchSymbolLibTable(), Prj().SchLibs()->GetCacheLibrary() );
  492. // Update the requested fields in the component copy
  493. std::list<SCH_COMPONENT*> components;
  494. components.push_back( &copy );
  495. InvokeDialogUpdateFields( GetParent(), components, false );
  496. wxGridTableMessage clear( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_fields->size() );
  497. m_grid->ProcessTableMessage( clear );
  498. // Copy fields from the component copy to the dialog buffer
  499. m_fields->clear();
  500. std::set<wxString> defined;
  501. for( int i = 0; i < copy.GetFieldCount(); ++i )
  502. {
  503. copy.GetField( i )->SetParent( m_cmp );
  504. defined.insert( copy.GetField( i )->GetName() );
  505. m_fields->push_back( *copy.GetField( i ) );
  506. }
  507. // Add in any template fieldnames not yet defined:
  508. for( const TEMPLATE_FIELDNAME& templateFieldname : GetParent()->GetTemplateFieldNames() )
  509. {
  510. if( defined.count( templateFieldname.m_Name ) <= 0 )
  511. {
  512. SCH_FIELD field( wxPoint( 0, 0 ), -1, m_cmp, templateFieldname.m_Name );
  513. field.SetVisible( templateFieldname.m_Visible );
  514. m_fields->push_back( field );
  515. }
  516. }
  517. wxGridTableMessage refresh( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->size() );
  518. m_grid->ProcessTableMessage( refresh );
  519. }
  520. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::AdjustGridColumns( int aWidth )
  521. {
  522. m_width = aWidth;
  523. // Account for scroll bars
  524. aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
  525. m_grid->AutoSizeColumn( 0 );
  526. int fixedColsWidth = m_grid->GetColSize( 0 );
  527. for( int i = 2; i < m_grid->GetNumberCols(); i++ )
  528. fixedColsWidth += m_grid->GetColSize( i );
  529. m_grid->SetColSize( 1, aWidth - fixedColsWidth );
  530. }
  531. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnUpdateUI( wxUpdateUIEvent& event )
  532. {
  533. wxString shownColumns = m_grid->GetShownColumns();
  534. if( shownColumns != m_shownColumns )
  535. {
  536. m_shownColumns = shownColumns;
  537. if( !m_grid->IsCellEditControlShown() )
  538. AdjustGridColumns( m_grid->GetRect().GetWidth() );
  539. }
  540. // Handle a delayed focus
  541. if( m_delayedFocusRow >= 0 )
  542. {
  543. m_grid->SetFocus();
  544. m_grid->MakeCellVisible( m_delayedFocusRow, m_delayedFocusColumn );
  545. m_grid->SetGridCursor( m_delayedFocusRow, m_delayedFocusColumn );
  546. m_grid->EnableCellEditControl( true );
  547. m_grid->ShowCellEditControl();
  548. m_delayedFocusRow = -1;
  549. m_delayedFocusColumn = -1;
  550. }
  551. }
  552. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnSizeGrid( wxSizeEvent& event )
  553. {
  554. auto new_size = event.GetSize().GetX();
  555. if( m_width != new_size )
  556. {
  557. AdjustGridColumns( new_size );
  558. }
  559. // Always propagate for a grid repaint (needed if the height changes, as well as width)
  560. event.Skip();
  561. }
  562. void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnInitDlg( wxInitDialogEvent& event )
  563. {
  564. TransferDataToWindow();
  565. // Now all widgets have the size fixed, call FinishDialogSettings
  566. FinishDialogSettings();
  567. }