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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <pgm_base.h>
27#include <eeschema_settings.h>
28#include <bitmaps.h>
29#include <confirm.h>
31#include <kiway.h>
32#include <symbol_edit_frame.h>
34#include <math/util.h> // for KiROUND
35#include <sch_symbol.h>
36#include <kiplatform/ui.h>
38#include <widgets/wx_grid.h>
40#include <string_utils.h>
41#include <project_sch.h>
42#include <refdes_utils.h>
43#include <dialog_sim_model.h>
44
49
50#include <wx/clipbrd.h>
51#include <wx/msgdlg.h>
52
53
57
58
60 LIB_SYMBOL* aLibEntry ) :
62 m_Parent( aParent ),
63 m_libEntry( aLibEntry ),
65 m_delayedFocusCtrl( nullptr ),
66 m_delayedFocusGrid( nullptr ),
71{
73 m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
74
75 m_fields = new FIELDS_GRID_TABLE( this, aParent, m_grid, m_libEntry,
76 { m_embeddedFiles->GetLocalFiles() } );
77 m_grid->SetTable( m_fields );
78 m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, { m_embeddedFiles->GetLocalFiles() },
79 [&]( wxCommandEvent& aEvent )
80 {
81 OnAddField( aEvent );
82 } ) );
83 m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
84
85 // Load the FIELDS_GRID_TABLE -- ensure we are calling the overloaded push_back method
86 std::vector<SCH_FIELD> fields;
87 m_libEntry->CopyFields( fields );
88
89 for( const SCH_FIELD& f : fields )
90 m_fields->push_back( f );
91
92 if( std::shared_ptr<LIB_SYMBOL> parent = m_libEntry->GetParent().lock() )
93 addInheritedFields( parent );
94
95 m_grid->ShowHideColumns( "0 1 2 3 4 5 6 7" );
96
98
99 m_unitNamesGrid->PushEventHandler( new GRID_TRICKS( m_unitNamesGrid ) );
100 m_unitNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
101
103 [this]( wxCommandEvent& aEvent )
104 {
105 OnAddBodyStyle( aEvent );
106 } ) );
107 m_bodyStyleNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
108
109 m_jumperGroupsGrid->SetupColumnAutosizer( 0 );
110 m_jumperGroupsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
111
112 m_jumperGroupsGrid->PushEventHandler( new GRID_TRICKS( m_jumperGroupsGrid,
113 [this]( wxCommandEvent& aEvent )
114 {
115 OnAddJumperGroup( aEvent );
116 } ) );
117
118 // Configure button logos
123
128
132
135
137
138 if( aParent->IsSymbolFromLegacyLibrary() && !aParent->IsSymbolFromSchematic() )
139 {
140 m_stdSizerButtonCancel->SetDefault();
141 m_stdSizerButtonOK->SetLabel( _( "Read Only" ) );
142 m_stdSizerButtonOK->Enable( false );
143 }
144
145 // wxFormBuilder doesn't include this event...
146 m_grid->Bind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
147 m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
148 m_grid->GetGridWindow()->Bind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
149
150
151 // Forward the delete button to the tricks
152 m_deleteFilterButton->Bind( wxEVT_BUTTON,
153 [&]( wxCommandEvent& aEvent )
154 {
155 wxCommandEvent cmdEvent( EDA_EVT_LISTBOX_DELETE );
156 m_fpFilterTricks->ProcessEvent( cmdEvent );
157 } );
158
159 // When the filter tricks modifies something, update ourselves
160 m_FootprintFilterListBox->Bind( EDA_EVT_LISTBOX_CHANGED,
161 [&]( wxCommandEvent& aEvent )
162 {
163 OnModify();
164 } );
165
167 {
170 {
171 resetSize();
172 }
173 }
174
177
178 m_grid->GetParent()->Layout();
179 syncControlStates( m_libEntry->IsDerived() );
180 Layout();
181
183}
184
185
187{
188 m_lastOpenedPage = m_NoteBook->GetSelection( );
189
190 // Prevents crash bug in wxGrid's d'tor
191 m_grid->DestroyTable( m_fields );
192
193 m_grid->Unbind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
194 m_grid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
195 m_grid->GetGridWindow()->Unbind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
196
197 // Delete the GRID_TRICKS.
198 m_grid->PopEventHandler( true );
199 m_unitNamesGrid->PopEventHandler( true );
200 m_bodyStyleNamesGrid->PopEventHandler( true );
201 m_jumperGroupsGrid->PopEventHandler( true );
202}
203
204
205void DIALOG_LIB_SYMBOL_PROPERTIES::addInheritedFields( const std::shared_ptr<LIB_SYMBOL>& aParent )
206{
207 if( std::shared_ptr<LIB_SYMBOL> ancestor = aParent->GetParent().lock() )
208 addInheritedFields( ancestor );
209
210 std::vector<SCH_FIELD*> parentFields;
211 aParent->GetFields( parentFields );
212
213 for( SCH_FIELD* parentField : parentFields )
214 {
215 bool found = false;
216
217 if( parentField->IsMandatory() )
218 continue; // Don't inherit mandatory fields
219
220 for( size_t ii = 0; ii < m_fields->size(); ++ii )
221 {
222 SCH_FIELD& field = m_fields->at( ii );
223
224 if( field.IsMandatory() )
225 continue; // Don't inherit mandatory fields
226
227 if( field.GetCanonicalName() == parentField->GetCanonicalName() )
228 {
229 m_fields->SetFieldInherited( ii, *parentField );
230 found = true;
231 break;
232 }
233 }
234
235 if( !found )
236 m_fields->AddInheritedField( *parentField );
237 }
238}
239
240
242{
243 if( !wxDialog::TransferDataToWindow() )
244 return false;
245
246 std::set<wxString> defined;
247
248 for( SCH_FIELD& field : *m_fields )
249 defined.insert( field.GetName() );
250
251 // Add in any template fieldnames not yet defined:
252 // Read global fieldname templates
254 {
255 TEMPLATES templateMgr;
256
257 if( !cfg->m_Drawing.field_names.IsEmpty() )
258 templateMgr.AddTemplateFieldNames( cfg->m_Drawing.field_names );
259
260 for( const TEMPLATE_FIELDNAME& templateFieldname : templateMgr.GetTemplateFieldNames() )
261 {
262 if( defined.count( templateFieldname.m_Name ) <= 0 )
263 {
264 SCH_FIELD field( m_libEntry, FIELD_T::USER, templateFieldname.m_Name );
265 field.SetVisible( templateFieldname.m_Visible );
266 m_fields->push_back( field );
267 m_addedTemplateFields.insert( templateFieldname.m_Name );
268 }
269 }
270 }
271
272 // The Y axis for components in library file is from bottom to top while the screen axis is top
273 // to bottom.However it is nowhandled by the lib file parser/writer.
274
275 // notify the grid
276 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
277 m_grid->ProcessTableMessage( msg );
278
279 m_SymbolNameCtrl->ChangeValue( UnescapeString( m_libEntry->GetName() ) );
280
281 m_KeywordCtrl->ChangeValue( m_libEntry->GetKeyWords() );
282 m_unitSpinCtrl->SetValue( m_libEntry->GetUnitCount() );
283 m_OptionPartsInterchangeable->SetValue( !m_libEntry->UnitsLocked() || m_libEntry->GetUnitCount() == 1 );
284
286
287 for( int unit = 0; unit < m_libEntry->GetUnitCount(); unit++ )
288 {
289 if( m_libEntry->GetUnitDisplayNames().contains( unit + 1 ) )
290 m_unitNamesGrid->SetCellValue( unit, 1, m_libEntry->GetUnitDisplayNames().at( unit + 1 ) );
291 }
292
293 if( m_libEntry->HasDeMorganBodyStyles() )
294 {
295 m_radioDeMorgan->SetValue( true );
296 }
297 else if( m_libEntry->IsMultiBodyStyle() )
298 {
299 m_radioCustom->SetValue( true );
300
301 for( const wxString& name : m_libEntry->GetBodyStyleNames() )
302 {
303 int row = m_bodyStyleNamesGrid->GetNumberRows();
304 m_bodyStyleNamesGrid->AppendRows( 1 );
305 m_bodyStyleNamesGrid->SetCellValue( row, 0, name );
306 }
307 }
308 else
309 {
310 m_radioSingle->SetValue( true );
311 }
312
313 m_OptionPower->SetValue( m_libEntry->IsPower() );
314 m_OptionLocalPower->SetValue( m_libEntry->IsLocalPower() );
315
316 if( m_libEntry->IsPower() )
317 {
318 m_spiceFieldsButton->Hide();
319 m_OptionLocalPower->Enable();
320 }
321 else
322 {
323 m_OptionLocalPower->Enable( false );
324 }
325
326 m_excludeFromSimCheckBox->SetValue( m_libEntry->GetExcludedFromSim() );
327 m_excludeFromBomCheckBox->SetValue( m_libEntry->GetExcludedFromBOM() );
328 m_excludeFromBoardCheckBox->SetValue( m_libEntry->GetExcludedFromBoard() );
329
330 m_ShowPinNumButt->SetValue( m_libEntry->GetShowPinNumbers() );
331 m_ShowPinNameButt->SetValue( m_libEntry->GetShowPinNames() );
332 m_PinsNameInsideButt->SetValue( m_libEntry->GetPinNameOffset() != 0 );
333 m_pinNameOffset.ChangeValue( m_libEntry->GetPinNameOffset() );
334
335 wxArrayString tmp = m_libEntry->GetFPFilters();
336 m_FootprintFilterListBox->Append( tmp );
337
338 m_cbDuplicatePinsAreJumpers->SetValue( m_libEntry->GetDuplicatePinNumbersAreJumpers() );
339
340 std::set<wxString> availablePins;
341
342 for( const SCH_PIN* pin : m_libEntry->GetPins() )
343 availablePins.insert( pin->GetNumber() );
344
345 for( const std::set<wxString>& group : m_libEntry->JumperPinGroups() )
346 {
347 wxString groupTxt;
348
349 for( const wxString& pinNumber : group )
350 {
351 if( !groupTxt.IsEmpty() )
352 groupTxt << ", ";
353
354 groupTxt << pinNumber;
355 }
356
357 m_jumperGroupsGrid->AppendRows( 1 );
358 m_jumperGroupsGrid->SetCellValue( m_jumperGroupsGrid->GetNumberRows() - 1, 0, groupTxt );
359 }
360
361 // Populate the list of root parts for inherited objects.
362 if( m_libEntry->IsDerived() )
363 {
364 wxArrayString symbolNames;
365 wxString libName = m_Parent->GetCurLib();
366
367 // Someone forgot to set the current library in the editor frame window.
368 wxCHECK( !libName.empty(), false );
369
370 m_Parent->GetLibManager().GetSymbolNames( libName, symbolNames );
371
372 // Sort the list of symbols for easier search
373 symbolNames.Sort(
374 []( const wxString& a, const wxString& b ) -> int
375 {
376 return StrNumCmp( a, b, true );
377 } );
378
379 // Do allow an inherited symbol to be derived from itself.
380 if( symbolNames.Index( m_libEntry->GetName() ) != wxNOT_FOUND )
381 symbolNames.Remove( m_libEntry->GetName() );
382
383 m_inheritanceSelectCombo->Append( symbolNames );
384
385 if( std::shared_ptr<LIB_SYMBOL> rootSymbol = m_libEntry->GetParent().lock() )
386 {
387 wxString parentName = UnescapeString( rootSymbol->GetName() );
388 int selection = m_inheritanceSelectCombo->FindString( parentName );
389
390 if( selection == wxNOT_FOUND )
391 return false;
392
393 m_inheritanceSelectCombo->SetSelection( selection );
394 }
395
397 }
398
399 m_NoteBook->SetSelection( (unsigned) m_lastOpenedPage );
400
401 m_embeddedFiles->TransferDataToWindow();
402
403 return true;
404}
405
406
408{
409 if( !m_grid->CommitPendingChanges() )
410 return false;
411
412 // Symbol reference can be empty because it inherits from the parent symbol.
413 if( m_libEntry->IsRoot() )
414 {
415 SCH_FIELD* field = m_fields->GetField( FIELD_T::REFERENCE );
416
417 if( UTIL::GetRefDesPrefix( field->GetText() ).IsEmpty() )
418 {
419 if( m_NoteBook->GetSelection() != 0 )
420 m_NoteBook->SetSelection( 0 );
421
422 m_delayedErrorMessage = _( "References must start with a letter." );
427
428 return false;
429 }
430 }
431
432 // Check for missing field names.
433 for( int ii = 0; ii < (int) m_fields->size(); ++ii )
434 {
435 SCH_FIELD& field = m_fields->at( ii );
436
437 if( field.IsMandatory() )
438 continue;
439
440 wxString fieldName = field.GetName( false );
441
442 if( fieldName.IsEmpty() && !field.GetText().IsEmpty() )
443 {
444 if( m_NoteBook->GetSelection() != 0 )
445 m_NoteBook->SetSelection( 0 );
446
447 m_delayedErrorMessage = _( "Fields must have a name." );
452
453 return false;
454 }
455 }
456
457 // Verify that the parent name is set if the symbol is inherited
458 if( m_libEntry->IsDerived() )
459 {
460 wxString parentName = m_inheritanceSelectCombo->GetValue();
461
462 if( parentName.IsEmpty() )
463 {
464 m_delayedErrorMessage = _( "Derived symbol must have a parent selected" );
465 return false;
466 }
467 }
468
469 /*
470 * Confirm destructive actions.
471 */
472
473 if( m_unitSpinCtrl->GetValue() < m_libEntry->GetUnitCount() )
474 {
475 if( !IsOK( this, _( "Delete extra units from symbol?" ) ) )
476 return false;
477 }
478
479 int bodyStyleCount = 0;
480
481 if( m_radioSingle->GetValue() )
482 {
483 bodyStyleCount = 1;
484 }
485 if( m_radioDeMorgan->GetValue() )
486 {
487 bodyStyleCount = 2;
488 }
489 else if( m_radioCustom->GetValue() )
490 {
491 for( int ii = 0; ii < m_bodyStyleNamesGrid->GetNumberRows(); ++ii )
492 {
493 if( !m_bodyStyleNamesGrid->GetCellValue( ii, 0 ).IsEmpty() )
494 bodyStyleCount++;
495 }
496 }
497
498 if( bodyStyleCount == 0 )
499 {
500 m_delayedErrorMessage = _( "Symbol must have at least 1 body style" );
501 return false;
502 }
503
504 if( bodyStyleCount < m_libEntry->GetBodyStyleCount() )
505 {
506 if( !IsOK( this, _( "Delete extra body styles from symbol?" ) ) )
507 return false;
508 }
509
510 return true;
511}
512
513
515{
516 if( !m_grid->CommitPendingChanges()
517 || !m_unitNamesGrid->CommitPendingChanges()
518 || !m_bodyStyleNamesGrid->CommitPendingChanges()
519 || !m_jumperGroupsGrid->CommitPendingChanges() )
520 {
521 return false;
522 }
523
524 wxString newName = EscapeString( m_SymbolNameCtrl->GetValue(), CTX_LIBID );
525 wxString oldName = m_libEntry->GetName();
526
527 if( newName.IsEmpty() )
528 {
529 wxMessageBox( _( "Symbol must have a name." ) );
530 return false;
531 }
532
534
535 if( oldName != newName )
536 {
537 wxString libName = m_Parent->GetCurLib();
538
539 if( m_Parent->GetLibManager().SymbolNameInUse( newName, libName ) )
540 {
541 wxString msg;
542
543 msg.Printf( _( "Symbol name '%s' already in use in library '%s'." ),
544 UnescapeString( newName ),
545 libName );
546 DisplayErrorMessage( this, msg );
547 return false;
548 }
549
550 opType = UNDO_REDO::LIB_RENAME;
551 }
552
553 m_Parent->SaveCopyInUndoList( _( "Edit Symbol Properties" ), m_libEntry, opType );
554
555 // The Y axis for components in lib is from bottom to top while the screen axis is top
556 // to bottom: we must change the y coord sign when writing back to the library
557 std::vector<SCH_FIELD> fieldsToSave;
558 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
559
560 for( size_t ii = 0; ii < m_fields->size(); ++ii )
561 {
562 SCH_FIELD& field = m_fields->at( ii );
563
564 if( !field.IsMandatory() )
565 field.SetOrdinal( ordinal++ );
566
567 wxString fieldName = field.GetCanonicalName();
568
569 if( m_fields->IsInherited( ii ) && field == m_fields->ParentField( ii ) )
570 continue; // Skip inherited fields
571
572 if( field.GetText().IsEmpty() )
573 {
574 if( fieldName.IsEmpty() || m_addedTemplateFields.contains( fieldName ) )
575 continue; // Skip empty fields that are not mandatory or template fields
576 }
577 else if( fieldName.IsEmpty() )
578 {
579 field.SetName( _( "untitled" ) ); // Set a default name for unnamed fields
580 }
581
582 fieldsToSave.push_back( field );
583 }
584
585 m_libEntry->SetFields( fieldsToSave );
586
587 // Update the parent for inherited symbols
588 if( m_libEntry->IsDerived() )
589 {
590 wxString parentName = EscapeString( m_inheritanceSelectCombo->GetValue(), CTX_LIBID );
591
592 // The parentName was verified to be non-empty in the Validator
593 wxString libName = m_Parent->GetCurLib();
594
595 // Get the parent from the libManager based on the name set in the inheritance combo box.
596 LIB_SYMBOL* newParent = m_Parent->GetLibManager().GetSymbol( parentName, libName );
597
598 // Verify that the requested parent exists
599 wxCHECK( newParent, false );
600
601 m_libEntry->SetParent( newParent );
602 }
603
604 m_libEntry->SetName( newName );
605 m_libEntry->SetKeyWords( m_KeywordCtrl->GetValue() );
606 m_libEntry->SetUnitCount( m_unitSpinCtrl->GetValue(), true );
607 m_libEntry->LockUnits( m_libEntry->GetUnitCount() > 1 && !m_OptionPartsInterchangeable->GetValue() );
608
609 m_libEntry->GetUnitDisplayNames().clear();
610
611 for( int row = 0; row < m_unitNamesGrid->GetNumberRows(); row++ )
612 {
613 if( !m_unitNamesGrid->GetCellValue( row, 1 ).IsEmpty() )
614 m_libEntry->GetUnitDisplayNames()[row+1] = m_unitNamesGrid->GetCellValue( row, 1 );
615 }
616
617 if( m_radioSingle->GetValue() )
618 {
619 m_libEntry->SetHasDeMorganBodyStyles( false );
620 m_libEntry->SetBodyStyleCount( 1, false, false );
621 m_libEntry->SetBodyStyleNames( {} );
622 }
623 else if( m_radioDeMorgan->GetValue() )
624 {
625 m_libEntry->SetHasDeMorganBodyStyles( true );
626 m_libEntry->SetBodyStyleCount( 2, false, true );
627 m_libEntry->SetBodyStyleNames( {} );
628 }
629 else
630 {
631 std::vector<wxString> bodyStyleNames;
632
633 for( int row = 0; row < m_bodyStyleNamesGrid->GetNumberRows(); ++row )
634 {
635 if( !m_bodyStyleNamesGrid->GetCellValue( row, 0 ).IsEmpty() )
636 bodyStyleNames.push_back( m_bodyStyleNamesGrid->GetCellValue( row, 0 ) );
637 }
638
639 m_libEntry->SetHasDeMorganBodyStyles( false );
640 m_libEntry->SetBodyStyleCount( bodyStyleNames.size(), true, true );
641 m_libEntry->SetBodyStyleNames( bodyStyleNames );
642 }
643
644 if( m_OptionPower->GetValue() )
645 {
646 if( m_OptionLocalPower->GetValue() )
647 m_libEntry->SetLocalPower();
648 else
649 m_libEntry->SetGlobalPower();
650
651 // Power symbols must have value matching name for now
652 m_libEntry->GetValueField().SetText( newName );
653 }
654 else
655 {
656 m_libEntry->SetNormal();
657 }
658
659 m_libEntry->SetExcludedFromSim( m_excludeFromSimCheckBox->GetValue() );
660 m_libEntry->SetExcludedFromBOM( m_excludeFromBomCheckBox->GetValue() );
661 m_libEntry->SetExcludedFromBoard( m_excludeFromBoardCheckBox->GetValue() );
662
663 m_libEntry->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
664 m_libEntry->SetShowPinNames( m_ShowPinNameButt->GetValue() );
665
666 if( m_PinsNameInsideButt->GetValue() )
667 {
668 int offset = KiROUND( (double) m_pinNameOffset.GetValue() );
669
670 // We interpret an offset of 0 as "outside", so make sure it's non-zero
671 m_libEntry->SetPinNameOffset( offset == 0 ? 20 : offset );
672 }
673 else
674 {
675 m_libEntry->SetPinNameOffset( 0 ); // pin text outside the body (name is on the pin)
676 }
677
678 m_libEntry->SetFPFilters( m_FootprintFilterListBox->GetStrings());
679
680 m_libEntry->SetDuplicatePinNumbersAreJumpers( m_cbDuplicatePinsAreJumpers->GetValue() );
681
682 std::vector<std::set<wxString>>& jumpers = m_libEntry->JumperPinGroups();
683 jumpers.clear();
684
685 for( int ii = 0; ii < m_jumperGroupsGrid->GetNumberRows(); ++ii )
686 {
687 wxStringTokenizer tokenizer( m_jumperGroupsGrid->GetCellValue( ii, 0 ), ", \t\r\n", wxTOKEN_STRTOK );
688 std::set<wxString>& group = jumpers.emplace_back();
689
690 while( tokenizer.HasMoreTokens() )
691 {
692 if( wxString token = tokenizer.GetNextToken(); !token.IsEmpty() )
693 group.insert( token );
694 }
695 }
696
697 m_Parent->UpdateAfterSymbolProperties( &oldName );
698
699 return true;
700}
701
702
703void DIALOG_LIB_SYMBOL_PROPERTIES::OnBodyStyle( wxCommandEvent& event )
704{
705 m_bodyStyleNamesGrid->Enable( m_radioCustom->GetValue() );
706
707 m_bpAddBodyStyle->Enable( m_radioCustom->GetValue() );
708 m_bpMoveUpBodyStyle->Enable( m_radioCustom->GetValue() );
709 m_bpMoveDownBodyStyle->Enable( m_radioCustom->GetValue() );
710 m_bpDeleteBodyStyle->Enable( m_radioCustom->GetValue() );
711}
712
713
715{
716 aEvent.Skip();
717
718 wxPoint pos = aEvent.GetPosition();
719 wxPoint unscolled_pos = m_grid->CalcUnscrolledPosition( pos );
720 int row = m_grid->YToRow( unscolled_pos.y );
721 int col = m_grid->XToCol( unscolled_pos.x );
722
723 if( row == wxNOT_FOUND || col == wxNOT_FOUND || !m_fields->IsInherited( row ) )
724 {
725 m_grid->SetToolTip( "" );
726 return;
727 }
728
729 m_grid->SetToolTip( wxString::Format( _( "This field is inherited from '%s'." ),
730 m_fields->ParentField( row ).GetName() ) );
731}
732
733
735{
736 wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
737 wxControl* control = editor->GetControl();
738
739 if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
740 {
741 event.Veto();
742
744 m_delayedFocusRow = event.GetRow();
745 m_delayedFocusColumn = event.GetCol();
747 }
748 else if( event.GetCol() == FDC_NAME )
749 {
750 wxString newName = event.GetString();
751
752 for( int i = 0; i < m_grid->GetNumberRows(); ++i )
753 {
754 if( i == event.GetRow() )
755 continue;
756
757 if( newName.CmpNoCase( m_grid->GetCellValue( i, FDC_NAME ) ) == 0 )
758 {
759 DisplayError( this, wxString::Format( _( "The name '%s' is already in use." ), newName ) );
760 event.Veto();
761 m_delayedFocusRow = event.GetRow();
762 m_delayedFocusColumn = event.GetCol();
763 }
764 }
765 }
766
767 editor->DecRef();
768}
769
770
772{
773 m_grid->ForceRefresh();
774 OnModify();
775}
776
777
779{
780 if( m_OptionPower->IsChecked() )
781 m_grid->SetCellValue( m_fields->GetFieldRow( FIELD_T::VALUE ), FDC_VALUE, m_SymbolNameCtrl->GetValue() );
782
783 OnModify();
784}
785
786
788{
789 if( !m_delayedFocusCtrl )
790 {
791 // If the validation fails and we throw up a dialog then GTK will give us another
792 // KillFocus event and we end up in infinite recursion. So we use m_delayedFocusCtrl
793 // as a re-entrancy block and then clear it again if validation passes.
796
797 if( m_SymbolNameCtrl->GetValidator()->Validate( m_SymbolNameCtrl ) )
798 {
799 m_delayedFocusCtrl = nullptr;
801 }
802 }
803
804 event.Skip();
805}
806
807
808void DIALOG_LIB_SYMBOL_PROPERTIES::OnAddField( wxCommandEvent& event )
809{
810 m_grid->OnAddRow(
811 [&]() -> std::pair<int, int>
812 {
813 SYMBOL_EDITOR_SETTINGS* settings = m_Parent->GetSettings();
815
816 newField.SetTextSize( VECTOR2I( schIUScale.MilsToIU( settings->m_Defaults.text_size ),
817 schIUScale.MilsToIU( settings->m_Defaults.text_size ) ) );
818 newField.SetVisible( false );
819
820 m_fields->push_back( newField );
821
822 // notify the grid
823 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
824 m_grid->ProcessTableMessage( msg );
825 OnModify();
826
827 return { m_fields->size() - 1, FDC_NAME };
828 } );
829}
830
831
833{
834 m_grid->OnDeleteRows(
835 [&]( int row )
836 {
837 if( row < m_fields->GetMandatoryRowCount() )
838 {
839 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
840 m_fields->GetMandatoryRowCount() ) );
841 return false;
842 }
843
844 return true;
845 },
846 [&]( int row )
847 {
848 if( !m_fields->EraseRow( row ) )
849 return;
850
851 // notify the grid
852 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
853 m_grid->ProcessTableMessage( msg );
854 } );
855
856 OnModify();
857}
858
859
860void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
861{
862 m_grid->OnMoveRowUp(
863 [&]( int row )
864 {
865 return row > m_fields->GetMandatoryRowCount();
866 },
867 [&]( int row )
868 {
869 m_fields->SwapRows( row, row - 1 );
870 m_grid->ForceRefresh();
871 OnModify();
872 } );
873}
874
875
876void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
877{
878 m_grid->OnMoveRowDown(
879 [&]( int row )
880 {
881 return row >= m_fields->GetMandatoryRowCount();
882 },
883 [&]( int row )
884 {
885 m_fields->SwapRows( row, row + 1 );
886 m_grid->ForceRefresh();
887 OnModify();
888 } );
889}
890
891
893{
894 m_bodyStyleNamesGrid->OnAddRow(
895 [&]() -> std::pair<int, int>
896 {
897 m_bodyStyleNamesGrid->AppendRows( 1 );
898 OnModify();
899
900 return { m_bodyStyleNamesGrid->GetNumberRows() - 1, 0 };
901 } );
902}
903
904
906{
907 m_bodyStyleNamesGrid->OnDeleteRows(
908 [&]( int row )
909 {
910 m_bodyStyleNamesGrid->DeleteRows( row );
911 } );
912
913 OnModify();
914}
915
916
918{
919 m_bodyStyleNamesGrid->OnMoveRowUp(
920 [&]( int row )
921 {
922 m_bodyStyleNamesGrid->SwapRows( row, row - 1 );
923 OnModify();
924 } );
925}
926
927
929{
930 m_bodyStyleNamesGrid->OnMoveRowDown(
931 [&]( int row )
932 {
933 m_bodyStyleNamesGrid->SwapRows( row, row + 1 );
934 OnModify();
935 } );
936}
937
938
940{
941 if( !m_grid->CommitPendingChanges() )
942 return;
943
944 m_grid->ClearSelection();
945
946 std::vector<SCH_FIELD> fields;
947
948 for( const SCH_FIELD& field : *m_fields )
949 fields.emplace_back( field );
950
951 DIALOG_SIM_MODEL dialog( this, m_parentFrame, *m_libEntry, fields );
952
953 if( dialog.ShowModal() != wxID_OK )
954 return;
955
956 // Add in any new fields
957 for( const SCH_FIELD& editedField : fields )
958 {
959 bool found = false;
960
961 for( SCH_FIELD& existingField : *m_fields )
962 {
963 if( existingField.GetName() == editedField.GetName() )
964 {
965 found = true;
966 existingField.SetText( editedField.GetText() );
967 break;
968 }
969 }
970
971 if( !found )
972 {
973 m_fields->emplace_back( editedField );
974 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
975 m_grid->ProcessTableMessage( msg );
976 }
977 }
978
979 // Remove any deleted fields
980 for( int ii = (int) m_fields->size() - 1; ii >= 0; --ii )
981 {
982 SCH_FIELD& existingField = m_fields->at( ii );
983 bool found = false;
984
985 for( SCH_FIELD& editedField : fields )
986 {
987 if( editedField.GetName() == existingField.GetName() )
988 {
989 found = true;
990 break;
991 }
992 }
993
994 if( !found )
995 {
996 m_grid->ClearSelection();
997 m_fields->erase( m_fields->begin() + ii );
998
999 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, ii, 1 );
1000 m_grid->ProcessTableMessage( msg );
1001 }
1002 }
1003
1004 OnModify();
1005 m_grid->ForceRefresh();
1006}
1007
1008
1010{
1011 int idx = m_FootprintFilterListBox->HitTest( event.GetPosition() );
1012 wxCommandEvent dummy;
1013
1014 if( idx >= 0 )
1016 else
1018}
1019
1020
1022{
1023 // Running the Footprint Browser gums up the works and causes the automatic cancel
1024 // stuff to no longer work. So we do it here ourselves.
1025 EndQuasiModal( wxID_CANCEL );
1026}
1027
1028
1030{
1031 wxString filterLine;
1032 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Add Footprint Filter" ), filterLine );
1033
1034 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue().IsEmpty() )
1035 return;
1036
1037 filterLine = dlg.GetValue();
1038 filterLine.Replace( wxT( " " ), wxT( "_" ) );
1039
1040 // duplicate filters do no harm, so don't be a nanny.
1041 m_FootprintFilterListBox->Append( filterLine );
1042 m_FootprintFilterListBox->SetSelection( (int) m_FootprintFilterListBox->GetCount() - 1 );
1043
1044 OnModify();
1045}
1046
1047
1049{
1050 wxArrayInt selections;
1051 int n = m_FootprintFilterListBox->GetSelections( selections );
1052
1053 if( n > 0 )
1054 {
1055 // Just edit the first one
1056 int idx = selections[0];
1057 wxString filter = m_FootprintFilterListBox->GetString( idx );
1058
1059 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Edit Footprint Filter" ), filter );
1060
1061 if( dlg.ShowModal() == wxID_OK && !dlg.GetValue().IsEmpty() )
1062 {
1063 m_FootprintFilterListBox->SetString( (unsigned) idx, dlg.GetValue() );
1064 OnModify();
1065 }
1066 }
1067}
1068
1069
1070void DIALOG_LIB_SYMBOL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
1071{
1072 m_OptionPartsInterchangeable->Enable( m_unitSpinCtrl->GetValue() > 1 );
1073 m_pinNameOffset.Enable( m_PinsNameInsideButt->GetValue() );
1074
1075 if( m_grid->IsCellEditControlShown() )
1076 {
1077 int row = m_grid->GetGridCursorRow();
1078 int col = m_grid->GetGridCursorCol();
1079
1080 if( row == m_fields->GetFieldRow( FIELD_T::VALUE ) && col == FDC_VALUE && m_OptionPower->IsChecked() )
1081 {
1082 wxGridCellEditor* editor = m_grid->GetCellEditor( row, col );
1083 m_SymbolNameCtrl->ChangeValue( editor->GetValue() );
1084 editor->DecRef();
1085 }
1086 }
1087
1088 // Handle shown columns changes
1089 std::bitset<64> shownColumns = m_grid->GetShownColumns();
1090
1091 if( shownColumns != m_shownColumns )
1092 {
1093 m_shownColumns = shownColumns;
1094
1095 if( !m_grid->IsCellEditControlShown() )
1096 m_grid->SetGridWidthsDirty();
1097 }
1098
1099 // Handle a delayed focus. The delay allows us to:
1100 // a) change focus when the error was triggered from within a killFocus handler
1101 // b) show the correct notebook page in the background before the error dialog comes up
1102 // when triggered from an OK or a notebook page change
1103
1104 if( m_delayedFocusPage >= 0 && m_NoteBook->GetSelection() != m_delayedFocusPage )
1105 {
1106 m_NoteBook->ChangeSelection( (unsigned) m_delayedFocusPage );
1107 m_delayedFocusPage = -1;
1108 }
1109
1110 if( !m_delayedErrorMessage.IsEmpty() )
1111 {
1112 // We will re-enter this routine when the error dialog is displayed, so make
1113 // sure we don't keep putting up more dialogs.
1114 wxString msg = m_delayedErrorMessage;
1115 m_delayedErrorMessage = wxEmptyString;
1116
1117 // Do not use DisplayErrorMessage(); it screws up window order on Mac
1118 DisplayError( nullptr, msg );
1119 }
1120
1121 if( m_delayedFocusCtrl )
1122 {
1123 m_delayedFocusCtrl->SetFocus();
1124
1125 if( wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_delayedFocusCtrl ) )
1126 textEntry->SelectAll();
1127
1128 m_delayedFocusCtrl = nullptr;
1129 }
1130 else if( m_delayedFocusGrid )
1131 {
1132 m_delayedFocusGrid->SetFocus();
1135
1136 m_delayedFocusGrid->EnableCellEditControl( true );
1137 m_delayedFocusGrid->ShowCellEditControl();
1138
1139 m_delayedFocusGrid = nullptr;
1140 m_delayedFocusRow = -1;
1142 }
1143}
1144
1145
1147{
1148 bSizerLowerBasicPanel->Show( !aIsAlias );
1149 m_inheritanceSelectCombo->Enable( aIsAlias );
1150 m_inheritsStaticText->Enable( aIsAlias );
1151 m_grid->ForceRefresh();
1152}
1153
1154
1156{
1157 if( m_OptionPower->IsChecked() )
1158 {
1159 m_excludeFromSimCheckBox->SetValue( true );
1160 m_excludeFromBomCheckBox->SetValue( true );
1161 m_excludeFromBoardCheckBox->SetValue( true );
1162 m_excludeFromBomCheckBox->Enable( false );
1163 m_excludeFromBoardCheckBox->Enable( false );
1164 m_excludeFromSimCheckBox->Enable( false );
1165 m_spiceFieldsButton->Show( false );
1166 m_OptionLocalPower->Enable( true );
1167 }
1168 else
1169 {
1170 m_excludeFromBomCheckBox->Enable( true );
1171 m_excludeFromBoardCheckBox->Enable( true );
1172 m_excludeFromSimCheckBox->Enable( true );
1173 m_spiceFieldsButton->Show( true );
1174 m_OptionLocalPower->Enable( false );
1175 }
1176
1177 OnModify();
1178}
1179
1180
1181void DIALOG_LIB_SYMBOL_PROPERTIES::OnText( wxCommandEvent& event )
1182{
1183 OnModify();
1184}
1185
1186
1187void DIALOG_LIB_SYMBOL_PROPERTIES::OnCombobox( wxCommandEvent& event )
1188{
1189 OnModify();
1190}
1191
1192
1193void DIALOG_LIB_SYMBOL_PROPERTIES::OnCheckBox( wxCommandEvent& event )
1194{
1195 OnModify();
1196}
1197
1198
1200{
1201 m_unitNamesGrid->CommitPendingChanges( true /* aQuietMode */ );
1202
1203 int extra = m_unitNamesGrid->GetNumberRows() - m_unitSpinCtrl->GetValue();
1204 int needed = m_unitSpinCtrl->GetValue() - m_unitNamesGrid->GetNumberRows();
1205
1206 if( extra > 0 )
1207 {
1208 m_unitNamesGrid->DeleteRows( m_unitNamesGrid->GetNumberRows() - extra, extra );
1209 return true;
1210 }
1211
1212 if( needed > 0 )
1213 {
1214 m_unitNamesGrid->AppendRows( needed );
1215
1216 for( int row = m_unitNamesGrid->GetNumberRows() - needed; row < m_unitNamesGrid->GetNumberRows(); ++row )
1217 m_unitNamesGrid->SetCellValue( row, 0, LIB_SYMBOL::LetterSubReference( row + 1, 'A' ) );
1218
1219 return true;
1220 }
1221
1222 return false;
1223}
1224
1225
1227{
1228 if( updateUnitCount() )
1229 OnModify();
1230}
1231
1232
1234{
1235 // wait for kill focus to update unit count
1236}
1237
1238
1240{
1241 if( updateUnitCount() )
1242 OnModify();
1243}
1244
1245
1247{
1248 if( updateUnitCount() )
1249 OnModify();
1250}
1251
1252
1254{
1255 if( !m_grid->CommitPendingChanges() )
1256 aEvent.Veto();
1257}
1258
1259
1261{
1262 m_jumperGroupsGrid->OnAddRow(
1263 [&]() -> std::pair<int, int>
1264 {
1265 m_jumperGroupsGrid->AppendRows( 1 );
1266 OnModify();
1267
1268 return { m_jumperGroupsGrid->GetNumberRows() - 1, 0 };
1269 } );
1270}
1271
1272
1274{
1275 m_jumperGroupsGrid->OnDeleteRows(
1276 [&]( int row )
1277 {
1278 m_jumperGroupsGrid->DeleteRows( row, 1 );
1279 } );
1280
1281 OnModify();
1282}
1283
1284
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
DIALOG_LIB_SYMBOL_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=ID_LIBEDIT_NOTEBOOK, const wxString &title=_("Library Symbol Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnSymbolNameKillFocus(wxFocusEvent &event) override
void OnUnitSpinCtrlText(wxCommandEvent &event) override
std::unique_ptr< LISTBOX_TRICKS > m_fpFilterTricks
void OnPageChanging(wxNotebookEvent &event) override
void OnBodyStyle(wxCommandEvent &event) override
DIALOG_LIB_SYMBOL_PROPERTIES(SYMBOL_EDIT_FRAME *parent, LIB_SYMBOL *aLibEntry)
void OnUpdateUI(wxUpdateUIEvent &event) override
void OnBodyStyleMoveUp(wxCommandEvent &event) override
void OnCancelButtonClick(wxCommandEvent &event) override
void OnEditFootprintFilter(wxCommandEvent &event) override
void OnDeleteField(wxCommandEvent &event) override
void OnCombobox(wxCommandEvent &event) override
void OnFpFilterDClick(wxMouseEvent &event) override
void OnUnitSpinCtrlEnter(wxCommandEvent &event) override
void OnAddBodyStyle(wxCommandEvent &event) override
void OnMoveUp(wxCommandEvent &event) override
void OnDeleteBodyStyle(wxCommandEvent &event) override
void OnAddJumperGroup(wxCommandEvent &event) override
void OnText(wxCommandEvent &event) override
void OnUnitSpinCtrlKillFocus(wxFocusEvent &event) override
void OnMoveDown(wxCommandEvent &event) override
void OnAddFootprintFilter(wxCommandEvent &event) override
void OnCheckBox(wxCommandEvent &event) override
void OnSymbolNameText(wxCommandEvent &event) override
void OnBodyStyleMoveDown(wxCommandEvent &event) override
void OnRemoveJumperGroup(wxCommandEvent &event) override
void onPowerCheckBox(wxCommandEvent &aEvent) override
void OnAddField(wxCommandEvent &event) override
void OnEditSpiceModel(wxCommandEvent &event) override
void addInheritedFields(const std::shared_ptr< LIB_SYMBOL > &aParent)
void OnUnitSpinCtrl(wxSpinEvent &event) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void resetSize()
Clear the existing dialog size and position.
void EndQuasiModal(int retCode)
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
EDA_BASE_FRAME * m_parentFrame
int ShowModal() override
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:540
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:394
A text control validator used for validating the text allowed in fields.
Definition validators.h:142
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
Define a library symbol object.
Definition lib_symbol.h:83
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition lib_symbol.h:203
bool IsDerived() const
Definition lib_symbol.h:204
static wxString LetterSubReference(int aUnit, wxChar aInitialLetter)
void SetOrdinal(int aOrdinal)
Definition sch_field.h:122
bool IsMandatory() const
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
void SetName(const wxString &aName)
The symbol library editor main window.
bool IsSymbolFromLegacyLibrary() const
bool IsSymbolFromSchematic() const
void AddTemplateFieldNames(const wxString &aSerializedFieldNames)
Add a serialized list of template field names.
const std::vector< TEMPLATE_FIELDNAME > & GetTemplateFieldNames()
Return a template field name list for read only access.
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
#define _(s)
@ FDC_NAME
@ FDC_VALUE
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
STL namespace.
see class PGM_BASE
Collection of utility functions for component reference designators (refdes)
T * GetAppSettings(const char *aFilename)
std::vector< FAB_LAYER_COLOR > dummy
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
Hold a name of a symbol's field, field value, and default visibility.
wxString GetUserFieldName(int aFieldNdx, bool aTranslateForHI)
#define DO_TRANSLATE
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
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:695