KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_lib_symbol_properties.cpp
Go to the documentation of this file.
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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <pgm_base.h>
23#include <bitmaps.h>
24#include <confirm.h>
26#include <kiway.h>
27#include <symbol_edit_frame.h>
29#include <math/util.h> // for KiROUND
30#include <sch_symbol.h>
31#include <kiplatform/ui.h>
33#include <widgets/wx_grid.h>
35#include <string_utils.h>
36#include <template_fieldnames.h>
37#include <project_sch.h>
38#include <refdes_utils.h>
39#include <dialog_sim_model.h>
40#include <tools/sch_actions.h>
46
47#include <vector>
48
49#include <wx/clipbrd.h>
50#include <wx/msgdlg.h>
51
52#include "pin_numbers.h"
53
54
58
59
62 m_Parent( aParent ),
63 m_libEntry( aLibEntry ),
65 m_delayedFocusCtrl( nullptr ),
66 m_delayedFocusGrid( nullptr ),
71{
72 std::vector<const EMBEDDED_FILES*> inheritedEmbeddedFiles;
73
74 if( std::shared_ptr<LIB_SYMBOL> parent = m_libEntry->GetParent().lock() )
75 {
76 while( parent )
77 {
78 inheritedEmbeddedFiles.push_back( parent->GetEmbeddedFiles() );
79 parent = parent->GetParent().lock();
80 }
81 }
82
83 m_embeddedFiles = new PANEL_EMBEDDED_FILES( m_NoteBook, m_libEntry, 0, std::move( inheritedEmbeddedFiles ) );
84 m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
85
86 m_pinMapPanel = new PANEL_SYMBOL_PIN_MAP( m_pinMapPage );
87 bPinMapPageSizer->Add( m_pinMapPanel, 1, wxEXPAND, 5 );
88
89 m_fields = new FIELDS_GRID_TABLE( this, aParent, m_grid, m_libEntry, { m_embeddedFiles->GetLocalFiles() } );
90 m_grid->SetTable( m_fields );
91 m_grid->OverrideMinSize( 1.0, 1.0 );
92 m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, { m_embeddedFiles->GetLocalFiles() },
93 [&]( wxCommandEvent& aEvent )
94 {
95 OnAddField( aEvent );
96 } ) );
97 m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
98
99 // Load the FIELDS_GRID_TABLE -- ensure we are calling the overloaded push_back method
100 std::vector<SCH_FIELD> fields;
101 m_libEntry->CopyFields( fields );
102
103 for( const SCH_FIELD& f : fields )
104 m_fields->push_back( f );
105
106 if( std::shared_ptr<LIB_SYMBOL> parent = m_libEntry->GetParent().lock() )
107 addInheritedFields( parent );
108
109 int minWidth = wxSystemSettings::GetMetric( wxSYS_VSCROLL_X );
110
111 for( int ii = 0; ii <= 7; ++ii )
112 {
113 if( m_grid->IsColShown( ii ) )
114 minWidth += m_grid->GetColSize( ii );
115 }
116
117 m_grid->SetMinSize( wxSize( minWidth, -1 ) );
118
119 // Putting too many columns in wxFormBuilder results in the minimum dialog size getting set too
120 // large (even with the m_grid->SetMinSize() call above).
121 m_grid->SetColSize( 13, 48 ); // "Color"
122 m_grid->SetColSize( 14, 136 ); // "Allow Autoplace"
123 m_grid->SetColSize( 15, 62 ); // "Private"
124 m_grid->SetupColumnAutosizer( 1 );
125
126 m_grid->ShowHideColumns( "0 1 2 3 4 5 6 7" );
127
128 m_SymbolNameCtrl->SetValidator( FIELD_VALIDATOR( FIELD_T::VALUE ) );
129
130 m_unitNamesGrid->PushEventHandler( new GRID_TRICKS( m_unitNamesGrid ) );
131 m_unitNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
132 m_unitNamesGrid->SetupColumnAutosizer( 1 );
133
134 m_bodyStyleNamesGrid->PushEventHandler( new GRID_TRICKS( m_bodyStyleNamesGrid,
135 [this]( wxCommandEvent& aEvent )
136 {
137 OnAddBodyStyle( aEvent );
138 } ) );
139 m_bodyStyleNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
140 m_bodyStyleNamesGrid->SetupColumnAutosizer( 0 );
141
142 m_jumperGroupsGrid->SetupColumnAutosizer( 0 );
143 m_jumperGroupsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
144
145 m_jumperGroupsGrid->PushEventHandler( new GRID_TRICKS( m_jumperGroupsGrid,
146 [this]( wxCommandEvent& aEvent )
147 {
148 OnAddJumperGroup( aEvent );
149 } ) );
150
151 // Configure button logos
152 m_bpAdd->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
153 m_bpMoveUp->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
154 m_bpMoveDown->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
155 m_bpDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
156
157 m_bpAddBodyStyle->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
158 m_bpMoveUpBodyStyle->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
159 m_bpMoveDownBodyStyle->SetBitmap( KiBitmapBundle( BITMAPS::small_down ) );
160 m_bpDeleteBodyStyle->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
161
162 m_addFilterButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
163 m_editFilterButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
164 m_deleteFilterButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
165
166 m_bpAddJumperGroup->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
167 m_bpRemoveJumperGroup->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
168
169 SetupStandardButtons();
170
171 if( aParent->IsSymbolFromLegacyLibrary() && !aParent->IsSymbolFromSchematic() )
172 {
173 m_stdSizerButtonCancel->SetDefault();
174 m_stdSizerButtonOK->SetLabel( _( "Read Only" ) );
175 m_stdSizerButtonOK->Enable( false );
176 }
177
178 // wxFormBuilder doesn't include this event...
179 m_grid->Bind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
180 m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
181 m_grid->GetGridWindow()->Bind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
182
183
184 // Forward the delete button to the tricks
185 m_deleteFilterButton->Bind( wxEVT_BUTTON,
186 [&]( wxCommandEvent& aEvent )
187 {
188 wxCommandEvent cmdEvent( EDA_EVT_LISTBOX_DELETE );
189 m_fpFilterTricks->ProcessEvent( cmdEvent );
190 } );
191
192 // When the filter tricks modifies something, update ourselves
193 m_FootprintFilterListBox->Bind( EDA_EVT_LISTBOX_CHANGED,
194 [&]( wxCommandEvent& aEvent )
195 {
196 OnModify();
197 } );
198
200 {
201 if( ( m_lastLayout == DIALOG_LIB_SYMBOL_PROPERTIES::LAST_LAYOUT::ALIAS && aLibEntry->IsRoot() )
202 || ( m_lastLayout == DIALOG_LIB_SYMBOL_PROPERTIES::LAST_LAYOUT::PARENT && aLibEntry->IsDerived() ) )
203 {
204 resetSize();
205 }
206 }
207
208 m_lastLayout = ( aLibEntry->IsDerived() ) ? DIALOG_LIB_SYMBOL_PROPERTIES::LAST_LAYOUT::ALIAS
210
211 m_grid->GetParent()->Layout();
212 syncControlStates( m_libEntry->IsDerived() );
213 Layout();
214
215 finishDialogSettings();
216}
217
218
220{
221 // TransferDataToWindow restores the remembered page, so defer the switch to a flag it honours.
222 m_forcePinMapPage = true;
223
224 for( size_t page = 0; page < m_NoteBook->GetPageCount(); ++page )
225 {
226 if( m_NoteBook->GetPage( page ) == m_pinMapPage )
227 {
228 m_NoteBook->SetSelection( page );
229 return;
230 }
231 }
232}
233
234
236{
237 m_lastOpenedPage = m_NoteBook->GetSelection( );
238
239 // Prevents crash bug in wxGrid's d'tor
240 m_grid->DestroyTable( m_fields );
241
242 m_grid->Unbind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
243 m_grid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
244 m_grid->GetGridWindow()->Unbind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
245
246 // Delete the GRID_TRICKS.
247 m_grid->PopEventHandler( true );
248 m_unitNamesGrid->PopEventHandler( true );
249 m_bodyStyleNamesGrid->PopEventHandler( true );
250 m_jumperGroupsGrid->PopEventHandler( true );
251}
252
253
254void DIALOG_LIB_SYMBOL_PROPERTIES::addInheritedFields( const std::shared_ptr<LIB_SYMBOL>& aParent )
255{
256 if( std::shared_ptr<LIB_SYMBOL> ancestor = aParent->GetParent().lock() )
257 addInheritedFields( ancestor );
258
259 std::vector<SCH_FIELD*> parentFields;
260 aParent->GetFields( parentFields );
261
262 for( SCH_FIELD* parentField : parentFields )
263 {
264 bool found = false;
265
266 if( parentField->IsMandatory() )
267 continue; // Don't inherit mandatory fields
268
269 for( size_t ii = 0; ii < m_fields->size(); ++ii )
270 {
271 SCH_FIELD& field = m_fields->at( ii );
272
273 if( field.IsMandatory() )
274 continue; // Don't inherit mandatory fields
275
276 if( field.GetUntranslatedName() == parentField->GetUntranslatedName() )
277 {
278 m_fields->SetFieldInherited( ii, *parentField );
279 found = true;
280 break;
281 }
282 }
283
284 if( !found )
285 m_fields->AddInheritedField( *parentField );
286 }
287}
288
289
291{
292 if( !wxDialog::TransferDataToWindow() )
293 return false;
294
295 std::set<wxString> defined;
296
297 for( SCH_FIELD& field : *m_fields )
298 defined.insert( field.GetName() );
299
300 // Add in any global template field names not yet defined.
301 for( const TEMPLATE_FIELDNAME& templateFieldname :
302 Pgm().GetCommonSettings()->m_FieldNameTemplates.GetTemplateFieldNames( TEMPLATES::SCOPE::GLOBAL ) )
303 {
304 if( defined.count( templateFieldname.m_Name ) <= 0 )
305 {
306 SCH_FIELD field( m_libEntry, FIELD_T::USER, templateFieldname.m_Name );
307 field.SetVisible( templateFieldname.m_Visible );
308 m_fields->push_back( field );
309 m_addedTemplateFields.insert( templateFieldname.m_Name );
310 }
311 }
312
313 // The Y axis for components in library file is from bottom to top while the screen axis is top
314 // to bottom.However it is nowhandled by the lib file parser/writer.
315
316 // notify the grid
317 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
318 m_grid->ProcessTableMessage( msg );
319
320 m_SymbolNameCtrl->ChangeValue( UnescapeString( m_libEntry->GetName() ) );
321
322 m_KeywordCtrl->ChangeValue( m_libEntry->GetKeyWords() );
323 m_unitSpinCtrl->SetValue( m_libEntry->GetUnitCount() );
324 m_OptionPartsInterchangeable->SetValue( !m_libEntry->UnitsLocked() || m_libEntry->GetUnitCount() == 1 );
325
327
328 // The map accessor is raw storage, so a derived symbol has to be shown its root's names
329 std::shared_ptr<LIB_SYMBOL> root = m_libEntry->GetRootSymbol();
330 const std::map<int, wxString>& unitNames = root->GetUnitDisplayNames();
331
332 for( int unit = 0; unit < m_libEntry->GetUnitCount(); unit++ )
333 {
334 if( unitNames.contains( unit + 1 ) )
335 m_unitNamesGrid->SetCellValue( unit, 1, unitNames.at( unit + 1 ) );
336 }
337
338 if( m_libEntry->HasDeMorganBodyStyles() )
339 {
340 m_radioDeMorgan->SetValue( true );
341 }
342 else if( m_libEntry->IsMultiBodyStyle() )
343 {
344 m_radioCustom->SetValue( true );
345
346 for( const wxString& name : m_libEntry->GetBodyStyleNames() )
347 {
348 int row = m_bodyStyleNamesGrid->GetNumberRows();
349 m_bodyStyleNamesGrid->AppendRows( 1 );
350 m_bodyStyleNamesGrid->SetCellValue( row, 0, name );
351 }
352 }
353 else
354 {
355 m_radioSingle->SetValue( true );
356 }
357
359
360 m_OptionPower->SetValue( m_libEntry->IsPower() );
361 m_OptionLocalPower->SetValue( m_libEntry->IsLocalPower() );
362
363 if( m_libEntry->IsPower() )
364 {
365 m_spiceFieldsButton->Hide();
366 m_OptionLocalPower->Enable();
367 }
368 else
369 {
370 m_OptionLocalPower->Enable( false );
371 }
372
373 m_excludeFromSimCheckBox->SetValue( m_libEntry->GetExcludedFromSim() );
374 m_excludeFromBomCheckBox->SetValue( m_libEntry->GetExcludedFromBOM() );
375 m_excludeFromBoardCheckBox->SetValue( m_libEntry->GetExcludedFromBoard() );
376 m_excludeFromPosFilesCheckBox->SetValue( m_libEntry->GetExcludedFromPosFiles() );
377
378 m_ShowPinNumButt->SetValue( m_libEntry->GetShowPinNumbers() );
379 m_ShowPinNameButt->SetValue( m_libEntry->GetShowPinNames() );
380 m_PinsNameInsideButt->SetValue( m_libEntry->GetPinNameOffset() != 0 );
381 m_pinNameOffset.ChangeValue( m_libEntry->GetPinNameOffset() );
382
383 wxArrayString tmp = m_libEntry->GetFPFilters();
384 m_FootprintFilterListBox->Append( tmp );
385
386 m_cbDuplicatePinsAreJumpers->SetValue( m_libEntry->GetDuplicatePinNumbersAreJumpers() );
387
388 std::set<wxString> availablePins;
389
390 for( const SCH_PIN* pin : m_libEntry->GetGraphicalPins( 0, 0 ) )
391 availablePins.insert( pin->GetNumber() );
392
393 for( const std::set<wxString>& group : m_libEntry->JumperPinGroups() )
394 {
395 wxString groupTxt;
396
397 for( const wxString& pinNumber : group )
398 {
399 if( !groupTxt.IsEmpty() )
400 groupTxt << ", ";
401
402 groupTxt << pinNumber;
403 }
404
405 m_jumperGroupsGrid->AppendRows( 1 );
406 m_jumperGroupsGrid->SetCellValue( m_jumperGroupsGrid->GetNumberRows() - 1, 0, groupTxt );
407 }
408
409 // Populate the list of root parts for inherited objects.
410 if( m_libEntry->IsDerived() )
411 {
412 wxArrayString symbolNames;
413 wxString libName = m_libEntry->GetLibNickname();
414
415 if( !libName.empty() )
416 {
417 m_Parent->GetLibManager().GetSymbolNames( libName, symbolNames );
418
419 // Sort the list of symbols for easier search
420 symbolNames.Sort( []( const wxString& a, const wxString& b ) -> int
421 {
422 return StrNumCmp( a, b, true );
423 } );
424
425 // Don't allow a symbol to be derived from itself
426 if( symbolNames.Index( m_libEntry->GetName() ) != wxNOT_FOUND )
427 symbolNames.Remove( m_libEntry->GetName() );
428
429 // Don't allow a symbol to be derived from any of its descendants (would create
430 // circular inheritance)
431 wxArrayString descendants;
432 m_Parent->GetLibManager().GetDerivedSymbolNames( m_libEntry->GetName(), libName, descendants );
433
434 for( const wxString& descendant : descendants )
435 {
436 if( symbolNames.Index( descendant ) != wxNOT_FOUND )
437 symbolNames.Remove( descendant );
438 }
439
440 m_inheritanceSelectCombo->Append( symbolNames );
441
442 if( std::shared_ptr<LIB_SYMBOL> rootSymbol = m_libEntry->GetParent().lock() )
443 {
444 wxString parentName = UnescapeString( rootSymbol->GetName() );
445 int selection = m_inheritanceSelectCombo->FindString( parentName );
446
447 if( selection != wxNOT_FOUND )
448 m_inheritanceSelectCombo->SetSelection( selection );
449 }
450 }
451
453 }
454
455 m_NoteBook->SetSelection( (unsigned) m_lastOpenedPage );
456
457 m_embeddedFiles->TransferDataToWindow();
458
459 m_grid->SetMinVisibleRows( this, 4 );
460
461 m_pinMapPanel->SetSymbol( m_libEntry );
462 m_pinMapPanel->TransferDataToWindow();
463
464 // Always open on the General page, unless the caller explicitly asked for the Pin Map
465 // tab (the Edit Pin Map button). This overrides DIALOG_SHIM's remembered-tab restore.
466 int targetPage = 0;
467
469 {
470 for( size_t page = 0; page < m_NoteBook->GetPageCount(); ++page )
471 {
472 if( m_NoteBook->GetPage( page ) == m_pinMapPage )
473 {
474 targetPage = (int) page;
475 break;
476 }
477 }
478 }
479
480 m_NoteBook->ChangeSelection( targetPage );
481
482 return true;
483}
484
485
487{
488 if( !m_grid->CommitPendingChanges() )
489 return false;
490
491 // Symbol reference can be empty because it inherits from the parent symbol.
492 if( m_libEntry->IsRoot() )
493 {
494 SCH_FIELD* field = m_fields->GetField( FIELD_T::REFERENCE );
495
496 if( UTIL::GetRefDesPrefix( field->GetText() ).IsEmpty() )
497 {
498 if( m_NoteBook->GetSelection() != 0 )
499 m_NoteBook->SetSelection( 0 );
500
501 m_delayedErrorMessage = _( "References must start with a letter." );
506
507 return false;
508 }
509 }
510
511 // Check for missing field names.
512 for( int ii = 0; ii < (int) m_fields->size(); ++ii )
513 {
514 SCH_FIELD& field = m_fields->at( ii );
515
516 if( field.IsMandatory() )
517 continue;
518
519 wxString fieldName = field.GetName( false );
520
521 if( fieldName.IsEmpty() && !field.GetText().IsEmpty() )
522 {
523 if( m_NoteBook->GetSelection() != 0 )
524 m_NoteBook->SetSelection( 0 );
525
526 m_delayedErrorMessage = _( "Fields must have a name." );
531
532 return false;
533 }
534 }
535
536 // Verify that the parent name is set if the symbol is inherited
537 if( m_libEntry->IsDerived() )
538 {
539 wxString parentName = m_inheritanceSelectCombo->GetValue();
540
541 if( parentName.IsEmpty() )
542 {
543 m_delayedErrorMessage = _( "Derived symbol must have a parent selected" );
544 return false;
545 }
546 }
547
548 if( !m_unitNamesGrid->CommitPendingChanges() )
549 return false;
550
551 std::set<wxString> seenSubRefs;
552
553 for( int row = 0; row < m_unitNamesGrid->GetNumberRows(); ++row )
554 {
555 wxString subRef = m_unitNamesGrid->GetCellValue( row, 1 );
556
557 if( subRef.IsEmpty() )
558 subRef = LIB_SYMBOL::LetterSubReference( row + 1, 'A' );
559
560 if( !seenSubRefs.insert( subRef.Upper() ).second )
561 {
562 m_delayedErrorMessage = wxString::Format( _( "The unit name '%s' is already in use." ), subRef );
565 m_delayedFocusRow = row;
567 return false;
568 }
569 }
570
571 /*
572 * Confirm destructive actions.
573 */
574
575 if( m_unitSpinCtrl->GetValue() < m_libEntry->GetUnitCount() )
576 {
577 if( !IsOK( this, _( "Delete extra units from symbol?" ) ) )
578 return false;
579 }
580
581 int bodyStyleCount = 0;
582
583 if( m_radioSingle->GetValue() )
584 {
585 bodyStyleCount = 1;
586 }
587 else if( m_radioDeMorgan->GetValue() )
588 {
589 bodyStyleCount = 2;
590 }
591 else if( m_radioCustom->GetValue() )
592 {
593 if( !m_bodyStyleNamesGrid->CommitPendingChanges() )
594 return false;
595
596 for( int ii = 0; ii < m_bodyStyleNamesGrid->GetNumberRows(); ++ii )
597 {
598 if( !m_bodyStyleNamesGrid->GetCellValue( ii, 0 ).IsEmpty() )
599 bodyStyleCount++;
600 }
601 }
602
603 if( m_radioCustom->GetValue() && bodyStyleCount < 2 )
604 {
605 m_delayedErrorMessage = _( "Custom body styles must have at least 2 entries" );
606 return false;
607 }
608
609 if( bodyStyleCount < m_libEntry->GetBodyStyleCount() )
610 {
611 if( !IsOK( this, _( "Delete extra body styles from symbol?" ) ) )
612 return false;
613 }
614
615 return true;
616}
617
618
620{
621 if( !m_grid->CommitPendingChanges()
622 || !m_unitNamesGrid->CommitPendingChanges()
623 || !m_bodyStyleNamesGrid->CommitPendingChanges()
624 || !m_jumperGroupsGrid->CommitPendingChanges()
625 || !m_embeddedFiles->TransferDataFromWindow() )
626 {
627 return false;
628 }
629
630 wxString newName = EscapeString( m_SymbolNameCtrl->GetValue(), CTX_LIBID );
631 wxString oldName = m_libEntry->GetName();
632
633 if( newName.IsEmpty() )
634 {
635 wxMessageBox( _( "Symbol must have a name." ) );
636 return false;
637 }
638
640
641 if( oldName != newName )
642 {
643 wxString libName = m_libEntry->GetLibNickname();
644
645 if( !libName.empty() && m_Parent->GetLibManager().SymbolNameInUse( newName, libName ) )
646 {
647 DisplayErrorMessage( this, wxString::Format( _( "Symbol name '%s' already in use in library '%s'." ),
648 UnescapeString( newName ),
649 libName ) );
650 return false;
651 }
652
653 opType = UNDO_REDO::LIB_RENAME;
654 }
655
656 // When invoked from the library tree (Symbol Properties context menu) we edit a buffered
657 // copy that is not the symbol loaded in the editor. The editor-coupled side effects below
658 // (undo list, view/title refresh) operate on the frame's current symbol, so only run them
659 // when we are actually editing it. The library-tree path does its own buffer/tree update.
660 bool editingCurrentSymbol = ( m_libEntry == m_Parent->GetCurSymbol() );
661
662 if( editingCurrentSymbol )
663 m_Parent->SaveCopyInUndoList( _( "Edit Symbol Properties" ), m_libEntry, opType );
664
665 // The Y axis for components in lib is from bottom to top while the screen axis is top
666 // to bottom: we must change the y coord sign when writing back to the library
667 std::vector<SCH_FIELD> fieldsToSave;
668 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
669
670 for( size_t ii = 0; ii < m_fields->size(); ++ii )
671 {
672 SCH_FIELD& field = m_fields->at( ii );
673
674 if( !field.IsMandatory() )
675 field.SetOrdinal( ordinal++, FIELD_T::USER );
676
677 wxString fieldName = field.GetUntranslatedName();
678
679 // Writing an unmodified inherited row into the derived symbol would stop it from
680 // tracking the parent field. Fields the symbol already owns (transferred user
681 // fields) are kept even when they match the parent. operator== is owner-sensitive,
682 // so compare content.
683 if( m_fields->IsInherited( ii ) && !m_libEntry->GetField( fieldName )
684 && field.HasSameContent( m_fields->ParentField( ii ) ) )
685 {
686 continue;
687 }
688
689 if( field.GetText().IsEmpty() )
690 {
691 if( fieldName.IsEmpty() || m_addedTemplateFields.contains( fieldName ) )
692 continue; // Skip empty fields that are not mandatory or template fields
693 }
694 else if( fieldName.IsEmpty() )
695 {
696 field.SetName( _( "untitled" ) ); // Set a default name for unnamed fields
697 }
698
699 fieldsToSave.push_back( field );
700 }
701
702 m_libEntry->SetFields( fieldsToSave );
703
704 // Update the parent for inherited symbols
705 if( m_libEntry->IsDerived() && !m_libEntry->GetLibNickname().empty() )
706 {
707 wxString parentName = EscapeString( m_inheritanceSelectCombo->GetValue(), CTX_LIBID );
708 wxString libName = m_libEntry->GetLibNickname();
709
710 // Get the parent from the libManager based on the name set in the inheritance combo box.
711 LIB_SYMBOL* newParent = m_Parent->GetLibManager().GetSymbol( parentName, libName );
712
713 // Verify that the requested parent exists
714 wxCHECK( newParent, false );
715
716 m_libEntry->SetParent( newParent );
717 }
718
719 m_libEntry->SetName( newName );
720 m_libEntry->SetKeyWords( m_KeywordCtrl->GetValue() );
721
722 // A derived symbol owns no drawings, so none of this is its to change and none of it is
723 // written out on save
724 if( !m_libEntry->IsDerived() )
725 {
726 m_libEntry->SetUnitCount( m_unitSpinCtrl->GetValue(), true );
727 m_libEntry->LockUnits( m_libEntry->GetUnitCount() > 1 && !m_OptionPartsInterchangeable->GetValue() );
728
729 m_libEntry->GetUnitDisplayNames().clear();
730
731 for( int row = 0; row < m_unitNamesGrid->GetNumberRows(); row++ )
732 {
733 if( !m_unitNamesGrid->GetCellValue( row, 1 ).IsEmpty() )
734 m_libEntry->GetUnitDisplayNames()[row+1] = m_unitNamesGrid->GetCellValue( row, 1 );
735 }
736
737 // SetBodyStyleCount() adds and deletes draw items relative to the current count, so it
738 // has to run before the flag and the names it derives from are overwritten
739 if( m_radioSingle->GetValue() )
740 {
741 m_libEntry->SetBodyStyleCount( 1, false, false );
742 m_libEntry->SetHasDeMorganBodyStyles( false );
743 m_libEntry->SetBodyStyleNames( {} );
744 }
745 else if( m_radioDeMorgan->GetValue() )
746 {
747 m_libEntry->SetBodyStyleCount( 2, false, true );
748 m_libEntry->SetHasDeMorganBodyStyles( true );
749 m_libEntry->SetBodyStyleNames( {} );
750 }
751 else
752 {
753 std::vector<wxString> bodyStyleNames;
754
755 for( int row = 0; row < m_bodyStyleNamesGrid->GetNumberRows(); ++row )
756 {
757 if( !m_bodyStyleNamesGrid->GetCellValue( row, 0 ).IsEmpty() )
758 bodyStyleNames.push_back( m_bodyStyleNamesGrid->GetCellValue( row, 0 ) );
759 }
760
761 m_libEntry->SetBodyStyleCount( bodyStyleNames.size(), true, true );
762 m_libEntry->SetHasDeMorganBodyStyles( false );
763 m_libEntry->SetBodyStyleNames( bodyStyleNames );
764 }
765 }
766
767 if( m_OptionPower->GetValue() )
768 {
769 if( m_OptionLocalPower->GetValue() )
770 m_libEntry->SetLocalPower();
771 else
772 m_libEntry->SetGlobalPower();
773
774 // Power symbols must have value matching name for now
775 m_libEntry->GetValueField().SetText( newName );
776 }
777 else
778 {
779 m_libEntry->SetNormal();
780 }
781
782 m_libEntry->SetExcludedFromSim( m_excludeFromSimCheckBox->GetValue() );
783 m_libEntry->SetExcludedFromBOM( m_excludeFromBomCheckBox->GetValue() );
784 m_libEntry->SetExcludedFromBoard( m_excludeFromBoardCheckBox->GetValue() );
785 m_libEntry->SetExcludedFromPosFiles( m_excludeFromPosFilesCheckBox->GetValue() );
786
787 m_libEntry->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
788 m_libEntry->SetShowPinNames( m_ShowPinNameButt->GetValue() );
789
790 if( m_PinsNameInsideButt->GetValue() )
791 {
792 int offset = KiROUND( (double) m_pinNameOffset.GetValue() );
793
794 // We interpret an offset of 0 as "outside", so make sure it's non-zero
795 m_libEntry->SetPinNameOffset( offset == 0 ? 20 : offset );
796 }
797 else
798 {
799 m_libEntry->SetPinNameOffset( 0 ); // pin text outside the body (name is on the pin)
800 }
801
802 m_libEntry->SetFPFilters( m_FootprintFilterListBox->GetStrings());
803
804 m_libEntry->SetDuplicatePinNumbersAreJumpers( m_cbDuplicatePinsAreJumpers->GetValue() );
805
806 std::vector<std::set<wxString>>& jumpers = m_libEntry->JumperPinGroups();
807 jumpers.clear();
808
809 for( int ii = 0; ii < m_jumperGroupsGrid->GetNumberRows(); ++ii )
810 {
811 wxStringTokenizer tokenizer( m_jumperGroupsGrid->GetCellValue( ii, 0 ), ", \t\r\n", wxTOKEN_STRTOK );
812 std::set<wxString>& group = jumpers.emplace_back();
813
814 while( tokenizer.HasMoreTokens() )
815 {
816 wxString token = tokenizer.GetNextToken();
817
818 if( token.IsEmpty() )
819 continue;
820
821 if( !m_libEntry->HasPinNumber( token ) )
822 {
823 DisplayErrorMessage( this, wxString::Format( _( "Pin '%s' in jumper pin group %d does not exist "
824 "in this symbol." ),
825 token,
826 ii + 1 ) );
827 return false;
828 }
829
830 group.insert( token );
831 }
832 }
833
834 if( !m_pinMapPanel->CommitPendingChanges() )
835 return false;
836
837 m_pinMapPanel->ApplyToSymbol( m_libEntry );
838
839 if( editingCurrentSymbol )
840 m_Parent->UpdateAfterSymbolProperties( &oldName );
841
842 return true;
843}
844
845
846void DIALOG_LIB_SYMBOL_PROPERTIES::OnBodyStyle( wxCommandEvent& event )
847{
848 if( event.GetEventObject() == m_radioDeMorgan || event.GetEventObject() == m_radioCustom )
849 {
850 PIN_NUMBERS pinNumbersWithAlternates;
851
852 for( SCH_PIN* pin : m_libEntry->GetPins() )
853 {
854 if( !pin->GetAlternates().empty() )
855 pinNumbersWithAlternates.insert( pin->GetNumber() );
856 }
857
858 if( pinNumbersWithAlternates.size() )
859 {
861 wxString::Format( _( "(Pins %s.)" ), pinNumbersWithAlternates.GetSummary() ) );
862
863 m_radioSingle->SetValue( true );
864 return;
865 }
866 }
867
869}
870
871
873{
874 aEvent.Skip();
875
876 wxPoint pos = aEvent.GetPosition();
877 wxPoint unscolled_pos = m_grid->CalcUnscrolledPosition( pos );
878 int row = m_grid->YToRow( unscolled_pos.y );
879 int col = m_grid->XToCol( unscolled_pos.x );
880
881 if( row == wxNOT_FOUND || col == wxNOT_FOUND || !m_fields->IsInherited( row ) )
882 {
883 m_grid->SetToolTip( "" );
884 return;
885 }
886
887 m_grid->SetToolTip( wxString::Format( _( "This field is inherited from '%s'." ),
888 m_fields->ParentField( row ).GetName() ) );
889}
890
891
893{
894 wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
895 wxControl* control = editor->GetControl();
896
897 if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
898 {
899 event.Veto();
900
902 m_delayedFocusRow = event.GetRow();
903 m_delayedFocusColumn = event.GetCol();
905 }
906 else if( event.GetCol() == FDC_NAME )
907 {
908 wxString newName = event.GetString();
909
910 for( int i = 0; i < m_grid->GetNumberRows(); ++i )
911 {
912 if( i == event.GetRow() )
913 continue;
914
915 if( FieldNamesAreDuplicates( newName, m_grid->GetCellValue( i, FDC_NAME ) ) )
916 {
917 DisplayErrorMessage( this, wxString::Format( _( "The name '%s' is already in use." ), newName ) );
918 event.Veto();
919 m_delayedFocusRow = event.GetRow();
920 m_delayedFocusColumn = event.GetCol();
921 }
922 }
923 }
924
925 editor->DecRef();
926}
927
928
930{
931 m_grid->ForceRefresh();
932 OnModify();
933}
934
935
937{
938 if( m_OptionPower->IsChecked() )
939 m_grid->SetCellValue( m_fields->GetFieldRow( FIELD_T::VALUE ), FDC_VALUE, m_SymbolNameCtrl->GetValue() );
940
941 OnModify();
942}
943
944
946{
947 if( !m_delayedFocusCtrl )
948 {
949 // If the validation fails and we throw up a dialog then GTK will give us another
950 // KillFocus event and we end up in infinite recursion. So we use m_delayedFocusCtrl
951 // as a re-entrancy block and then clear it again if validation passes.
954
955 if( m_SymbolNameCtrl->GetValidator()->Validate( m_SymbolNameCtrl ) )
956 {
957 m_delayedFocusCtrl = nullptr;
959 }
960 }
961
962 event.Skip();
963}
964
965
966void DIALOG_LIB_SYMBOL_PROPERTIES::OnAddField( wxCommandEvent& event )
967{
968 m_grid->OnAddRow(
969 [&]() -> std::pair<int, int>
970 {
971 SYMBOL_EDITOR_SETTINGS* settings = m_Parent->GetSettings();
973
974 newField.SetTextSize( VECTOR2I( schIUScale.MilsToIU( settings->m_Defaults.text_size ),
975 schIUScale.MilsToIU( settings->m_Defaults.text_size ) ) );
976 newField.SetVisible( false );
977
978 m_fields->push_back( newField );
979
980 // notify the grid
981 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
982 m_grid->ProcessTableMessage( msg );
983 OnModify();
984
985 return { m_fields->size() - 1, FDC_NAME };
986 } );
987}
988
989
991{
992 m_grid->OnDeleteRows(
993 [&]( int row )
994 {
995 if( row < m_fields->GetMandatoryRowCount() )
996 {
997 DisplayErrorMessage( this, wxString::Format( _( "The first %d fields are mandatory." ),
998 m_fields->GetMandatoryRowCount() ) );
999 return false;
1000 }
1001
1002 return true;
1003 },
1004 [&]( int row )
1005 {
1006 if( !m_fields->EraseRow( row ) )
1007 return;
1008
1009 // notify the grid
1010 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
1011 m_grid->ProcessTableMessage( msg );
1012 } );
1013
1014 OnModify();
1015}
1016
1017
1018void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
1019{
1020 m_grid->OnMoveRowUp(
1021 [&]( int row )
1022 {
1023 return row > m_fields->GetMandatoryRowCount();
1024 },
1025 [&]( int row )
1026 {
1027 m_fields->SwapRows( row, row - 1 );
1028 m_grid->ForceRefresh();
1029 OnModify();
1030 } );
1031}
1032
1033
1034void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
1035{
1036 m_grid->OnMoveRowDown(
1037 [&]( int row )
1038 {
1039 return row >= m_fields->GetMandatoryRowCount();
1040 },
1041 [&]( int row )
1042 {
1043 m_fields->SwapRows( row, row + 1 );
1044 m_grid->ForceRefresh();
1045 OnModify();
1046 } );
1047}
1048
1049
1051{
1052 m_bodyStyleNamesGrid->OnAddRow(
1053 [&]() -> std::pair<int, int>
1054 {
1055 m_bodyStyleNamesGrid->AppendRows( 1 );
1056 OnModify();
1057
1058 return { m_bodyStyleNamesGrid->GetNumberRows() - 1, 0 };
1059 } );
1060}
1061
1062
1064{
1065 m_bodyStyleNamesGrid->OnDeleteRows(
1066 [&]( int row )
1067 {
1068 m_bodyStyleNamesGrid->DeleteRows( row );
1069 } );
1070
1071 OnModify();
1072}
1073
1074
1076{
1077 m_bodyStyleNamesGrid->OnMoveRowUp(
1078 [&]( int row )
1079 {
1080 m_bodyStyleNamesGrid->SwapRows( row, row - 1 );
1081 OnModify();
1082 } );
1083}
1084
1085
1087{
1088 m_bodyStyleNamesGrid->OnMoveRowDown(
1089 [&]( int row )
1090 {
1091 m_bodyStyleNamesGrid->SwapRows( row, row + 1 );
1092 OnModify();
1093 } );
1094}
1095
1096
1098{
1099 if( !m_grid->CommitPendingChanges() )
1100 return;
1101
1102 m_grid->ClearSelection();
1103
1104 std::vector<SCH_FIELD> fields;
1105
1106 for( const SCH_FIELD& field : *m_fields )
1107 fields.emplace_back( field );
1108
1109 DIALOG_SIM_MODEL dialog( this, m_parentFrame, *m_libEntry, fields );
1110
1111 if( dialog.ShowModal() != wxID_OK )
1112 return;
1113
1114 // Add in any new fields
1115 for( const SCH_FIELD& editedField : fields )
1116 {
1117 bool found = false;
1118
1119 for( SCH_FIELD& existingField : *m_fields )
1120 {
1121 if( existingField.GetName() == editedField.GetName() )
1122 {
1123 found = true;
1124 existingField.SetText( editedField.GetText() );
1125 break;
1126 }
1127 }
1128
1129 if( !found )
1130 {
1131 m_fields->emplace_back( editedField );
1132 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
1133 m_grid->ProcessTableMessage( msg );
1134 }
1135 }
1136
1137 // Remove any deleted fields
1138 for( int ii = (int) m_fields->size() - 1; ii >= 0; --ii )
1139 {
1140 SCH_FIELD& existingField = m_fields->at( ii );
1141 bool found = false;
1142
1143 for( SCH_FIELD& editedField : fields )
1144 {
1145 if( editedField.GetName() == existingField.GetName() )
1146 {
1147 found = true;
1148 break;
1149 }
1150 }
1151
1152 if( !found )
1153 {
1154 m_grid->ClearSelection();
1155 m_fields->erase( m_fields->begin() + ii );
1156
1157 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, ii, 1 );
1158 m_grid->ProcessTableMessage( msg );
1159 }
1160 }
1161
1162 OnModify();
1163 m_grid->ForceRefresh();
1164}
1165
1166
1168{
1169 int idx = m_FootprintFilterListBox->HitTest( event.GetPosition() );
1170 wxCommandEvent dummy;
1171
1172 if( idx >= 0 )
1174 else
1176}
1177
1178
1180{
1181 // Running the Footprint Browser gums up the works and causes the automatic cancel
1182 // stuff to no longer work. So we do it here ourselves.
1183 EndDialogShim( wxID_CANCEL );
1184}
1185
1186
1188{
1189 wxString filterLine;
1190 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Add Footprint Filter" ), filterLine );
1191
1192 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue().IsEmpty() )
1193 return;
1194
1195 filterLine = dlg.GetValue();
1196 filterLine.Replace( wxT( " " ), wxT( "_" ) );
1197
1198 // duplicate filters do no harm, so don't be a nanny.
1199 m_FootprintFilterListBox->Append( filterLine );
1200 m_FootprintFilterListBox->SetSelection( (int) m_FootprintFilterListBox->GetCount() - 1 );
1201
1202 OnModify();
1203}
1204
1205
1207{
1208 wxArrayInt selections;
1209 int n = m_FootprintFilterListBox->GetSelections( selections );
1210
1211 if( n > 0 )
1212 {
1213 // Just edit the first one
1214 int idx = selections[0];
1215 wxString filter = m_FootprintFilterListBox->GetString( idx );
1216
1217 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Edit Footprint Filter" ), filter );
1218
1219 if( dlg.ShowModal() == wxID_OK && !dlg.GetValue().IsEmpty() )
1220 {
1221 m_FootprintFilterListBox->SetString( (unsigned) idx, dlg.GetValue() );
1222 OnModify();
1223 }
1224 }
1225}
1226
1227
1228void DIALOG_LIB_SYMBOL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
1229{
1230 m_OptionPartsInterchangeable->Enable( !m_libEntry->IsDerived() && m_unitSpinCtrl->GetValue() > 1 );
1231 m_pinNameOffset.Enable( m_PinsNameInsideButt->GetValue() );
1232
1233 if( m_grid->IsCellEditControlShown() )
1234 {
1235 int row = m_grid->GetGridCursorRow();
1236 int col = m_grid->GetGridCursorCol();
1237
1238 if( row == m_fields->GetFieldRow( FIELD_T::VALUE ) && col == FDC_VALUE && m_OptionPower->IsChecked() )
1239 {
1240 wxGridCellEditor* editor = m_grid->GetCellEditor( row, col );
1241 m_SymbolNameCtrl->ChangeValue( editor->GetValue() );
1242 editor->DecRef();
1243 }
1244 }
1245
1246 // Handle shown columns changes
1247 std::bitset<64> shownColumns = m_grid->GetShownColumns();
1248
1249 if( shownColumns != m_shownColumns )
1250 {
1251 m_shownColumns = shownColumns;
1252
1253 if( !m_grid->IsCellEditControlShown() )
1254 m_grid->SetGridWidthsDirty();
1255 }
1256
1257 // Handle a delayed focus. The delay allows us to:
1258 // a) change focus when the error was triggered from within a killFocus handler
1259 // b) show the correct notebook page in the background before the error dialog comes up
1260 // when triggered from an OK or a notebook page change
1261
1262 if( m_delayedFocusPage >= 0 && m_NoteBook->GetSelection() != m_delayedFocusPage )
1263 {
1264 m_NoteBook->ChangeSelection( (unsigned) m_delayedFocusPage );
1265 m_delayedFocusPage = -1;
1266 }
1267
1268 if( !m_delayedErrorMessage.IsEmpty() )
1269 {
1270 // We will re-enter this routine when the error dialog is displayed, so make
1271 // sure we don't keep putting up more dialogs.
1272 wxString msg = m_delayedErrorMessage;
1273 m_delayedErrorMessage = wxEmptyString;
1274
1275 // Do not use DisplayErrorMessage(); it screws up window order on Mac
1276 DisplayError( nullptr, msg );
1277 }
1278
1279 if( m_delayedFocusCtrl )
1280 {
1281 m_delayedFocusCtrl->SetFocus();
1282
1283 if( wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_delayedFocusCtrl ) )
1284 textEntry->SelectAll();
1285
1286 m_delayedFocusCtrl = nullptr;
1287 }
1288 else if( m_delayedFocusGrid )
1289 {
1290 m_delayedFocusGrid->SetFocus();
1293
1294 m_delayedFocusGrid->EnableCellEditControl( true );
1295 m_delayedFocusGrid->ShowCellEditControl();
1296
1297 m_delayedFocusGrid = nullptr;
1298 m_delayedFocusRow = -1;
1300 }
1301}
1302
1303
1305{
1306 bSizerLowerBasicPanel->Show( !aIsAlias );
1307 m_inheritanceSelectCombo->Enable( aIsAlias );
1308 m_inheritsStaticText->Enable( aIsAlias );
1309
1310 // The drawings a derived symbol inherits carry the units and body styles with them
1311 m_unitSpinCtrl->Enable( !aIsAlias );
1312 m_unitNamesGrid->Enable( !aIsAlias );
1313
1315
1316 m_grid->ForceRefresh();
1317}
1318
1319
1321{
1322 bool editable = !m_libEntry->IsDerived();
1323 bool custom = editable && m_radioCustom->GetValue();
1324
1325 m_radioSingle->Enable( editable );
1326 m_radioDeMorgan->Enable( editable );
1327 m_radioCustom->Enable( editable );
1328
1329 m_bodyStyleNamesGrid->Enable( custom );
1330 m_bpAddBodyStyle->Enable( custom );
1331 m_bpMoveUpBodyStyle->Enable( custom );
1332 m_bpMoveDownBodyStyle->Enable( custom );
1333 m_bpDeleteBodyStyle->Enable( custom );
1334}
1335
1336
1338{
1339 if( m_OptionPower->IsChecked() )
1340 {
1341 m_excludeFromSimCheckBox->SetValue( true );
1342 m_excludeFromBomCheckBox->SetValue( true );
1343 m_excludeFromBoardCheckBox->SetValue( true );
1344 m_excludeFromPosFilesCheckBox->SetValue( true );
1345 m_excludeFromBomCheckBox->Enable( false );
1346 m_excludeFromBoardCheckBox->Enable( false );
1347 m_excludeFromSimCheckBox->Enable( false );
1348 m_excludeFromPosFilesCheckBox->Enable( false );
1349 m_spiceFieldsButton->Show( false );
1350 m_OptionLocalPower->Enable( true );
1351 }
1352 else
1353 {
1354 m_excludeFromBomCheckBox->Enable( true );
1355 m_excludeFromBoardCheckBox->Enable( true );
1356 m_excludeFromSimCheckBox->Enable( true );
1357 m_excludeFromPosFilesCheckBox->Enable( true );
1358 m_spiceFieldsButton->Show( true );
1359 m_OptionLocalPower->Enable( false );
1360 }
1361
1362 OnModify();
1363}
1364
1365
1366void DIALOG_LIB_SYMBOL_PROPERTIES::OnText( wxCommandEvent& event )
1367{
1368 OnModify();
1369}
1370
1371
1372void DIALOG_LIB_SYMBOL_PROPERTIES::OnCombobox( wxCommandEvent& event )
1373{
1374 OnModify();
1375}
1376
1377
1378void DIALOG_LIB_SYMBOL_PROPERTIES::OnCheckBox( wxCommandEvent& event )
1379{
1380 OnModify();
1381}
1382
1383
1385{
1386 m_unitNamesGrid->CommitPendingChanges( true /* aQuietMode */ );
1387
1388 int extra = m_unitNamesGrid->GetNumberRows() - m_unitSpinCtrl->GetValue();
1389 int needed = m_unitSpinCtrl->GetValue() - m_unitNamesGrid->GetNumberRows();
1390
1391 if( extra > 0 )
1392 {
1393 m_unitNamesGrid->DeleteRows( m_unitNamesGrid->GetNumberRows() - extra, extra );
1394 return true;
1395 }
1396
1397 if( needed > 0 )
1398 {
1399 m_unitNamesGrid->AppendRows( needed );
1400
1401 for( int row = m_unitNamesGrid->GetNumberRows() - needed; row < m_unitNamesGrid->GetNumberRows(); ++row )
1402 m_unitNamesGrid->SetCellValue( row, 0, LIB_SYMBOL::LetterSubReference( row + 1, 'A' ) );
1403
1404 return true;
1405 }
1406
1407 return false;
1408}
1409
1410
1412{
1413 if( updateUnitCount() )
1414 OnModify();
1415}
1416
1417
1419{
1420 // wait for kill focus to update unit count
1421}
1422
1423
1425{
1426 if( updateUnitCount() )
1427 OnModify();
1428}
1429
1430
1432{
1433 if( updateUnitCount() )
1434 OnModify();
1435}
1436
1437
1439{
1440 if( !m_grid->CommitPendingChanges() )
1441 aEvent.Veto();
1442}
1443
1444
1446{
1447 m_jumperGroupsGrid->OnAddRow(
1448 [&]() -> std::pair<int, int>
1449 {
1450 m_jumperGroupsGrid->AppendRows( 1 );
1451 OnModify();
1452
1453 return { m_jumperGroupsGrid->GetNumberRows() - 1, 0 };
1454 } );
1455}
1456
1457
1459{
1460 m_jumperGroupsGrid->OnDeleteRows(
1461 [&]( int row )
1462 {
1463 m_jumperGroupsGrid->DeleteRows( row, 1 );
1464 } );
1465
1466 OnModify();
1467}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
DIALOG_LIB_SYMBOL_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=ID_LIBEDIT_NOTEBOOK, const wxString &title=_("Library Symbol Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnSymbolNameKillFocus(wxFocusEvent &event) override
void OnUnitSpinCtrlText(wxCommandEvent &event) override
std::unique_ptr< LISTBOX_TRICKS > m_fpFilterTricks
void OnPageChanging(wxNotebookEvent &event) override
void OnBodyStyle(wxCommandEvent &event) override
DIALOG_LIB_SYMBOL_PROPERTIES(SYMBOL_EDIT_FRAME *parent, LIB_SYMBOL *aLibEntry)
void OnUpdateUI(wxUpdateUIEvent &event) override
void OnBodyStyleMoveUp(wxCommandEvent &event) override
void OnCancelButtonClick(wxCommandEvent &event) override
void OnEditFootprintFilter(wxCommandEvent &event) override
void OnDeleteField(wxCommandEvent &event) override
void OnCombobox(wxCommandEvent &event) override
void OnFpFilterDClick(wxMouseEvent &event) override
void OnUnitSpinCtrlEnter(wxCommandEvent &event) override
void OnAddBodyStyle(wxCommandEvent &event) override
void OnMoveUp(wxCommandEvent &event) override
void OnDeleteBodyStyle(wxCommandEvent &event) override
void OnAddJumperGroup(wxCommandEvent &event) override
void OnText(wxCommandEvent &event) override
void OnUnitSpinCtrlKillFocus(wxFocusEvent &event) override
void OnMoveDown(wxCommandEvent &event) override
void SelectPinMapPage()
Switch the notebook to the Pin Map page (issue #2282).
bool m_forcePinMapPage
When set by SelectPinMapPage(), TransferDataToWindow opens the Pin Map page instead of the remembered...
void OnAddFootprintFilter(wxCommandEvent &event) override
void OnCheckBox(wxCommandEvent &event) override
void OnSymbolNameText(wxCommandEvent &event) override
void OnBodyStyleMoveDown(wxCommandEvent &event) override
void OnRemoveJumperGroup(wxCommandEvent &event) override
void onPowerCheckBox(wxCommandEvent &aEvent) override
void OnAddField(wxCommandEvent &event) override
void OnEditSpiceModel(wxCommandEvent &event) override
void addInheritedFields(const std::shared_ptr< LIB_SYMBOL > &aParent)
void OnUnitSpinCtrl(wxSpinEvent &event) override
void EndDialogShim(int aReturnCode)
A mode agnostic way to close a dialog.
EDA_BASE_FRAME * m_parentFrame
int ShowModal() override
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:495
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
void push_back(const SCH_FIELD &field)
A text control validator used for validating the text allowed in fields.
Definition validators.h:138
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Define a library symbol object.
Definition lib_symbol.h:119
static wxString LetterSubReference(int aUnit, wxChar aInitialLetter)
Composed widget that edits a symbol's named pin maps and the footprint each is bound to.
container_type::size_type size() const
Definition pin_numbers.h:70
void insert(value_type const &v)
Definition pin_numbers.h:58
wxString GetSummary() const
bool IsMandatory() const
bool HasSameContent(const SCH_FIELD &aOther) const
Test if this field has the same name, content and presentation as another field, ignoring the owning ...
void SetOrdinal(int aOrdinal, FIELD_T aType)
Definition sch_field.h:148
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
void SetName(const wxString &aName)
The symbol library editor main window.
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define _(s)
@ FDC_NAME
@ FDC_VALUE
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Collection of utility functions for component reference designators (refdes)
#define NO_BODY_STYLES_WITH_ALTERNATE_PIN_FUNCTIONS
Definition sch_actions.h:36
std::vector< FAB_LAYER_COLOR > dummy
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
Hold a name of a symbol's field, field value, and default visibility.
bool FieldNamesAreDuplicates(const wxString &aLhs, const wxString &aRhs, std::initializer_list< FIELD_T > aMandatoryFields)
Test whether two field names should be treated as duplicates for the purposes of field name uniquenes...
wxString GetUserFieldName(int aFieldNdx, TRANSLATION aTranslation)
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ TRANSLATED
KIBIS_PIN * pin
UNDO_REDO
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683