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.

1156 lines
36 KiB

2 years ago
2 years ago
4 months ago
4 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The 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 "dialog_symbol_properties.h"
  24. #include <memory>
  25. #include <bitmaps.h>
  26. #include <wx/tooltip.h>
  27. #include <grid_tricks.h>
  28. #include <confirm.h>
  29. #include <kiface_base.h>
  30. #include <pin_numbers.h>
  31. #include <string_utils.h>
  32. #include <kiplatform/ui.h>
  33. #include <widgets/grid_icon_text_helpers.h>
  34. #include <widgets/grid_combobox.h>
  35. #include <widgets/std_bitmap_button.h>
  36. #include <settings/settings_manager.h>
  37. #include <sch_collectors.h>
  38. #include <symbol_library.h>
  39. #include <fields_grid_table.h>
  40. #include <sch_edit_frame.h>
  41. #include <sch_reference_list.h>
  42. #include <schematic.h>
  43. #include <sch_commit.h>
  44. #include <tool/tool_manager.h>
  45. #include <tool/actions.h>
  46. #include <dialog_sim_model.h>
  47. wxDEFINE_EVENT( SYMBOL_DELAY_FOCUS, wxCommandEvent );
  48. wxDEFINE_EVENT( SYMBOL_DELAY_SELECTION, wxCommandEvent );
  49. enum PIN_TABLE_COL_ORDER
  50. {
  51. COL_NUMBER,
  52. COL_BASE_NAME,
  53. COL_ALT_NAME,
  54. COL_TYPE,
  55. COL_SHAPE,
  56. COL_COUNT // keep as last
  57. };
  58. class SCH_PIN_TABLE_DATA_MODEL : public WX_GRID_TABLE_BASE, public std::vector<SCH_PIN>
  59. {
  60. public:
  61. SCH_PIN_TABLE_DATA_MODEL() :
  62. m_readOnlyAttr( nullptr ),
  63. m_typeAttr( nullptr ),
  64. m_shapeAttr( nullptr )
  65. {
  66. }
  67. ~SCH_PIN_TABLE_DATA_MODEL()
  68. {
  69. for( wxGridCellAttr* attr : m_nameAttrs )
  70. attr->DecRef();
  71. m_readOnlyAttr->DecRef();
  72. m_typeAttr->DecRef();
  73. m_shapeAttr->DecRef();
  74. }
  75. void BuildAttrs()
  76. {
  77. for( wxGridCellAttr* attr : m_nameAttrs )
  78. attr->DecRef();
  79. m_nameAttrs.clear();
  80. if( m_readOnlyAttr )
  81. m_readOnlyAttr->DecRef();
  82. m_readOnlyAttr = new wxGridCellAttr;
  83. m_readOnlyAttr->SetReadOnly( true );
  84. for( const SCH_PIN& pin : *this )
  85. {
  86. SCH_PIN* lib_pin = pin.GetLibPin();
  87. wxGridCellAttr* attr = nullptr;
  88. if( lib_pin->GetAlternates().empty() )
  89. {
  90. attr = new wxGridCellAttr;
  91. attr->SetReadOnly( true );
  92. attr->SetBackgroundColour( KIPLATFORM::UI::GetDialogBGColour() );
  93. }
  94. else
  95. {
  96. wxArrayString choices;
  97. choices.push_back( lib_pin->GetName() );
  98. for( const std::pair<const wxString, SCH_PIN::ALT>& alt : lib_pin->GetAlternates() )
  99. choices.push_back( alt.first );
  100. attr = new wxGridCellAttr();
  101. attr->SetEditor( new GRID_CELL_COMBOBOX( choices ) );
  102. }
  103. m_nameAttrs.push_back( attr );
  104. }
  105. if( m_typeAttr )
  106. m_typeAttr->DecRef();
  107. m_typeAttr = new wxGridCellAttr;
  108. m_typeAttr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinTypeIcons(), PinTypeNames() ) );
  109. m_typeAttr->SetReadOnly( true );
  110. if( m_shapeAttr )
  111. m_shapeAttr->DecRef();
  112. m_shapeAttr = new wxGridCellAttr;
  113. m_shapeAttr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinShapeIcons(), PinShapeNames() ) );
  114. m_shapeAttr->SetReadOnly( true );
  115. }
  116. int GetNumberRows() override { return (int) size(); }
  117. int GetNumberCols() override { return COL_COUNT; }
  118. wxString GetColLabelValue( int aCol ) override
  119. {
  120. switch( aCol )
  121. {
  122. case COL_NUMBER: return _( "Number" );
  123. case COL_BASE_NAME: return _( "Base Name" );
  124. case COL_ALT_NAME: return _( "Alternate Assignment" );
  125. case COL_TYPE: return _( "Electrical Type" );
  126. case COL_SHAPE: return _( "Graphic Style" );
  127. default: wxFAIL; return wxEmptyString;
  128. }
  129. }
  130. bool IsEmptyCell( int row, int col ) override
  131. {
  132. return false; // don't allow adjacent cell overflow, even if we are actually empty
  133. }
  134. bool CanSetValueAs( int aRow, int aCol, const wxString& aTypeName ) override
  135. {
  136. // Don't accept random values; must use the popup to change to a known alternate
  137. return false;
  138. }
  139. wxString GetValue( int aRow, int aCol ) override
  140. {
  141. return GetValue( at( aRow ), aCol );
  142. }
  143. static wxString GetValue( const SCH_PIN& aPin, int aCol )
  144. {
  145. if( aCol == COL_ALT_NAME )
  146. {
  147. if( aPin.GetLibPin()->GetAlternates().empty() )
  148. return wxEmptyString;
  149. else if( aPin.GetAlt().IsEmpty() )
  150. return aPin.GetName();
  151. else
  152. return aPin.GetAlt();
  153. }
  154. switch( aCol )
  155. {
  156. case COL_NUMBER: return aPin.GetNumber();
  157. case COL_BASE_NAME: return aPin.GetLibPin()->GetName();
  158. case COL_TYPE: return PinTypeNames()[static_cast<int>( aPin.GetType() )];
  159. case COL_SHAPE: return PinShapeNames()[static_cast<int>( aPin.GetShape() )];
  160. default: wxFAIL; return wxEmptyString;
  161. }
  162. }
  163. wxGridCellAttr* GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind ) override
  164. {
  165. switch( aCol )
  166. {
  167. case COL_NUMBER:
  168. case COL_BASE_NAME:
  169. m_readOnlyAttr->IncRef();
  170. return enhanceAttr( m_readOnlyAttr, aRow, aCol, aKind );
  171. case COL_ALT_NAME:
  172. m_nameAttrs[ aRow ]->IncRef();
  173. return enhanceAttr( m_nameAttrs[ aRow ], aRow, aCol, aKind );
  174. case COL_TYPE:
  175. m_typeAttr->IncRef();
  176. return enhanceAttr( m_typeAttr, aRow, aCol, aKind );
  177. case COL_SHAPE:
  178. m_shapeAttr->IncRef();
  179. return enhanceAttr( m_shapeAttr, aRow, aCol, aKind );
  180. default:
  181. wxFAIL;
  182. return nullptr;
  183. }
  184. }
  185. void SetValue( int aRow, int aCol, const wxString &aValue ) override
  186. {
  187. switch( aCol )
  188. {
  189. case COL_ALT_NAME:
  190. if( aValue == at( aRow ).GetLibPin()->GetName() )
  191. at( aRow ).SetAlt( wxEmptyString );
  192. else
  193. at( aRow ).SetAlt( aValue );
  194. break;
  195. case COL_NUMBER:
  196. case COL_BASE_NAME:
  197. case COL_TYPE:
  198. case COL_SHAPE:
  199. // Read-only.
  200. break;
  201. default:
  202. wxFAIL;
  203. break;
  204. }
  205. }
  206. static bool compare( const SCH_PIN& lhs, const SCH_PIN& rhs, int sortCol, bool ascending )
  207. {
  208. wxString lhStr = GetValue( lhs, sortCol );
  209. wxString rhStr = GetValue( rhs, sortCol );
  210. if( lhStr == rhStr )
  211. {
  212. // Secondary sort key is always COL_NUMBER
  213. sortCol = COL_NUMBER;
  214. lhStr = GetValue( lhs, sortCol );
  215. rhStr = GetValue( rhs, sortCol );
  216. }
  217. bool res;
  218. // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
  219. // to get the opposite sort. i.e. ~(a<b) != (a>b)
  220. auto cmp = [ ascending ]( const auto a, const auto b )
  221. {
  222. if( ascending )
  223. return a < b;
  224. else
  225. return b < a;
  226. };
  227. switch( sortCol )
  228. {
  229. case COL_NUMBER:
  230. case COL_BASE_NAME:
  231. case COL_ALT_NAME:
  232. res = cmp( PIN_NUMBERS::Compare( lhStr, rhStr ), 0 );
  233. break;
  234. case COL_TYPE:
  235. case COL_SHAPE:
  236. res = cmp( lhStr.CmpNoCase( rhStr ), 0 );
  237. break;
  238. default:
  239. res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
  240. break;
  241. }
  242. return res;
  243. }
  244. void SortRows( int aSortCol, bool ascending )
  245. {
  246. std::sort( begin(), end(),
  247. [ aSortCol, ascending ]( const SCH_PIN& lhs, const SCH_PIN& rhs ) -> bool
  248. {
  249. return compare( lhs, rhs, aSortCol, ascending );
  250. } );
  251. }
  252. protected:
  253. std::vector<wxGridCellAttr*> m_nameAttrs;
  254. wxGridCellAttr* m_readOnlyAttr;
  255. wxGridCellAttr* m_typeAttr;
  256. wxGridCellAttr* m_shapeAttr;
  257. };
  258. DIALOG_SYMBOL_PROPERTIES::DIALOG_SYMBOL_PROPERTIES( SCH_EDIT_FRAME* aParent, SCH_SYMBOL* aSymbol ) :
  259. DIALOG_SYMBOL_PROPERTIES_BASE( aParent ),
  260. m_symbol( nullptr ),
  261. m_part( nullptr ),
  262. m_lastRequestedPinsSize( 0, 0 ),
  263. m_editorShown( false ),
  264. m_fields( nullptr ),
  265. m_dataModel( nullptr )
  266. {
  267. m_symbol = aSymbol;
  268. m_part = m_symbol->GetLibSymbolRef().get();
  269. // GetLibSymbolRef() now points to the cached part in the schematic, which should always be
  270. // there for usual cases, but can be null when opening old schematics not storing the part
  271. // so we need to handle m_part == nullptr
  272. // wxASSERT( m_part );
  273. m_fields = new FIELDS_GRID_TABLE( this, aParent, m_fieldsGrid, m_symbol );
  274. m_fieldsGrid->SetTable( m_fields );
  275. m_fieldsGrid->PushEventHandler( new FIELDS_GRID_TRICKS( m_fieldsGrid, this,
  276. { &aParent->Schematic(), m_part },
  277. [&]( wxCommandEvent& aEvent )
  278. {
  279. OnAddField( aEvent );
  280. } ) );
  281. m_fieldsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
  282. // Show/hide columns according to user's preference
  283. if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
  284. {
  285. m_fieldsGrid->ShowHideColumns( cfg->m_Appearance.edit_symbol_visible_columns );
  286. m_shownColumns = m_fieldsGrid->GetShownColumns();
  287. }
  288. if( m_part && m_part->IsMultiBodyStyle() )
  289. {
  290. // Multiple body styles are a superclass of alternate pin assignments, so don't allow
  291. // free-form alternate assignments as well. (We won't know how to map the alternates
  292. // back and forth when the body style is changed.)
  293. m_pinTablePage->Disable();
  294. m_pinTablePage->SetToolTip( _( "Alternate pin assignments are not available for symbols with multiple "
  295. "body styles." ) );
  296. }
  297. else
  298. {
  299. m_dataModel = new SCH_PIN_TABLE_DATA_MODEL();
  300. // Make a copy of the pins for editing
  301. for( const std::unique_ptr<SCH_PIN>& pin : m_symbol->GetRawPins() )
  302. m_dataModel->push_back( *pin );
  303. m_dataModel->SortRows( COL_NUMBER, true );
  304. m_dataModel->BuildAttrs();
  305. m_pinGrid->SetTable( m_dataModel );
  306. }
  307. if( m_part && m_part->IsPower() )
  308. m_spiceFieldsButton->Hide();
  309. m_pinGrid->PushEventHandler( new GRID_TRICKS( m_pinGrid ) );
  310. m_pinGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
  311. wxFont infoFont = KIUI::GetSmallInfoFont( this );
  312. m_libraryIDLabel->SetFont( infoFont );
  313. m_tcLibraryID->SetFont( infoFont );
  314. m_tcLibraryID->SetBackgroundColour( KIPLATFORM::UI::GetDialogBGColour() );
  315. wxToolTip::Enable( true );
  316. SetupStandardButtons();
  317. // Configure button logos
  318. m_bpAdd->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
  319. m_bpDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
  320. m_bpMoveUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
  321. m_bpMoveDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
  322. // wxFormBuilder doesn't include this event...
  323. m_fieldsGrid->Bind( wxEVT_GRID_CELL_CHANGING, &DIALOG_SYMBOL_PROPERTIES::OnGridCellChanging, this );
  324. m_pinGrid->Bind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_PROPERTIES::OnPinTableColSort, this );
  325. Bind( SYMBOL_DELAY_FOCUS, &DIALOG_SYMBOL_PROPERTIES::HandleDelayedFocus, this );
  326. Bind( SYMBOL_DELAY_SELECTION, &DIALOG_SYMBOL_PROPERTIES::HandleDelayedSelection, this );
  327. wxCommandEvent* evt = new wxCommandEvent( SYMBOL_DELAY_SELECTION );
  328. evt->SetClientData( new VECTOR2I( 0, FDC_VALUE ) );
  329. QueueEvent( evt );
  330. evt = new wxCommandEvent( SYMBOL_DELAY_FOCUS );
  331. evt->SetClientData( new VECTOR2I( 0, FDC_VALUE ) );
  332. QueueEvent( evt );
  333. finishDialogSettings();
  334. }
  335. DIALOG_SYMBOL_PROPERTIES::~DIALOG_SYMBOL_PROPERTIES()
  336. {
  337. if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
  338. {
  339. cfg->m_Appearance.edit_symbol_visible_columns = m_fieldsGrid->GetShownColumnsAsString();
  340. cfg->m_Appearance.edit_symbol_width = GetSize().x;
  341. cfg->m_Appearance.edit_symbol_height = GetSize().y;
  342. }
  343. // Prevents crash bug in wxGrid's d'tor
  344. m_fieldsGrid->DestroyTable( m_fields );
  345. if( m_dataModel )
  346. m_pinGrid->DestroyTable( m_dataModel );
  347. m_fieldsGrid->Unbind( wxEVT_GRID_CELL_CHANGING, &DIALOG_SYMBOL_PROPERTIES::OnGridCellChanging, this );
  348. m_pinGrid->Unbind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_PROPERTIES::OnPinTableColSort, this );
  349. Unbind( SYMBOL_DELAY_FOCUS, &DIALOG_SYMBOL_PROPERTIES::HandleDelayedFocus, this );
  350. Unbind( SYMBOL_DELAY_SELECTION, &DIALOG_SYMBOL_PROPERTIES::HandleDelayedSelection, this );
  351. // Delete the GRID_TRICKS.
  352. m_fieldsGrid->PopEventHandler( true );
  353. m_pinGrid->PopEventHandler( true );
  354. }
  355. SCH_EDIT_FRAME* DIALOG_SYMBOL_PROPERTIES::GetParent()
  356. {
  357. return dynamic_cast<SCH_EDIT_FRAME*>( wxDialog::GetParent() );
  358. }
  359. bool DIALOG_SYMBOL_PROPERTIES::TransferDataToWindow()
  360. {
  361. if( !wxDialog::TransferDataToWindow() )
  362. return false;
  363. std::set<wxString> defined;
  364. // Push a copy of each field into m_updateFields
  365. for( SCH_FIELD& srcField : m_symbol->GetFields() )
  366. {
  367. SCH_FIELD field( srcField );
  368. // change offset to be symbol-relative
  369. field.Offset( -m_symbol->GetPosition() );
  370. field.SetText( m_symbol->Schematic()->ConvertKIIDsToRefs( field.GetText() ) );
  371. defined.insert( field.GetName() );
  372. m_fields->push_back( field );
  373. }
  374. // Add in any template fieldnames not yet defined:
  375. for( const TEMPLATE_FIELDNAME& templateFieldname :
  376. GetParent()->Schematic().Settings().m_TemplateFieldNames.GetTemplateFieldNames() )
  377. {
  378. if( defined.count( templateFieldname.m_Name ) <= 0 )
  379. {
  380. SCH_FIELD field( m_symbol, FIELD_T::USER, templateFieldname.m_Name );
  381. field.SetVisible( templateFieldname.m_Visible );
  382. m_fields->push_back( field );
  383. }
  384. }
  385. // notify the grid
  386. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
  387. m_fieldsGrid->ProcessTableMessage( msg );
  388. // If a multi-unit symbol, set up the unit selector and interchangeable checkbox.
  389. if( m_symbol->IsMultiUnit() )
  390. {
  391. // Ensure symbol unit is the currently selected unit (mandatory in complex hierarchies)
  392. // from the current sheet path, because it can be modified by previous calculations
  393. m_symbol->SetUnit( m_symbol->GetUnitSelection( &GetParent()->GetCurrentSheet() ) );
  394. for( int ii = 1; ii <= m_symbol->GetUnitCount(); ii++ )
  395. m_unitChoice->Append( m_symbol->GetUnitDisplayName( ii, false ) );
  396. if( m_symbol->GetUnit() <= ( int )m_unitChoice->GetCount() )
  397. m_unitChoice->SetSelection( m_symbol->GetUnit() - 1 );
  398. }
  399. else
  400. {
  401. m_unitLabel->Enable( false );
  402. m_unitChoice->Enable( false );
  403. }
  404. if( m_part && m_part->IsMultiBodyStyle() )
  405. {
  406. for( int ii = 0; ii < m_part->GetBodyStyleCount(); ii++ )
  407. m_bodyStyleChoice->Append( m_part->GetBodyStyleNames()[ii] );
  408. if( m_symbol->GetBodyStyle() <= (int) m_bodyStyleChoice->GetCount() )
  409. m_bodyStyleChoice->SetSelection( m_symbol->GetBodyStyle() - 1 );
  410. }
  411. else
  412. {
  413. m_bodyStyle->Enable( false );
  414. m_bodyStyleChoice->Enable( false );
  415. }
  416. // Set the symbol orientation and mirroring.
  417. int orientation = m_symbol->GetOrientation() & ~( SYM_MIRROR_X | SYM_MIRROR_Y );
  418. switch( orientation )
  419. {
  420. default:
  421. case SYM_ORIENT_0: m_orientationCtrl->SetSelection( 0 ); break;
  422. case SYM_ORIENT_90: m_orientationCtrl->SetSelection( 1 ); break;
  423. case SYM_ORIENT_270: m_orientationCtrl->SetSelection( 2 ); break;
  424. case SYM_ORIENT_180: m_orientationCtrl->SetSelection( 3 ); break;
  425. }
  426. int mirror = m_symbol->GetOrientation() & ( SYM_MIRROR_X | SYM_MIRROR_Y );
  427. switch( mirror )
  428. {
  429. default: m_mirrorCtrl->SetSelection( 0 ) ; break;
  430. case SYM_MIRROR_X: m_mirrorCtrl->SetSelection( 1 ); break;
  431. case SYM_MIRROR_Y: m_mirrorCtrl->SetSelection( 2 ); break;
  432. }
  433. m_cbExcludeFromSim->SetValue( m_symbol->GetExcludedFromSim() );
  434. m_cbExcludeFromBom->SetValue( m_symbol->GetExcludedFromBOM() );
  435. m_cbExcludeFromBoard->SetValue( m_symbol->GetExcludedFromBoard() );
  436. m_cbDNP->SetValue( m_symbol->GetDNP() );
  437. if( m_part )
  438. {
  439. m_ShowPinNumButt->SetValue( m_part->GetShowPinNumbers() );
  440. m_ShowPinNameButt->SetValue( m_part->GetShowPinNames() );
  441. }
  442. // Set the symbol's library name.
  443. m_tcLibraryID->SetValue( UnescapeString( m_symbol->GetLibId().Format() ) );
  444. Layout();
  445. m_fieldsGrid->Layout();
  446. #ifdef __WXGTK__
  447. wxSafeYield();
  448. #endif
  449. return true;
  450. }
  451. void DIALOG_SYMBOL_PROPERTIES::OnEditSpiceModel( wxCommandEvent& event )
  452. {
  453. if( !m_fieldsGrid->CommitPendingChanges() )
  454. return;
  455. m_fieldsGrid->ClearSelection();
  456. std::vector<SCH_FIELD> fields;
  457. for( const SCH_FIELD& field : *m_fields )
  458. fields.emplace_back( field );
  459. DIALOG_SIM_MODEL dialog( this, m_parentFrame, *m_symbol, fields );
  460. if( dialog.ShowModal() != wxID_OK )
  461. return;
  462. // Add in any new fields
  463. for( const SCH_FIELD& editedField : fields )
  464. {
  465. bool found = false;
  466. for( SCH_FIELD& existingField : *m_fields )
  467. {
  468. if( existingField.GetName() == editedField.GetName() )
  469. {
  470. found = true;
  471. existingField.SetText( editedField.GetText() );
  472. break;
  473. }
  474. }
  475. if( !found )
  476. {
  477. m_fields->emplace_back( editedField );
  478. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
  479. m_fieldsGrid->ProcessTableMessage( msg );
  480. }
  481. }
  482. // Remove any deleted fields
  483. for( int ii = (int) m_fields->size() - 1; ii >= 0; --ii )
  484. {
  485. SCH_FIELD& existingField = m_fields->at( ii );
  486. bool found = false;
  487. for( SCH_FIELD& editedField : fields )
  488. {
  489. if( editedField.GetName() == existingField.GetName() )
  490. {
  491. found = true;
  492. break;
  493. }
  494. }
  495. if( !found )
  496. {
  497. m_fieldsGrid->ClearSelection();
  498. m_fields->erase( m_fields->begin() + ii );
  499. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, ii, 1 );
  500. m_fieldsGrid->ProcessTableMessage( msg );
  501. }
  502. }
  503. OnModify();
  504. m_fieldsGrid->ForceRefresh();
  505. }
  506. void DIALOG_SYMBOL_PROPERTIES::OnCancelButtonClick( wxCommandEvent& event )
  507. {
  508. // Running the Footprint Browser gums up the works and causes the automatic cancel
  509. // stuff to no longer work. So we do it here ourselves.
  510. EndQuasiModal( wxID_CANCEL );
  511. }
  512. bool DIALOG_SYMBOL_PROPERTIES::Validate()
  513. {
  514. LIB_ID id;
  515. if( !m_fieldsGrid->CommitPendingChanges() || !m_fieldsGrid->Validate() )
  516. return false;
  517. // Check for missing field names.
  518. for( size_t i = 0; i < m_fields->size(); ++i )
  519. {
  520. SCH_FIELD& field = m_fields->at( i );
  521. if( field.IsMandatory() )
  522. continue;
  523. wxString fieldName = field.GetName( false );
  524. if( fieldName.IsEmpty() )
  525. {
  526. DisplayErrorMessage( this, _( "Fields must have a name." ) );
  527. wxCommandEvent *evt = new wxCommandEvent( SYMBOL_DELAY_FOCUS );
  528. evt->SetClientData( new VECTOR2I( i, FDC_VALUE ) );
  529. QueueEvent( evt );
  530. return false;
  531. }
  532. }
  533. return true;
  534. }
  535. bool DIALOG_SYMBOL_PROPERTIES::TransferDataFromWindow()
  536. {
  537. if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
  538. return false;
  539. if( !m_fieldsGrid->CommitPendingChanges() )
  540. return false;
  541. if( !m_pinGrid->CommitPendingChanges() )
  542. return false;
  543. SCH_COMMIT commit( GetParent() );
  544. SCH_SCREEN* currentScreen = GetParent()->GetScreen();
  545. bool replaceOnCurrentScreen;
  546. wxCHECK( currentScreen, false );
  547. // This needs to be done before the LIB_ID is changed to prevent stale library symbols in
  548. // the schematic file.
  549. replaceOnCurrentScreen = currentScreen->Remove( m_symbol );
  550. // save old cmp in undo list if not already in edit, or moving ...
  551. if( m_symbol->GetEditFlags() == 0 )
  552. commit.Modify( m_symbol, currentScreen );
  553. // Save current flags which could be modified by next change settings
  554. EDA_ITEM_FLAGS flags = m_symbol->GetFlags();
  555. //Set the part selection in multiple part per package
  556. int unit_selection = m_unitChoice->IsEnabled() ? m_unitChoice->GetSelection() + 1 : 1;
  557. m_symbol->SetUnitSelection( &GetParent()->GetCurrentSheet(), unit_selection );
  558. m_symbol->SetUnit( unit_selection );
  559. int bodyStyle_selection = m_bodyStyleChoice->IsEnabled() ? m_bodyStyleChoice->GetSelection() + 1 : 1;
  560. m_symbol->SetBodyStyle( bodyStyle_selection );
  561. switch( m_orientationCtrl->GetSelection() )
  562. {
  563. case 0: m_symbol->SetOrientation( SYM_ORIENT_0 ); break;
  564. case 1: m_symbol->SetOrientation( SYM_ORIENT_90 ); break;
  565. case 2: m_symbol->SetOrientation( SYM_ORIENT_270 ); break;
  566. case 3: m_symbol->SetOrientation( SYM_ORIENT_180 ); break;
  567. }
  568. switch( m_mirrorCtrl->GetSelection() )
  569. {
  570. case 0: break;
  571. case 1: m_symbol->SetOrientation( SYM_MIRROR_X ); break;
  572. case 2: m_symbol->SetOrientation( SYM_MIRROR_Y ); break;
  573. }
  574. m_symbol->SetShowPinNames( m_ShowPinNameButt->GetValue() );
  575. m_symbol->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
  576. // Restore m_Flag modified by SetUnit() and other change settings from the dialog
  577. m_symbol->ClearFlags();
  578. m_symbol->SetFlags( flags );
  579. // change all field positions from relative to absolute
  580. for( SCH_FIELD& field : *m_fields )
  581. {
  582. field.Offset( m_symbol->GetPosition() );
  583. field.SetText( m_symbol->Schematic()->ConvertRefsToKIIDs( field.GetText() ) );
  584. }
  585. SCH_FIELDS& fields = m_symbol->GetFields();
  586. fields.clear();
  587. for( SCH_FIELD& field : *m_fields )
  588. {
  589. const wxString& fieldName = field.GetCanonicalName();
  590. if( fieldName.IsEmpty() && field.GetText().IsEmpty() )
  591. continue;
  592. else if( fieldName.IsEmpty() )
  593. field.SetName( _( "untitled" ) );
  594. fields.push_back( field );
  595. }
  596. int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
  597. for( SCH_FIELD& field : fields )
  598. {
  599. if( !field.IsMandatory() )
  600. field.SetOrdinal( ordinal++ );
  601. }
  602. // Reference has a specific initialization, depending on the current active sheet
  603. // because for a given symbol, in a complex hierarchy, there are more than one
  604. // reference.
  605. m_symbol->SetRef( &GetParent()->GetCurrentSheet(), m_fields->GetField( FIELD_T::REFERENCE )->GetText() );
  606. // Similar for Value and Footprint, except that the GUI behavior is that they are kept
  607. // in sync between multiple instances.
  608. m_symbol->SetValueFieldText( m_fields->GetField( FIELD_T::VALUE )->GetText() );
  609. m_symbol->SetFootprintFieldText( m_fields->GetField( FIELD_T::FOOTPRINT )->GetText() );
  610. m_symbol->SetExcludedFromSim( m_cbExcludeFromSim->IsChecked() );
  611. m_symbol->SetExcludedFromBOM( m_cbExcludeFromBom->IsChecked() );
  612. m_symbol->SetExcludedFromBoard( m_cbExcludeFromBoard->IsChecked() );
  613. m_symbol->SetDNP( m_cbDNP->IsChecked() );
  614. // Update any assignments
  615. if( m_dataModel )
  616. {
  617. for( const SCH_PIN& model_pin : *m_dataModel )
  618. {
  619. // map from the edited copy back to the "real" pin in the symbol.
  620. SCH_PIN* src_pin = m_symbol->GetPin( model_pin.GetNumber() );
  621. if( src_pin )
  622. src_pin->SetAlt( model_pin.GetAlt() );
  623. }
  624. }
  625. // Keep fields other than the reference, include/exclude flags, and alternate pin assignements
  626. // in sync in multi-unit parts.
  627. m_symbol->SyncOtherUnits( GetParent()->GetCurrentSheet(), commit, nullptr );
  628. if( replaceOnCurrentScreen )
  629. currentScreen->Append( m_symbol );
  630. if( !commit.Empty() )
  631. commit.Push( _( "Edit Symbol Properties" ) );
  632. return true;
  633. }
  634. void DIALOG_SYMBOL_PROPERTIES::OnGridCellChanging( wxGridEvent& event )
  635. {
  636. wxGridCellEditor* editor = m_fieldsGrid->GetCellEditor( event.GetRow(), event.GetCol() );
  637. wxControl* control = editor->GetControl();
  638. if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
  639. {
  640. event.Veto();
  641. wxCommandEvent *evt = new wxCommandEvent( SYMBOL_DELAY_FOCUS );
  642. evt->SetClientData( new VECTOR2I( event.GetRow(), event.GetCol() ) );
  643. QueueEvent( evt );
  644. }
  645. else if( event.GetCol() == FDC_NAME )
  646. {
  647. wxString newName = event.GetString();
  648. for( int i = 0; i < m_fieldsGrid->GetNumberRows(); ++i )
  649. {
  650. if( i == event.GetRow() )
  651. continue;
  652. if( newName.CmpNoCase( m_fieldsGrid->GetCellValue( i, FDC_NAME ) ) == 0 )
  653. {
  654. DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
  655. newName ) );
  656. event.Veto();
  657. wxCommandEvent *evt = new wxCommandEvent( SYMBOL_DELAY_FOCUS );
  658. evt->SetClientData( new VECTOR2I( event.GetRow(), event.GetCol() ) );
  659. QueueEvent( evt );
  660. }
  661. }
  662. }
  663. editor->DecRef();
  664. }
  665. void DIALOG_SYMBOL_PROPERTIES::OnGridEditorShown( wxGridEvent& aEvent )
  666. {
  667. if( m_fields->at( aEvent.GetRow() ).GetId() == FIELD_T::REFERENCE
  668. && aEvent.GetCol() == FDC_VALUE )
  669. {
  670. wxCommandEvent* evt = new wxCommandEvent( SYMBOL_DELAY_SELECTION );
  671. evt->SetClientData( new VECTOR2I( aEvent.GetRow(), aEvent.GetCol() ) );
  672. QueueEvent( evt );
  673. }
  674. m_editorShown = true;
  675. }
  676. void DIALOG_SYMBOL_PROPERTIES::OnGridEditorHidden( wxGridEvent& aEvent )
  677. {
  678. m_editorShown = false;
  679. }
  680. void DIALOG_SYMBOL_PROPERTIES::OnAddField( wxCommandEvent& event )
  681. {
  682. m_fieldsGrid->OnAddRow(
  683. [&]() -> std::pair<int, int>
  684. {
  685. SCH_FIELD newField( m_symbol, FIELD_T::USER, GetUserFieldName( m_fields->size(), DO_TRANSLATE ) );
  686. newField.SetTextAngle( m_fields->GetField( FIELD_T::REFERENCE )->GetTextAngle() );
  687. newField.SetVisible( false );
  688. m_fields->push_back( newField );
  689. // notify the grid
  690. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
  691. m_fieldsGrid->ProcessTableMessage( msg );
  692. OnModify();
  693. return { m_fields->size() - 1, FDC_NAME };
  694. } );
  695. }
  696. void DIALOG_SYMBOL_PROPERTIES::OnDeleteField( wxCommandEvent& event )
  697. {
  698. m_fieldsGrid->OnDeleteRows(
  699. [&]( int row )
  700. {
  701. if( row < m_fields->GetMandatoryRowCount() )
  702. {
  703. DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
  704. m_fields->GetMandatoryRowCount() ) );
  705. return false;
  706. }
  707. return true;
  708. },
  709. [&]( int row )
  710. {
  711. m_fields->erase( m_fields->begin() + row );
  712. // notify the grid
  713. wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
  714. m_fieldsGrid->ProcessTableMessage( msg );
  715. } );
  716. OnModify();
  717. }
  718. void DIALOG_SYMBOL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
  719. {
  720. m_fieldsGrid->OnMoveRowUp(
  721. [&]( int row )
  722. {
  723. return row > m_fields->GetMandatoryRowCount();
  724. },
  725. [&]( int row )
  726. {
  727. std::swap( *( m_fields->begin() + row ), *( m_fields->begin() + row - 1 ) );
  728. m_fieldsGrid->ForceRefresh();
  729. OnModify();
  730. } );
  731. }
  732. void DIALOG_SYMBOL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
  733. {
  734. m_fieldsGrid->OnMoveRowDown(
  735. [&]( int row )
  736. {
  737. return row >= m_fields->GetMandatoryRowCount();
  738. },
  739. [&]( int row )
  740. {
  741. std::swap( *( m_fields->begin() + row ), *( m_fields->begin() + row + 1 ) );
  742. m_fieldsGrid->ForceRefresh();
  743. OnModify();
  744. } );
  745. }
  746. void DIALOG_SYMBOL_PROPERTIES::OnEditSymbol( wxCommandEvent& )
  747. {
  748. if( TransferDataFromWindow() )
  749. EndQuasiModal( SYMBOL_PROPS_EDIT_SCHEMATIC_SYMBOL );
  750. }
  751. void DIALOG_SYMBOL_PROPERTIES::OnEditLibrarySymbol( wxCommandEvent& )
  752. {
  753. if( TransferDataFromWindow() )
  754. EndQuasiModal( SYMBOL_PROPS_EDIT_LIBRARY_SYMBOL );
  755. }
  756. void DIALOG_SYMBOL_PROPERTIES::OnUpdateSymbol( wxCommandEvent& )
  757. {
  758. if( TransferDataFromWindow() )
  759. EndQuasiModal( SYMBOL_PROPS_WANT_UPDATE_SYMBOL );
  760. }
  761. void DIALOG_SYMBOL_PROPERTIES::OnExchangeSymbol( wxCommandEvent& )
  762. {
  763. if( TransferDataFromWindow() )
  764. EndQuasiModal( SYMBOL_PROPS_WANT_EXCHANGE_SYMBOL );
  765. }
  766. void DIALOG_SYMBOL_PROPERTIES::OnPinTableCellEdited( wxGridEvent& aEvent )
  767. {
  768. int row = aEvent.GetRow();
  769. if( m_pinGrid->GetCellValue( row, COL_ALT_NAME ) == m_dataModel->GetValue( row, COL_BASE_NAME ) )
  770. m_dataModel->SetValue( row, COL_ALT_NAME, wxEmptyString );
  771. // These are just to get the cells refreshed
  772. m_dataModel->SetValue( row, COL_TYPE, m_dataModel->GetValue( row, COL_TYPE ) );
  773. m_dataModel->SetValue( row, COL_SHAPE, m_dataModel->GetValue( row, COL_SHAPE ) );
  774. OnModify();
  775. }
  776. void DIALOG_SYMBOL_PROPERTIES::OnPinTableColSort( wxGridEvent& aEvent )
  777. {
  778. int sortCol = aEvent.GetCol();
  779. bool ascending;
  780. // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
  781. // event, and if we ask it will give us pre-event info.
  782. if( m_pinGrid->IsSortingBy( sortCol ) )
  783. // same column; invert ascending
  784. ascending = !m_pinGrid->IsSortOrderAscending();
  785. else
  786. // different column; start with ascending
  787. ascending = true;
  788. m_dataModel->SortRows( sortCol, ascending );
  789. m_dataModel->BuildAttrs();
  790. }
  791. void DIALOG_SYMBOL_PROPERTIES::AdjustPinsGridColumns()
  792. {
  793. wxGridUpdateLocker deferRepaintsTillLeavingScope( m_pinGrid );
  794. // Account for scroll bars
  795. int pinTblWidth = KIPLATFORM::UI::GetUnobscuredSize( m_pinGrid ).x;
  796. // Stretch the Base Name and Alternate Assignment columns to fit.
  797. for( int i = 0; i < COL_COUNT; ++i )
  798. {
  799. if( i != COL_BASE_NAME && i != COL_ALT_NAME )
  800. pinTblWidth -= m_pinGrid->GetColSize( i );
  801. }
  802. if( pinTblWidth > 2 )
  803. {
  804. m_pinGrid->SetColSize( COL_BASE_NAME, pinTblWidth / 2 );
  805. m_pinGrid->SetColSize( COL_ALT_NAME, pinTblWidth / 2 );
  806. }
  807. }
  808. void DIALOG_SYMBOL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
  809. {
  810. std::bitset<64> shownColumns = m_fieldsGrid->GetShownColumns();
  811. if( shownColumns != m_shownColumns )
  812. {
  813. m_shownColumns = shownColumns;
  814. if( !m_fieldsGrid->IsCellEditControlShown() )
  815. m_fieldsGrid->SetGridWidthsDirty();
  816. }
  817. }
  818. void DIALOG_SYMBOL_PROPERTIES::HandleDelayedFocus( wxCommandEvent& event )
  819. {
  820. VECTOR2I *loc = static_cast<VECTOR2I*>( event.GetClientData() );
  821. wxCHECK_RET( loc, wxT( "Missing focus cell location" ) );
  822. // Handle a delayed focus
  823. m_fieldsGrid->SetFocus();
  824. m_fieldsGrid->MakeCellVisible( loc->x, loc->y );
  825. m_fieldsGrid->SetGridCursor( loc->x, loc->y );
  826. m_fieldsGrid->EnableCellEditControl( true );
  827. m_fieldsGrid->ShowCellEditControl();
  828. delete loc;
  829. }
  830. void DIALOG_SYMBOL_PROPERTIES::HandleDelayedSelection( wxCommandEvent& event )
  831. {
  832. VECTOR2I *loc = static_cast<VECTOR2I*>( event.GetClientData() );
  833. wxCHECK_RET( loc, wxT( "Missing focus cell location" ) );
  834. // Handle a delayed selection
  835. wxGridCellEditor* cellEditor = m_fieldsGrid->GetCellEditor( loc->x, loc->y );
  836. if( wxTextEntry* txt = dynamic_cast<wxTextEntry*>( cellEditor->GetControl() ) )
  837. KIUI::SelectReferenceNumber( txt );
  838. cellEditor->DecRef(); // we're done; must release
  839. }
  840. void DIALOG_SYMBOL_PROPERTIES::OnSizePinsGrid( wxSizeEvent& event )
  841. {
  842. wxSize new_size = event.GetSize();
  843. if( ( !m_editorShown || m_lastRequestedPinsSize != new_size ) && m_pinsSize != new_size )
  844. {
  845. m_pinsSize = new_size;
  846. AdjustPinsGridColumns();
  847. }
  848. // We store this value to check whether the dialog is changing size. This might indicate
  849. // that the user is scaling the dialog with a grid-cell-editor shown. Some editors do not
  850. // close (at least on GTK) when the user drags a dialog corner
  851. m_lastRequestedPinsSize = new_size;
  852. // Always propagate for a grid repaint (needed if the height changes, as well as width)
  853. event.Skip();
  854. }
  855. void DIALOG_SYMBOL_PROPERTIES::OnInitDlg( wxInitDialogEvent& event )
  856. {
  857. TransferDataToWindow();
  858. // Now all widgets have the size fixed, call FinishDialogSettings
  859. finishDialogSettings();
  860. EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
  861. if( cfg && cfg->m_Appearance.edit_symbol_width > 0 && cfg->m_Appearance.edit_symbol_height > 0 )
  862. SetSize( cfg->m_Appearance.edit_symbol_width, cfg->m_Appearance.edit_symbol_height );
  863. }
  864. void DIALOG_SYMBOL_PROPERTIES::OnCheckBox( wxCommandEvent& event )
  865. {
  866. OnModify();
  867. }
  868. void DIALOG_SYMBOL_PROPERTIES::OnUnitChoice( wxCommandEvent& event )
  869. {
  870. if( m_dataModel )
  871. {
  872. EDA_ITEM_FLAGS flags = m_symbol->GetFlags();
  873. int unit_selection = m_unitChoice->GetSelection() + 1;
  874. // We need to select a new unit to build the new unit pin list
  875. // but we should not change the symbol, so the initial unit will be selected
  876. // after rebuilding the pin list
  877. int old_unit = m_symbol->GetUnit();
  878. m_symbol->SetUnit( unit_selection );
  879. // Rebuild a copy of the pins of the new unit for editing
  880. m_dataModel->clear();
  881. for( const std::unique_ptr<SCH_PIN>& pin : m_symbol->GetRawPins() )
  882. m_dataModel->push_back( *pin );
  883. m_dataModel->SortRows( COL_NUMBER, true );
  884. m_dataModel->BuildAttrs();
  885. m_symbol->SetUnit( old_unit );
  886. // Restore m_Flag modified by SetUnit()
  887. m_symbol->ClearFlags();
  888. m_symbol->SetFlags( flags );
  889. }
  890. OnModify();
  891. }
  892. void DIALOG_SYMBOL_PROPERTIES::onUpdateEditSymbol( wxUpdateUIEvent& event )
  893. {
  894. event.Enable( m_symbol && m_symbol->GetLibSymbolRef() );
  895. }
  896. void DIALOG_SYMBOL_PROPERTIES::onUpdateEditLibrarySymbol( wxUpdateUIEvent& event )
  897. {
  898. event.Enable( m_symbol && m_symbol->GetLibSymbolRef() );
  899. }
  900. void DIALOG_SYMBOL_PROPERTIES::OnPageChanging( wxBookCtrlEvent& aEvent )
  901. {
  902. if( !m_fieldsGrid->CommitPendingChanges() )
  903. aEvent.Veto();
  904. if( !m_pinGrid->CommitPendingChanges() )
  905. aEvent.Veto();
  906. }