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