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