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.

795 lines
25 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2020 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 <bitmaps.h>
  24. #include <class_libentry.h>
  25. #include <confirm.h>
  26. #include <dialogs/dialog_text_entry.h>
  27. #include <kiway.h>
  28. #include <symbol_edit_frame.h>
  29. #include <symbol_library_manager.h>
  30. #include <math/util.h> // for KiROUND
  31. #include <pgm_base.h>
  32. #include <sch_component.h>
  33. #include <widgets/grid_text_button_helpers.h>
  34. #include <widgets/wx_grid.h>
  35. #ifdef KICAD_SPICE
  36. #include <dialog_spice_model.h>
  37. #endif /* KICAD_SPICE */
  38. #include <dialog_lib_symbol_properties.h>
  39. #include <settings/settings_manager.h>
  40. #include <symbol_editor_settings.h>
  41. int DIALOG_LIB_SYMBOL_PROPERTIES::m_lastOpenedPage = 0;
  42. DIALOG_LIB_SYMBOL_PROPERTIES::LAST_LAYOUT
  43. DIALOG_LIB_SYMBOL_PROPERTIES::m_lastLayout = DIALOG_LIB_SYMBOL_PROPERTIES::NONE;
  44. DIALOG_LIB_SYMBOL_PROPERTIES::DIALOG_LIB_SYMBOL_PROPERTIES( SYMBOL_EDIT_FRAME* aParent,
  45. LIB_PART* aLibEntry ) :
  46. DIALOG_LIB_SYMBOL_PROPERTIES_BASE( aParent ),
  47. m_Parent( aParent ),
  48. m_libEntry( aLibEntry ),
  49. m_pinNameOffset( aParent, m_nameOffsetLabel, m_nameOffsetCtrl, m_nameOffsetUnits, true ),
  50. m_delayedFocusCtrl( nullptr ),
  51. m_delayedFocusGrid( nullptr ),
  52. m_delayedFocusRow( -1 ),
  53. m_delayedFocusColumn( -1 ),
  54. m_delayedFocusPage( -1 ),
  55. m_width( 0 )
  56. {
  57. // Give a bit more room for combobox editors
  58. m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
  59. m_fields = new FIELDS_GRID_TABLE<LIB_FIELD>( this, aParent, m_libEntry );
  60. m_grid->SetTable( m_fields );
  61. m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this ) );
  62. // Show/hide columns according to the user's preference
  63. auto cfg = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
  64. m_grid->ShowHideColumns( cfg->m_EditComponentVisibleColumns );
  65. wxGridCellAttr* attr = new wxGridCellAttr;
  66. attr->SetEditor( new GRID_CELL_URL_EDITOR( this ) );
  67. m_grid->SetAttr( DATASHEET_FIELD, FDC_VALUE, attr );
  68. m_SymbolNameCtrl->SetValidator( SCH_FIELD_VALIDATOR( true, VALUE_FIELD ) );
  69. // Configure button logos
  70. m_bpAdd->SetBitmap( KiBitmap( small_plus_xpm ) );
  71. m_bpDelete->SetBitmap( KiBitmap( small_trash_xpm ) );
  72. m_bpMoveUp->SetBitmap( KiBitmap( small_up_xpm ) );
  73. m_bpMoveDown->SetBitmap( KiBitmap( small_down_xpm ) );
  74. m_addFilterButton->SetBitmap( KiBitmap( small_plus_xpm ) );
  75. m_deleteFilterButton->SetBitmap( KiBitmap( small_trash_xpm ) );
  76. m_editFilterButton->SetBitmap( KiBitmap( small_edit_xpm ) );
  77. if( aParent->IsSymbolFromLegacyLibrary() )
  78. {
  79. m_stdSizerButtonCancel->SetDefault();
  80. m_stdSizerButtonOK->SetLabel( _( "Read Only" ) );
  81. m_stdSizerButtonOK->Enable( false );
  82. }
  83. else
  84. {
  85. m_stdSizerButtonOK->SetDefault();
  86. }
  87. #ifndef KICAD_SPICE
  88. m_spiceFieldsButton->Hide();
  89. #endif
  90. // wxFormBuilder doesn't include this event...
  91. m_grid->Connect( wxEVT_GRID_CELL_CHANGING,
  92. wxGridEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging ),
  93. NULL, this );
  94. if( m_lastLayout != DIALOG_LIB_SYMBOL_PROPERTIES::NONE )
  95. {
  96. if( ( m_lastLayout == DIALOG_LIB_SYMBOL_PROPERTIES::ALIAS && aLibEntry->IsRoot() )
  97. || ( m_lastLayout == DIALOG_LIB_SYMBOL_PROPERTIES::PARENT && aLibEntry->IsAlias() ) )
  98. {
  99. resetSize();
  100. }
  101. }
  102. m_lastLayout = ( aLibEntry->IsAlias() ) ? DIALOG_LIB_SYMBOL_PROPERTIES::ALIAS
  103. : DIALOG_LIB_SYMBOL_PROPERTIES::PARENT;
  104. m_grid->GetParent()->Layout();
  105. syncControlStates( m_libEntry->IsAlias() );
  106. Layout();
  107. finishDialogSettings();
  108. }
  109. DIALOG_LIB_SYMBOL_PROPERTIES::~DIALOG_LIB_SYMBOL_PROPERTIES()
  110. {
  111. m_lastOpenedPage = m_NoteBook->GetSelection( );
  112. auto cfg = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
  113. cfg->m_EditComponentVisibleColumns = m_grid->GetShownColumns();
  114. // Prevents crash bug in wxGrid's d'tor
  115. m_grid->DestroyTable( m_fields );
  116. m_grid->Disconnect( wxEVT_GRID_CELL_CHANGING,
  117. wxGridEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging ),
  118. NULL, this );
  119. // Delete the GRID_TRICKS.
  120. m_grid->PopEventHandler( true );
  121. }
  122. bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataToWindow()
  123. {
  124. if( !wxDialog::TransferDataToWindow() )
  125. return false;
  126. // Push a copy of each field into m_updateFields
  127. m_libEntry->GetFields( *m_fields );
  128. // The Y axis for components in lib is from bottom to top while the screen axis is top
  129. // to bottom: we must change the y coord sign for editing
  130. for( size_t i = 0; i < m_fields->size(); ++i )
  131. {
  132. wxPoint pos = m_fields->at( i ).GetPosition();
  133. pos.y = -pos.y;
  134. m_fields->at( i ).SetPosition( pos );
  135. }
  136. // notify the grid
  137. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
  138. m_grid->ProcessTableMessage( msg );
  139. adjustGridColumns( m_grid->GetRect().GetWidth() );
  140. m_SymbolNameCtrl->SetValue( m_libEntry->GetName() );
  141. m_DescCtrl->SetValue( m_libEntry->GetDescription() );
  142. m_KeywordCtrl->SetValue( m_libEntry->GetKeyWords() );
  143. m_SelNumberOfUnits->SetValue( m_libEntry->GetUnitCount() );
  144. m_OptionPartsLocked->SetValue( m_libEntry->UnitsLocked() && m_libEntry->GetUnitCount() > 1 );
  145. m_AsConvertButt->SetValue( m_libEntry->HasConversion() );
  146. m_OptionPower->SetValue( m_libEntry->IsPower() );
  147. m_excludeFromBomCheckBox->SetValue( !m_libEntry->GetIncludeInBom() );
  148. m_excludeFromBoardCheckBox->SetValue( !m_libEntry->GetIncludeOnBoard() );
  149. m_ShowPinNumButt->SetValue( m_libEntry->ShowPinNumbers() );
  150. m_ShowPinNameButt->SetValue( m_libEntry->ShowPinNames() );
  151. m_PinsNameInsideButt->SetValue( m_libEntry->GetPinNameOffset() != 0 );
  152. m_pinNameOffset.SetValue( m_libEntry->GetPinNameOffset() );
  153. wxArrayString tmp = m_libEntry->GetFPFilters();
  154. m_FootprintFilterListBox->Append( tmp );
  155. // Populate the list of root parts for inherited objects.
  156. if( m_libEntry->IsAlias() )
  157. {
  158. wxArrayString rootSymbolNames;
  159. wxString libName = m_Parent->GetCurLib();
  160. // Someone forgot to set the current library in the editor frame window.
  161. wxCHECK( !libName.empty(), false );
  162. m_Parent->GetLibManager().GetRootSymbolNames( libName, rootSymbolNames );
  163. m_inheritanceSelectCombo->Append( rootSymbolNames );
  164. PART_SPTR rootPart = m_libEntry->GetParent().lock();
  165. wxCHECK( rootPart, false );
  166. wxString parentName = rootPart->GetName();
  167. int selection = m_inheritanceSelectCombo->FindString( parentName );
  168. wxCHECK( selection != wxNOT_FOUND, false );
  169. m_inheritanceSelectCombo->SetSelection( selection );
  170. m_lastOpenedPage = 0;
  171. }
  172. m_NoteBook->SetSelection( (unsigned) m_lastOpenedPage );
  173. return true;
  174. }
  175. bool DIALOG_LIB_SYMBOL_PROPERTIES::Validate()
  176. {
  177. if( !m_grid->CommitPendingChanges() )
  178. return false;
  179. // Alias symbol reference can be empty because it inherits from the parent symbol.
  180. if( m_libEntry->IsRoot() &&
  181. !SCH_COMPONENT::IsReferenceStringValid( m_fields->at( REFERENCE_FIELD ).GetText() ) )
  182. {
  183. if( m_NoteBook->GetSelection() != 0 )
  184. m_NoteBook->SetSelection( 0 );
  185. m_delayedErrorMessage = _( "References must start with a letter." );
  186. m_delayedFocusGrid = m_grid;
  187. m_delayedFocusColumn = FDC_VALUE;
  188. m_delayedFocusRow = REFERENCE_FIELD;
  189. m_delayedFocusPage = 0;
  190. return false;
  191. }
  192. // Check for missing field names.
  193. for( size_t i = MANDATORY_FIELDS; i < m_fields->size(); ++i )
  194. {
  195. LIB_FIELD& field = m_fields->at( i );
  196. wxString fieldName = field.GetName( false );
  197. if( fieldName.IsEmpty() )
  198. {
  199. if( m_NoteBook->GetSelection() != 0 )
  200. m_NoteBook->SetSelection( 0 );
  201. m_delayedErrorMessage = _( "Fields must have a name." );
  202. m_delayedFocusGrid = m_grid;
  203. m_delayedFocusColumn = FDC_NAME;
  204. m_delayedFocusRow = i;
  205. m_delayedFocusPage = 0;
  206. return false;
  207. }
  208. }
  209. // Verify that the parent name is set if the symbol is inherited
  210. if( m_libEntry->IsAlias() )
  211. {
  212. wxString parentName = m_inheritanceSelectCombo->GetValue();
  213. if( parentName.IsEmpty() )
  214. {
  215. m_delayedErrorMessage = _( "Aliased symbol must have a parent selected" );
  216. return false;
  217. }
  218. }
  219. if( m_SelNumberOfUnits->GetValue() < m_libEntry->GetUnitCount() )
  220. {
  221. if( !IsOK( this, _( "Delete extra units from symbol?" ) ) )
  222. return false;
  223. }
  224. if( m_AsConvertButt->GetValue() && !m_libEntry->HasConversion() )
  225. {
  226. if( !IsOK( this, _( "Add new pins for alternate body style (DeMorgan) to symbol?" ) ) )
  227. return false;
  228. }
  229. else if( !m_AsConvertButt->GetValue() && m_libEntry->HasConversion() )
  230. {
  231. if( !IsOK( this, _( "Delete alternate body style (DeMorgan) draw items from symbol?" ) ) )
  232. return false;
  233. }
  234. return true;
  235. }
  236. bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataFromWindow()
  237. {
  238. if( !wxDialog::TransferDataFromWindow() )
  239. return false;
  240. // We need to keep the name and the value the same at the moment!
  241. wxString newName = m_fields->at( VALUE_FIELD ).GetText();
  242. wxString oldName = m_libEntry->GetName();
  243. if( oldName != newName )
  244. {
  245. wxString libName = m_Parent->GetCurLib();
  246. if( m_Parent->GetLibManager().PartExists( newName, libName ) )
  247. {
  248. wxString msg;
  249. msg.Printf( _( "The name '%s' conflicts with an existing entry in the library '%s'." ),
  250. newName, libName );
  251. DisplayErrorMessage( this, msg );
  252. return false;
  253. }
  254. m_Parent->SaveCopyInUndoList( m_libEntry, UNDO_REDO::LIB_RENAME );
  255. }
  256. else
  257. {
  258. m_Parent->SaveCopyInUndoList( m_libEntry );
  259. }
  260. // The Y axis for components in lib is from bottom to top while the screen axis is top
  261. // to bottom: we must change the y coord sign when writing back to the library
  262. for( size_t i = 0; i < m_fields->size(); ++i )
  263. {
  264. wxPoint pos = m_fields->at( i ).GetPosition();
  265. pos.y = -pos.y;
  266. m_fields->at( i ).SetPosition( pos );
  267. }
  268. m_libEntry->SetFields( *m_fields );
  269. // Update the parent for inherited symbols
  270. if( m_libEntry->IsAlias() )
  271. {
  272. wxString parentName = m_inheritanceSelectCombo->GetValue();
  273. // The parentName was verified to be non-empty in the Validator
  274. wxString libName = m_Parent->GetCurLib();
  275. // Get the parent from the libManager based on the name set in the inheritance combo box.
  276. LIB_PART* newParent = m_Parent->GetLibManager().GetAlias( parentName, libName );
  277. // Verify that the requested parent exists
  278. wxCHECK( newParent, false );
  279. // Verify that the new parent is not an alias.
  280. wxCHECK( !newParent->IsAlias(), false );
  281. m_libEntry->SetParent( newParent );
  282. }
  283. // We need to keep the name and the value the same at the moment!
  284. m_libEntry->SetName( newName );
  285. m_libEntry->SetDescription( m_DescCtrl->GetValue() );
  286. m_libEntry->SetKeyWords( m_KeywordCtrl->GetValue() );
  287. m_libEntry->SetUnitCount( m_SelNumberOfUnits->GetValue() );
  288. m_libEntry->LockUnits( m_libEntry->GetUnitCount() > 1 && m_OptionPartsLocked->GetValue() );
  289. m_libEntry->SetConversion( m_AsConvertButt->GetValue() );
  290. if( m_OptionPower->GetValue() )
  291. m_libEntry->SetPower();
  292. else
  293. m_libEntry->SetNormal();
  294. m_libEntry->SetIncludeInBom( !m_excludeFromBomCheckBox->GetValue() );
  295. m_libEntry->SetIncludeOnBoard( !m_excludeFromBoardCheckBox->GetValue() );
  296. m_libEntry->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
  297. m_libEntry->SetShowPinNames( m_ShowPinNameButt->GetValue() );
  298. if( m_PinsNameInsideButt->GetValue() )
  299. {
  300. int offset = KiROUND( (double) m_pinNameOffset.GetValue() );
  301. // We interpret an offset of 0 as "outside", so make sure it's non-zero
  302. m_libEntry->SetPinNameOffset( offset == 0 ? 20 : offset );
  303. }
  304. else
  305. {
  306. m_libEntry->SetPinNameOffset( 0 ); // pin text outside the body (name is on the pin)
  307. }
  308. m_libEntry->SetFPFilters( m_FootprintFilterListBox->GetStrings());
  309. m_Parent->UpdateAfterSymbolProperties( &oldName );
  310. // It's possible that the symbol being edited has no pins, in which case there may be no
  311. // alternate body style objects causing #LIB_PART::HasCoversion() to always return false.
  312. // This allows the user to edit the alternate body style just in case this condition occurs.
  313. m_Parent->SetShowDeMorgan( m_AsConvertButt->GetValue() );
  314. return true;
  315. }
  316. void DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging( wxGridEvent& event )
  317. {
  318. wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
  319. wxControl* control = editor->GetControl();
  320. if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
  321. {
  322. event.Veto();
  323. m_delayedFocusGrid = m_grid;
  324. m_delayedFocusRow = event.GetRow();
  325. m_delayedFocusColumn = event.GetCol();
  326. m_delayedFocusPage = 0;
  327. }
  328. else if( event.GetCol() == FDC_NAME )
  329. {
  330. wxString newName = event.GetString();
  331. for( int i = 0; i < m_grid->GetNumberRows(); ++i )
  332. {
  333. if( i == event.GetRow() )
  334. continue;
  335. if( newName.CmpNoCase( m_grid->GetCellValue( i, FDC_NAME ) ) == 0 )
  336. {
  337. DisplayError( this, wxString::Format( _( "The name '%s' is already in use." ),
  338. newName ) );
  339. event.Veto();
  340. m_delayedFocusRow = event.GetRow();
  341. m_delayedFocusColumn = event.GetCol();
  342. }
  343. }
  344. }
  345. else if( event.GetRow() == VALUE_FIELD && event.GetCol() == FDC_VALUE )
  346. m_SymbolNameCtrl->ChangeValue( event.GetString() );
  347. editor->DecRef();
  348. }
  349. void DIALOG_LIB_SYMBOL_PROPERTIES::OnSymbolNameText( wxCommandEvent& event )
  350. {
  351. m_grid->SetCellValue( VALUE_FIELD, FDC_VALUE, m_SymbolNameCtrl->GetValue() );
  352. }
  353. void DIALOG_LIB_SYMBOL_PROPERTIES::OnSymbolNameKillFocus( wxFocusEvent& event )
  354. {
  355. if( !m_delayedFocusCtrl && !m_SymbolNameCtrl->GetValidator()->Validate( m_SymbolNameCtrl ) )
  356. {
  357. m_delayedFocusCtrl = m_SymbolNameCtrl;
  358. m_delayedFocusPage = 0;
  359. }
  360. event.Skip();
  361. }
  362. void DIALOG_LIB_SYMBOL_PROPERTIES::OnAddField( wxCommandEvent& event )
  363. {
  364. if( !m_grid->CommitPendingChanges() )
  365. return;
  366. auto* settings = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
  367. int fieldID = m_fields->size();
  368. LIB_FIELD newField( m_libEntry, fieldID );
  369. newField.SetTextSize( wxSize( Mils2iu( settings->m_Defaults.text_size ),
  370. Mils2iu( settings->m_Defaults.text_size ) ) );
  371. m_fields->push_back( newField );
  372. // notify the grid
  373. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
  374. m_grid->ProcessTableMessage( msg );
  375. m_grid->MakeCellVisible( (int) m_fields->size() - 1, 0 );
  376. m_grid->SetGridCursor( (int) m_fields->size() - 1, 0 );
  377. m_grid->EnableCellEditControl();
  378. m_grid->ShowCellEditControl();
  379. }
  380. void DIALOG_LIB_SYMBOL_PROPERTIES::OnDeleteField( wxCommandEvent& event )
  381. {
  382. int curRow = m_grid->GetGridCursorRow();
  383. if( curRow < 0 )
  384. {
  385. return;
  386. }
  387. else if( curRow < MANDATORY_FIELDS )
  388. {
  389. DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
  390. MANDATORY_FIELDS ) );
  391. return;
  392. }
  393. m_grid->CommitPendingChanges( true /* quiet mode */ );
  394. m_fields->erase( m_fields->begin() + curRow );
  395. // notify the grid
  396. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, curRow, 1 );
  397. m_grid->ProcessTableMessage( msg );
  398. if( m_grid->GetNumberRows() > 0 )
  399. {
  400. m_grid->MakeCellVisible( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
  401. m_grid->SetGridCursor( std::max( 0, curRow-1 ), m_grid->GetGridCursorCol() );
  402. }
  403. }
  404. void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
  405. {
  406. if( !m_grid->CommitPendingChanges() )
  407. return;
  408. int i = m_grid->GetGridCursorRow();
  409. if( i > MANDATORY_FIELDS )
  410. {
  411. LIB_FIELD tmp = m_fields->at( (unsigned) i );
  412. m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
  413. m_fields->insert( m_fields->begin() + i - 1, tmp );
  414. m_grid->ForceRefresh();
  415. m_grid->SetGridCursor( i - 1, m_grid->GetGridCursorCol() );
  416. m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
  417. }
  418. else
  419. wxBell();
  420. }
  421. void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
  422. {
  423. if( !m_grid->CommitPendingChanges() )
  424. return;
  425. int i = m_grid->GetGridCursorRow();
  426. if( i >= MANDATORY_FIELDS && i + 1 < m_fields->GetNumberRows() )
  427. {
  428. LIB_FIELD tmp = m_fields->at( (unsigned) i );
  429. m_fields->erase( m_fields->begin() + i, m_fields->begin() + i + 1 );
  430. m_fields->insert( m_fields->begin() + i + 1, tmp );
  431. m_grid->ForceRefresh();
  432. m_grid->SetGridCursor( i + 1, m_grid->GetGridCursorCol() );
  433. m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
  434. }
  435. else
  436. wxBell();
  437. }
  438. void DIALOG_LIB_SYMBOL_PROPERTIES::OnEditSpiceModel( wxCommandEvent& event )
  439. {
  440. #ifdef KICAD_SPICE
  441. int diff = m_fields->size();
  442. auto cmp = SCH_COMPONENT( *m_libEntry, m_libEntry->GetLibId(), nullptr );
  443. DIALOG_SPICE_MODEL dialog( this, cmp, m_fields );
  444. if( dialog.ShowModal() != wxID_OK )
  445. return;
  446. diff = (int) m_fields->size() - diff;
  447. if( diff > 0 )
  448. {
  449. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, diff );
  450. m_grid->ProcessTableMessage( msg );
  451. }
  452. else if( diff < 0 )
  453. {
  454. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, -diff );
  455. m_grid->ProcessTableMessage( msg );
  456. }
  457. m_grid->ForceRefresh();
  458. #endif /* KICAD_SPICE */
  459. }
  460. void DIALOG_LIB_SYMBOL_PROPERTIES::OnFilterDClick( wxMouseEvent& event)
  461. {
  462. int idx = m_FootprintFilterListBox->HitTest( event.GetPosition() );
  463. wxCommandEvent dummy;
  464. if( idx >= 0 )
  465. OnEditFootprintFilter( dummy );
  466. else
  467. OnAddFootprintFilter( dummy );
  468. }
  469. void DIALOG_LIB_SYMBOL_PROPERTIES::OnCancelButtonClick( wxCommandEvent& event )
  470. {
  471. // Running the Footprint Browser gums up the works and causes the automatic cancel
  472. // stuff to no longer work. So we do it here ourselves.
  473. EndQuasiModal( wxID_CANCEL );
  474. }
  475. void DIALOG_LIB_SYMBOL_PROPERTIES::OnAddFootprintFilter( wxCommandEvent& event )
  476. {
  477. wxString filterLine;
  478. WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Add Footprint Filter" ), filterLine );
  479. if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue().IsEmpty() )
  480. return;
  481. filterLine = dlg.GetValue();
  482. filterLine.Replace( wxT( " " ), wxT( "_" ) );
  483. // duplicate filters do no harm, so don't be a nanny.
  484. m_FootprintFilterListBox->Append( filterLine );
  485. m_FootprintFilterListBox->SetSelection( (int) m_FootprintFilterListBox->GetCount() - 1 );
  486. }
  487. void DIALOG_LIB_SYMBOL_PROPERTIES::OnDeleteFootprintFilter( wxCommandEvent& event )
  488. {
  489. int ii = m_FootprintFilterListBox->GetSelection();
  490. if( ii >= 0 )
  491. {
  492. m_FootprintFilterListBox->Delete( (unsigned) ii );
  493. if( m_FootprintFilterListBox->GetCount() == 0 )
  494. m_FootprintFilterListBox->SetSelection( wxNOT_FOUND );
  495. else
  496. m_FootprintFilterListBox->SetSelection( std::max( 0, ii - 1 ) );
  497. }
  498. }
  499. void DIALOG_LIB_SYMBOL_PROPERTIES::OnEditFootprintFilter( wxCommandEvent& event )
  500. {
  501. int idx = m_FootprintFilterListBox->GetSelection();
  502. if( idx >= 0 )
  503. {
  504. wxString filter = m_FootprintFilterListBox->GetStringSelection();
  505. WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Edit Footprint Filter" ), filter );
  506. if( dlg.ShowModal() == wxID_OK && !dlg.GetValue().IsEmpty() )
  507. m_FootprintFilterListBox->SetString( (unsigned) idx, dlg.GetValue() );
  508. }
  509. }
  510. void DIALOG_LIB_SYMBOL_PROPERTIES::adjustGridColumns( int aWidth )
  511. {
  512. m_width = aWidth;
  513. // Account for scroll bars
  514. aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
  515. m_grid->AutoSizeColumn( FDC_NAME );
  516. int fixedColsWidth = m_grid->GetColSize( FDC_NAME );
  517. for( int i = 2; i < m_grid->GetNumberCols(); i++ )
  518. fixedColsWidth += m_grid->GetColSize( i );
  519. m_grid->SetColSize( FDC_VALUE, aWidth - fixedColsWidth );
  520. }
  521. void DIALOG_LIB_SYMBOL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
  522. {
  523. m_OptionPartsLocked->Enable( m_SelNumberOfUnits->GetValue() > 1 );
  524. m_pinNameOffset.Enable( m_PinsNameInsideButt->GetValue() );
  525. if( m_grid->IsCellEditControlShown() )
  526. {
  527. int row = m_grid->GetGridCursorRow();
  528. int col = m_grid->GetGridCursorCol();
  529. if( row == VALUE_FIELD && col == FDC_VALUE )
  530. {
  531. wxGridCellEditor* editor = m_grid->GetCellEditor( row, col );
  532. m_SymbolNameCtrl->ChangeValue( editor->GetValue() );
  533. editor->DecRef();
  534. }
  535. }
  536. // Handle shown columns changes
  537. wxString shownColumns = m_grid->GetShownColumns();
  538. if( shownColumns != m_shownColumns )
  539. {
  540. m_shownColumns = shownColumns;
  541. if( !m_grid->IsCellEditControlShown() )
  542. adjustGridColumns( m_grid->GetRect().GetWidth() );
  543. }
  544. // Handle a delayed focus. The delay allows us to:
  545. // a) change focus when the error was triggered from within a killFocus handler
  546. // b) show the correct notebook page in the background before the error dialog comes up
  547. // when triggered from an OK or a notebook page change
  548. if( m_delayedFocusPage >= 0 && m_NoteBook->GetSelection() != m_delayedFocusPage )
  549. {
  550. m_NoteBook->SetSelection( (unsigned) m_delayedFocusPage );
  551. m_delayedFocusPage = -1;
  552. }
  553. if( !m_delayedErrorMessage.IsEmpty() )
  554. {
  555. // We will re-enter this routine when the error dialog is displayed, so make
  556. // sure we don't keep putting up more dialogs.
  557. wxString msg = m_delayedErrorMessage;
  558. m_delayedErrorMessage = wxEmptyString;
  559. // Do not use DisplayErrorMessage(); it screws up window order on Mac
  560. DisplayError( nullptr, msg );
  561. }
  562. if( m_delayedFocusCtrl )
  563. {
  564. m_delayedFocusCtrl->SetFocus();
  565. if( auto textEntry = dynamic_cast<wxTextEntry*>( m_delayedFocusCtrl ) )
  566. textEntry->SelectAll();
  567. m_delayedFocusCtrl = nullptr;
  568. }
  569. else if( m_delayedFocusGrid )
  570. {
  571. m_delayedFocusGrid->SetFocus();
  572. m_delayedFocusGrid->MakeCellVisible( m_delayedFocusRow, m_delayedFocusColumn );
  573. m_delayedFocusGrid->SetGridCursor( m_delayedFocusRow, m_delayedFocusColumn );
  574. m_delayedFocusGrid->EnableCellEditControl( true );
  575. m_delayedFocusGrid->ShowCellEditControl();
  576. m_delayedFocusGrid = nullptr;
  577. m_delayedFocusRow = -1;
  578. m_delayedFocusColumn = -1;
  579. }
  580. }
  581. void DIALOG_LIB_SYMBOL_PROPERTIES::OnSizeGrid( wxSizeEvent& event )
  582. {
  583. auto new_size = event.GetSize().GetX();
  584. if( new_size != m_width )
  585. {
  586. adjustGridColumns( event.GetSize().GetX() );
  587. }
  588. // Always propagate a wxSizeEvent:
  589. event.Skip();
  590. }
  591. void DIALOG_LIB_SYMBOL_PROPERTIES::syncControlStates( bool aIsAlias )
  592. {
  593. bSizerLowerBasicPanel->Show( !aIsAlias );
  594. #ifdef KICAD_SPICE
  595. m_spiceFieldsButton->Show( !aIsAlias );
  596. #endif
  597. m_inheritanceSelectCombo->Enable( aIsAlias );
  598. m_inheritsStaticText->Enable( aIsAlias );
  599. m_grid->ForceRefresh();
  600. }
  601. void DIALOG_LIB_SYMBOL_PROPERTIES::onPowerCheckBox( wxCommandEvent& aEvent )
  602. {
  603. if( m_OptionPower->IsChecked() )
  604. {
  605. m_excludeFromBomCheckBox->SetValue( true );
  606. m_excludeFromBoardCheckBox->SetValue( true );
  607. m_excludeFromBomCheckBox->Enable( false );
  608. m_excludeFromBoardCheckBox->Enable( false );
  609. }
  610. else
  611. {
  612. m_excludeFromBomCheckBox->Enable( true );
  613. m_excludeFromBoardCheckBox->Enable( true );
  614. }
  615. }