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 // Show/hide columns according to the user's preference
96 SYMBOL_EDITOR_SETTINGS* cfg = m_Parent->GetSettings();
97 m_grid->ShowHideColumns( cfg->m_EditSymbolVisibleColumns );
98
100
101 m_unitNamesGrid->PushEventHandler( new GRID_TRICKS( m_unitNamesGrid ) );
102 m_unitNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
103
105 [this]( wxCommandEvent& aEvent )
106 {
107 OnAddBodyStyle( aEvent );
108 } ) );
109 m_bodyStyleNamesGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
110
111 m_jumperGroupsGrid->SetupColumnAutosizer( 0 );
112 m_jumperGroupsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
113
114 m_jumperGroupsGrid->PushEventHandler( new GRID_TRICKS( m_jumperGroupsGrid,
115 [this]( wxCommandEvent& aEvent )
116 {
117 OnAddJumperGroup( aEvent );
118 } ) );
119
120 // Configure button logos
125
130
134
137
139
140 if( aParent->IsSymbolFromLegacyLibrary() && !aParent->IsSymbolFromSchematic() )
141 {
142 m_stdSizerButtonCancel->SetDefault();
143 m_stdSizerButtonOK->SetLabel( _( "Read Only" ) );
144 m_stdSizerButtonOK->Enable( false );
145 }
146
147 // wxFormBuilder doesn't include this event...
148 m_grid->Bind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
149 m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
150 m_grid->GetGridWindow()->Bind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
151
152
153 // Forward the delete button to the tricks
154 m_deleteFilterButton->Bind( wxEVT_BUTTON,
155 [&]( wxCommandEvent& aEvent )
156 {
157 wxCommandEvent cmdEvent( EDA_EVT_LISTBOX_DELETE );
158 m_fpFilterTricks->ProcessEvent( cmdEvent );
159 } );
160
161 // When the filter tricks modifies something, update ourselves
162 m_FootprintFilterListBox->Bind( EDA_EVT_LISTBOX_CHANGED,
163 [&]( wxCommandEvent& aEvent )
164 {
165 OnModify();
166 } );
167
169 {
172 {
173 resetSize();
174 }
175 }
176
179
180 m_grid->GetParent()->Layout();
181 syncControlStates( m_libEntry->IsDerived() );
182 Layout();
183
185}
186
187
189{
190 m_lastOpenedPage = m_NoteBook->GetSelection( );
191
192 if( SYMBOL_EDITOR_SETTINGS* cfg = m_Parent->GetSettings() )
193 cfg->m_EditSymbolVisibleColumns = m_grid->GetShownColumnsAsString();
194
195 // Prevents crash bug in wxGrid's d'tor
196 m_grid->DestroyTable( m_fields );
197
198 m_grid->Unbind( wxEVT_GRID_CELL_CHANGING, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanging, this );
199 m_grid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridCellChanged, this );
200 m_grid->GetGridWindow()->Unbind( wxEVT_MOTION, &DIALOG_LIB_SYMBOL_PROPERTIES::OnGridMotion, this );
201
202 // Delete the GRID_TRICKS.
203 m_grid->PopEventHandler( true );
204 m_unitNamesGrid->PopEventHandler( true );
205 m_bodyStyleNamesGrid->PopEventHandler( true );
206 m_jumperGroupsGrid->PopEventHandler( true );
207}
208
209
210void DIALOG_LIB_SYMBOL_PROPERTIES::addInheritedFields( const std::shared_ptr<LIB_SYMBOL>& aParent )
211{
212 if( std::shared_ptr<LIB_SYMBOL> ancestor = aParent->GetParent().lock() )
213 addInheritedFields( ancestor );
214
215 std::vector<SCH_FIELD*> parentFields;
216 aParent->GetFields( parentFields );
217
218 for( SCH_FIELD* parentField : parentFields )
219 {
220 bool found = false;
221
222 if( parentField->IsMandatory() )
223 continue; // Don't inherit mandatory fields
224
225 for( size_t ii = 0; ii < m_fields->size(); ++ii )
226 {
227 SCH_FIELD& field = m_fields->at( ii );
228
229 if( field.IsMandatory() )
230 continue; // Don't inherit mandatory fields
231
232 if( field.GetCanonicalName() == parentField->GetCanonicalName() )
233 {
234 m_fields->SetFieldInherited( ii, *parentField );
235 found = true;
236 break;
237 }
238 }
239
240 if( !found )
241 m_fields->AddInheritedField( *parentField );
242 }
243}
244
245
247{
248 if( !wxDialog::TransferDataToWindow() )
249 return false;
250
251 std::set<wxString> defined;
252
253 for( SCH_FIELD& field : *m_fields )
254 defined.insert( field.GetName() );
255
256 // Add in any template fieldnames not yet defined:
257 // Read global fieldname templates
259 {
260 TEMPLATES templateMgr;
261
262 if( !cfg->m_Drawing.field_names.IsEmpty() )
263 templateMgr.AddTemplateFieldNames( cfg->m_Drawing.field_names );
264
265 for( const TEMPLATE_FIELDNAME& templateFieldname : templateMgr.GetTemplateFieldNames() )
266 {
267 if( defined.count( templateFieldname.m_Name ) <= 0 )
268 {
269 SCH_FIELD field( m_libEntry, FIELD_T::USER, templateFieldname.m_Name );
270 field.SetVisible( templateFieldname.m_Visible );
271 m_fields->push_back( field );
272 m_addedTemplateFields.insert( templateFieldname.m_Name );
273 }
274 }
275 }
276
277 // The Y axis for components in library file is from bottom to top while the screen axis is top
278 // to bottom.However it is nowhandled by the lib file parser/writer.
279
280 // notify the grid
281 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_fields->GetNumberRows() );
282 m_grid->ProcessTableMessage( msg );
283
284 m_SymbolNameCtrl->ChangeValue( UnescapeString( m_libEntry->GetName() ) );
285
286 m_KeywordCtrl->ChangeValue( m_libEntry->GetKeyWords() );
287 m_unitSpinCtrl->SetValue( m_libEntry->GetUnitCount() );
288 m_OptionPartsInterchangeable->SetValue( !m_libEntry->UnitsLocked() || m_libEntry->GetUnitCount() == 1 );
289
291
292 for( int unit = 0; unit < m_libEntry->GetUnitCount(); unit++ )
293 {
294 if( m_libEntry->GetUnitDisplayNames().contains( unit + 1 ) )
295 m_unitNamesGrid->SetCellValue( unit, 1, m_libEntry->GetUnitDisplayNames().at( unit + 1 ) );
296 }
297
298 if( m_libEntry->HasDeMorganBodyStyles() )
299 {
300 m_radioDeMorgan->SetValue( true );
301 }
302 else if( m_libEntry->IsMultiBodyStyle() )
303 {
304 m_radioCustom->SetValue( true );
305
306 for( const wxString& name : m_libEntry->GetBodyStyleNames() )
307 {
308 int row = m_bodyStyleNamesGrid->GetNumberRows();
309 m_bodyStyleNamesGrid->AppendRows( 1 );
310 m_bodyStyleNamesGrid->SetCellValue( row, 0, name );
311 }
312 }
313 else
314 {
315 m_radioSingle->SetValue( true );
316 }
317
318 m_OptionPower->SetValue( m_libEntry->IsPower() );
319 m_OptionLocalPower->SetValue( m_libEntry->IsLocalPower() );
320
321 if( m_libEntry->IsPower() )
322 {
323 m_spiceFieldsButton->Hide();
324 m_OptionLocalPower->Enable();
325 }
326 else
327 {
328 m_OptionLocalPower->Enable( false );
329 }
330
331 m_excludeFromSimCheckBox->SetValue( m_libEntry->GetExcludedFromSim() );
332 m_excludeFromBomCheckBox->SetValue( m_libEntry->GetExcludedFromBOM() );
333 m_excludeFromBoardCheckBox->SetValue( m_libEntry->GetExcludedFromBoard() );
334
335 m_ShowPinNumButt->SetValue( m_libEntry->GetShowPinNumbers() );
336 m_ShowPinNameButt->SetValue( m_libEntry->GetShowPinNames() );
337 m_PinsNameInsideButt->SetValue( m_libEntry->GetPinNameOffset() != 0 );
338 m_pinNameOffset.ChangeValue( m_libEntry->GetPinNameOffset() );
339
340 wxArrayString tmp = m_libEntry->GetFPFilters();
341 m_FootprintFilterListBox->Append( tmp );
342
343 m_cbDuplicatePinsAreJumpers->SetValue( m_libEntry->GetDuplicatePinNumbersAreJumpers() );
344
345 std::set<wxString> availablePins;
346
347 for( const SCH_PIN* pin : m_libEntry->GetPins() )
348 availablePins.insert( pin->GetNumber() );
349
350 for( const std::set<wxString>& group : m_libEntry->JumperPinGroups() )
351 {
352 wxString groupTxt;
353
354 for( const wxString& pinNumber : group )
355 {
356 if( !groupTxt.IsEmpty() )
357 groupTxt << ", ";
358
359 groupTxt << pinNumber;
360 }
361
362 m_jumperGroupsGrid->AppendRows( 1 );
363 m_jumperGroupsGrid->SetCellValue( m_jumperGroupsGrid->GetNumberRows() - 1, 0, groupTxt );
364 }
365
366 // Populate the list of root parts for inherited objects.
367 if( m_libEntry->IsDerived() )
368 {
369 wxArrayString symbolNames;
370 wxString libName = m_Parent->GetCurLib();
371
372 // Someone forgot to set the current library in the editor frame window.
373 wxCHECK( !libName.empty(), false );
374
375 m_Parent->GetLibManager().GetSymbolNames( libName, symbolNames );
376
377 // Sort the list of symbols for easier search
378 symbolNames.Sort(
379 []( const wxString& a, const wxString& b ) -> int
380 {
381 return StrNumCmp( a, b, true );
382 } );
383
384 // Do allow an inherited symbol to be derived from itself.
385 if( symbolNames.Index( m_libEntry->GetName() ) != wxNOT_FOUND )
386 symbolNames.Remove( m_libEntry->GetName() );
387
388 m_inheritanceSelectCombo->Append( symbolNames );
389
390 if( std::shared_ptr<LIB_SYMBOL> rootSymbol = m_libEntry->GetParent().lock() )
391 {
392 wxString parentName = UnescapeString( rootSymbol->GetName() );
393 int selection = m_inheritanceSelectCombo->FindString( parentName );
394
395 if( selection == wxNOT_FOUND )
396 return false;
397
398 m_inheritanceSelectCombo->SetSelection( selection );
399 }
400
402 }
403
404 m_NoteBook->SetSelection( (unsigned) m_lastOpenedPage );
405
406 m_embeddedFiles->TransferDataToWindow();
407
408 return true;
409}
410
411
413{
414 if( !m_grid->CommitPendingChanges() )
415 return false;
416
417 // Symbol reference can be empty because it inherits from the parent symbol.
418 if( m_libEntry->IsRoot() )
419 {
420 SCH_FIELD* field = m_fields->GetField( FIELD_T::REFERENCE );
421
422 if( UTIL::GetRefDesPrefix( field->GetText() ).IsEmpty() )
423 {
424 if( m_NoteBook->GetSelection() != 0 )
425 m_NoteBook->SetSelection( 0 );
426
427 m_delayedErrorMessage = _( "References must start with a letter." );
432
433 return false;
434 }
435 }
436
437 // Check for missing field names.
438 for( int ii = 0; ii < (int) m_fields->size(); ++ii )
439 {
440 SCH_FIELD& field = m_fields->at( ii );
441
442 if( field.IsMandatory() )
443 continue;
444
445 wxString fieldName = field.GetName( false );
446
447 if( fieldName.IsEmpty() && !field.GetText().IsEmpty() )
448 {
449 if( m_NoteBook->GetSelection() != 0 )
450 m_NoteBook->SetSelection( 0 );
451
452 m_delayedErrorMessage = _( "Fields must have a name." );
457
458 return false;
459 }
460 }
461
462 // Verify that the parent name is set if the symbol is inherited
463 if( m_libEntry->IsDerived() )
464 {
465 wxString parentName = m_inheritanceSelectCombo->GetValue();
466
467 if( parentName.IsEmpty() )
468 {
469 m_delayedErrorMessage = _( "Derived symbol must have a parent selected" );
470 return false;
471 }
472 }
473
474 /*
475 * Confirm destructive actions.
476 */
477
478 if( m_unitSpinCtrl->GetValue() < m_libEntry->GetUnitCount() )
479 {
480 if( !IsOK( this, _( "Delete extra units from symbol?" ) ) )
481 return false;
482 }
483
484 int bodyStyleCount = 0;
485
486 if( m_radioSingle->GetValue() )
487 {
488 bodyStyleCount = 1;
489 }
490 if( m_radioDeMorgan->GetValue() )
491 {
492 bodyStyleCount = 2;
493 }
494 else if( m_radioCustom->GetValue() )
495 {
496 for( int ii = 0; ii < m_bodyStyleNamesGrid->GetNumberRows(); ++ii )
497 {
498 if( !m_bodyStyleNamesGrid->GetCellValue( ii, 0 ).IsEmpty() )
499 bodyStyleCount++;
500 }
501 }
502
503 if( bodyStyleCount == 0 )
504 {
505 m_delayedErrorMessage = _( "Symbol must have at least 1 body style" );
506 return false;
507 }
508
509 if( bodyStyleCount < m_libEntry->GetBodyStyleCount() )
510 {
511 if( !IsOK( this, _( "Delete extra body styles from symbol?" ) ) )
512 return false;
513 }
514
515 return true;
516}
517
518
520{
521 if( !m_grid->CommitPendingChanges()
522 || !m_unitNamesGrid->CommitPendingChanges()
523 || !m_bodyStyleNamesGrid->CommitPendingChanges()
524 || !m_jumperGroupsGrid->CommitPendingChanges() )
525 {
526 return false;
527 }
528
529 wxString newName = EscapeString( m_SymbolNameCtrl->GetValue(), CTX_LIBID );
530 wxString oldName = m_libEntry->GetName();
531
532 if( newName.IsEmpty() )
533 {
534 wxMessageBox( _( "Symbol must have a name." ) );
535 return false;
536 }
537
539
540 if( oldName != newName )
541 {
542 wxString libName = m_Parent->GetCurLib();
543
544 if( m_Parent->GetLibManager().SymbolNameInUse( newName, libName ) )
545 {
546 wxString msg;
547
548 msg.Printf( _( "Symbol name '%s' already in use in library '%s'." ),
549 UnescapeString( newName ),
550 libName );
551 DisplayErrorMessage( this, msg );
552 return false;
553 }
554
555 opType = UNDO_REDO::LIB_RENAME;
556 }
557
558 m_Parent->SaveCopyInUndoList( _( "Edit Symbol Properties" ), m_libEntry, opType );
559
560 // The Y axis for components in lib is from bottom to top while the screen axis is top
561 // to bottom: we must change the y coord sign when writing back to the library
562 std::vector<SCH_FIELD> fieldsToSave;
563 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T ids.
564
565 for( size_t ii = 0; ii < m_fields->size(); ++ii )
566 {
567 SCH_FIELD& field = m_fields->at( ii );
568
569 if( !field.IsMandatory() )
570 field.SetOrdinal( ordinal++ );
571
572 wxString fieldName = field.GetCanonicalName();
573
574 if( m_fields->IsInherited( ii ) && field == m_fields->ParentField( ii ) )
575 continue; // Skip inherited fields
576
577 if( field.GetText().IsEmpty() )
578 {
579 if( fieldName.IsEmpty() || m_addedTemplateFields.contains( fieldName ) )
580 continue; // Skip empty fields that are not mandatory or template fields
581 }
582 else if( fieldName.IsEmpty() )
583 {
584 field.SetName( _( "untitled" ) ); // Set a default name for unnamed fields
585 }
586
587 fieldsToSave.push_back( field );
588 }
589
590 m_libEntry->SetFields( fieldsToSave );
591
592 // Update the parent for inherited symbols
593 if( m_libEntry->IsDerived() )
594 {
595 wxString parentName = EscapeString( m_inheritanceSelectCombo->GetValue(), CTX_LIBID );
596
597 // The parentName was verified to be non-empty in the Validator
598 wxString libName = m_Parent->GetCurLib();
599
600 // Get the parent from the libManager based on the name set in the inheritance combo box.
601 LIB_SYMBOL* newParent = m_Parent->GetLibManager().GetSymbol( parentName, libName );
602
603 // Verify that the requested parent exists
604 wxCHECK( newParent, false );
605
606 m_libEntry->SetParent( newParent );
607 }
608
609 m_libEntry->SetName( newName );
610 m_libEntry->SetKeyWords( m_KeywordCtrl->GetValue() );
611 m_libEntry->SetUnitCount( m_unitSpinCtrl->GetValue(), true );
612 m_libEntry->LockUnits( m_libEntry->GetUnitCount() > 1 && !m_OptionPartsInterchangeable->GetValue() );
613
614 m_libEntry->GetUnitDisplayNames().clear();
615
616 for( int row = 0; row < m_unitNamesGrid->GetNumberRows(); row++ )
617 {
618 if( !m_unitNamesGrid->GetCellValue( row, 1 ).IsEmpty() )
619 m_libEntry->GetUnitDisplayNames()[row+1] = m_unitNamesGrid->GetCellValue( row, 1 );
620 }
621
622 if( m_radioSingle->GetValue() )
623 {
624 m_libEntry->SetHasDeMorganBodyStyles( false );
625 m_libEntry->SetBodyStyleCount( 1, false, false );
626 m_libEntry->SetBodyStyleNames( {} );
627 }
628 else if( m_radioDeMorgan->GetValue() )
629 {
630 m_libEntry->SetHasDeMorganBodyStyles( true );
631 m_libEntry->SetBodyStyleCount( 2, false, true );
632 m_libEntry->SetBodyStyleNames( {} );
633 }
634 else
635 {
636 std::vector<wxString> bodyStyleNames;
637
638 for( int row = 0; row < m_bodyStyleNamesGrid->GetNumberRows(); ++row )
639 {
640 if( !m_bodyStyleNamesGrid->GetCellValue( row, 0 ).IsEmpty() )
641 bodyStyleNames.push_back( m_bodyStyleNamesGrid->GetCellValue( row, 0 ) );
642 }
643
644 m_libEntry->SetHasDeMorganBodyStyles( false );
645 m_libEntry->SetBodyStyleCount( bodyStyleNames.size(), true, true );
646 m_libEntry->SetBodyStyleNames( bodyStyleNames );
647 }
648
649 if( m_OptionPower->GetValue() )
650 {
651 if( m_OptionLocalPower->GetValue() )
652 m_libEntry->SetLocalPower();
653 else
654 m_libEntry->SetGlobalPower();
655
656 // Power symbols must have value matching name for now
657 m_libEntry->GetValueField().SetText( newName );
658 }
659 else
660 {
661 m_libEntry->SetNormal();
662 }
663
664 m_libEntry->SetExcludedFromSim( m_excludeFromSimCheckBox->GetValue() );
665 m_libEntry->SetExcludedFromBOM( m_excludeFromBomCheckBox->GetValue() );
666 m_libEntry->SetExcludedFromBoard( m_excludeFromBoardCheckBox->GetValue() );
667
668 m_libEntry->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
669 m_libEntry->SetShowPinNames( m_ShowPinNameButt->GetValue() );
670
671 if( m_PinsNameInsideButt->GetValue() )
672 {
673 int offset = KiROUND( (double) m_pinNameOffset.GetValue() );
674
675 // We interpret an offset of 0 as "outside", so make sure it's non-zero
676 m_libEntry->SetPinNameOffset( offset == 0 ? 20 : offset );
677 }
678 else
679 {
680 m_libEntry->SetPinNameOffset( 0 ); // pin text outside the body (name is on the pin)
681 }
682
683 m_libEntry->SetFPFilters( m_FootprintFilterListBox->GetStrings());
684
685 m_libEntry->SetDuplicatePinNumbersAreJumpers( m_cbDuplicatePinsAreJumpers->GetValue() );
686
687 std::vector<std::set<wxString>>& jumpers = m_libEntry->JumperPinGroups();
688 jumpers.clear();
689
690 for( int ii = 0; ii < m_jumperGroupsGrid->GetNumberRows(); ++ii )
691 {
692 wxStringTokenizer tokenizer( m_jumperGroupsGrid->GetCellValue( ii, 0 ), ", \t\r\n", wxTOKEN_STRTOK );
693 std::set<wxString>& group = jumpers.emplace_back();
694
695 while( tokenizer.HasMoreTokens() )
696 {
697 if( wxString token = tokenizer.GetNextToken(); !token.IsEmpty() )
698 group.insert( token );
699 }
700 }
701
702 m_Parent->UpdateAfterSymbolProperties( &oldName );
703
704 return true;
705}
706
707
708void DIALOG_LIB_SYMBOL_PROPERTIES::OnBodyStyle( wxCommandEvent& event )
709{
710 m_bodyStyleNamesGrid->Enable( m_radioCustom->GetValue() );
711
712 m_bpAddBodyStyle->Enable( m_radioCustom->GetValue() );
713 m_bpMoveUpBodyStyle->Enable( m_radioCustom->GetValue() );
714 m_bpMoveDownBodyStyle->Enable( m_radioCustom->GetValue() );
715 m_bpDeleteBodyStyle->Enable( m_radioCustom->GetValue() );
716}
717
718
720{
721 aEvent.Skip();
722
723 wxPoint pos = aEvent.GetPosition();
724 wxPoint unscolled_pos = m_grid->CalcUnscrolledPosition( pos );
725 int row = m_grid->YToRow( unscolled_pos.y );
726 int col = m_grid->XToCol( unscolled_pos.x );
727
728 if( row == wxNOT_FOUND || col == wxNOT_FOUND || !m_fields->IsInherited( row ) )
729 {
730 m_grid->SetToolTip( "" );
731 return;
732 }
733
734 m_grid->SetToolTip( wxString::Format( _( "This field is inherited from '%s'." ),
735 m_fields->ParentField( row ).GetName() ) );
736}
737
738
740{
741 wxGridCellEditor* editor = m_grid->GetCellEditor( event.GetRow(), event.GetCol() );
742 wxControl* control = editor->GetControl();
743
744 if( control && control->GetValidator() && !control->GetValidator()->Validate( control ) )
745 {
746 event.Veto();
747
749 m_delayedFocusRow = event.GetRow();
750 m_delayedFocusColumn = event.GetCol();
752 }
753 else if( event.GetCol() == FDC_NAME )
754 {
755 wxString newName = event.GetString();
756
757 for( int i = 0; i < m_grid->GetNumberRows(); ++i )
758 {
759 if( i == event.GetRow() )
760 continue;
761
762 if( newName.CmpNoCase( m_grid->GetCellValue( i, FDC_NAME ) ) == 0 )
763 {
764 DisplayError( this, wxString::Format( _( "The name '%s' is already in use." ), newName ) );
765 event.Veto();
766 m_delayedFocusRow = event.GetRow();
767 m_delayedFocusColumn = event.GetCol();
768 }
769 }
770 }
771
772 editor->DecRef();
773}
774
775
777{
778 m_grid->ForceRefresh();
779 OnModify();
780}
781
782
784{
785 if( m_OptionPower->IsChecked() )
786 m_grid->SetCellValue( m_fields->GetFieldRow( FIELD_T::VALUE ), FDC_VALUE, m_SymbolNameCtrl->GetValue() );
787
788 OnModify();
789}
790
791
793{
794 if( !m_delayedFocusCtrl )
795 {
796 // If the validation fails and we throw up a dialog then GTK will give us another
797 // KillFocus event and we end up in infinite recursion. So we use m_delayedFocusCtrl
798 // as a re-entrancy block and then clear it again if validation passes.
801
802 if( m_SymbolNameCtrl->GetValidator()->Validate( m_SymbolNameCtrl ) )
803 {
804 m_delayedFocusCtrl = nullptr;
806 }
807 }
808
809 event.Skip();
810}
811
812
813void DIALOG_LIB_SYMBOL_PROPERTIES::OnAddField( wxCommandEvent& event )
814{
815 m_grid->OnAddRow(
816 [&]() -> std::pair<int, int>
817 {
818 SYMBOL_EDITOR_SETTINGS* settings = m_Parent->GetSettings();
820
821 newField.SetTextSize( VECTOR2I( schIUScale.MilsToIU( settings->m_Defaults.text_size ),
822 schIUScale.MilsToIU( settings->m_Defaults.text_size ) ) );
823 newField.SetVisible( false );
824
825 m_fields->push_back( newField );
826
827 // notify the grid
828 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
829 m_grid->ProcessTableMessage( msg );
830 OnModify();
831
832 return { m_fields->size() - 1, FDC_NAME };
833 } );
834}
835
836
838{
839 m_grid->OnDeleteRows(
840 [&]( int row )
841 {
842 if( row < m_fields->GetMandatoryRowCount() )
843 {
844 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
845 m_fields->GetMandatoryRowCount() ) );
846 return false;
847 }
848
849 return true;
850 },
851 [&]( int row )
852 {
853 if( !m_fields->EraseRow( row ) )
854 return;
855
856 // notify the grid
857 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, row, 1 );
858 m_grid->ProcessTableMessage( msg );
859 } );
860
861 OnModify();
862}
863
864
865void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveUp( wxCommandEvent& event )
866{
867 m_grid->OnMoveRowUp(
868 [&]( int row )
869 {
870 return row > m_fields->GetMandatoryRowCount();
871 },
872 [&]( int row )
873 {
874 m_fields->SwapRows( row, row - 1 );
875 m_grid->ForceRefresh();
876 OnModify();
877 } );
878}
879
880
881void DIALOG_LIB_SYMBOL_PROPERTIES::OnMoveDown( wxCommandEvent& event )
882{
883 m_grid->OnMoveRowDown(
884 [&]( int row )
885 {
886 return row >= m_fields->GetMandatoryRowCount();
887 },
888 [&]( int row )
889 {
890 m_fields->SwapRows( row, row + 1 );
891 m_grid->ForceRefresh();
892 OnModify();
893 } );
894}
895
896
898{
899 m_bodyStyleNamesGrid->OnAddRow(
900 [&]() -> std::pair<int, int>
901 {
902 m_bodyStyleNamesGrid->AppendRows( 1 );
903 OnModify();
904
905 return { m_bodyStyleNamesGrid->GetNumberRows() - 1, 0 };
906 } );
907}
908
909
911{
912 m_bodyStyleNamesGrid->OnDeleteRows(
913 [&]( int row )
914 {
915 m_bodyStyleNamesGrid->DeleteRows( row );
916 } );
917
918 OnModify();
919}
920
921
923{
924 m_bodyStyleNamesGrid->OnMoveRowUp(
925 [&]( int row )
926 {
927 m_bodyStyleNamesGrid->SwapRows( row, row - 1 );
928 OnModify();
929 } );
930}
931
932
934{
935 m_bodyStyleNamesGrid->OnMoveRowDown(
936 [&]( int row )
937 {
938 m_bodyStyleNamesGrid->SwapRows( row, row + 1 );
939 OnModify();
940 } );
941}
942
943
945{
946 if( !m_grid->CommitPendingChanges() )
947 return;
948
949 m_grid->ClearSelection();
950
951 std::vector<SCH_FIELD> fields;
952
953 for( const SCH_FIELD& field : *m_fields )
954 fields.emplace_back( field );
955
956 DIALOG_SIM_MODEL dialog( this, m_parentFrame, *m_libEntry, fields );
957
958 if( dialog.ShowModal() != wxID_OK )
959 return;
960
961 // Add in any new fields
962 for( const SCH_FIELD& editedField : fields )
963 {
964 bool found = false;
965
966 for( SCH_FIELD& existingField : *m_fields )
967 {
968 if( existingField.GetName() == editedField.GetName() )
969 {
970 found = true;
971 existingField.SetText( editedField.GetText() );
972 break;
973 }
974 }
975
976 if( !found )
977 {
978 m_fields->emplace_back( editedField );
979 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
980 m_grid->ProcessTableMessage( msg );
981 }
982 }
983
984 // Remove any deleted fields
985 for( int ii = (int) m_fields->size() - 1; ii >= 0; --ii )
986 {
987 SCH_FIELD& existingField = m_fields->at( ii );
988 bool found = false;
989
990 for( SCH_FIELD& editedField : fields )
991 {
992 if( editedField.GetName() == existingField.GetName() )
993 {
994 found = true;
995 break;
996 }
997 }
998
999 if( !found )
1000 {
1001 m_grid->ClearSelection();
1002 m_fields->erase( m_fields->begin() + ii );
1003
1004 wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, ii, 1 );
1005 m_grid->ProcessTableMessage( msg );
1006 }
1007 }
1008
1009 OnModify();
1010 m_grid->ForceRefresh();
1011}
1012
1013
1015{
1016 int idx = m_FootprintFilterListBox->HitTest( event.GetPosition() );
1017 wxCommandEvent dummy;
1018
1019 if( idx >= 0 )
1021 else
1023}
1024
1025
1027{
1028 // Running the Footprint Browser gums up the works and causes the automatic cancel
1029 // stuff to no longer work. So we do it here ourselves.
1030 EndQuasiModal( wxID_CANCEL );
1031}
1032
1033
1035{
1036 wxString filterLine;
1037 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Add Footprint Filter" ), filterLine );
1038
1039 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue().IsEmpty() )
1040 return;
1041
1042 filterLine = dlg.GetValue();
1043 filterLine.Replace( wxT( " " ), wxT( "_" ) );
1044
1045 // duplicate filters do no harm, so don't be a nanny.
1046 m_FootprintFilterListBox->Append( filterLine );
1047 m_FootprintFilterListBox->SetSelection( (int) m_FootprintFilterListBox->GetCount() - 1 );
1048
1049 OnModify();
1050}
1051
1052
1054{
1055 wxArrayInt selections;
1056 int n = m_FootprintFilterListBox->GetSelections( selections );
1057
1058 if( n > 0 )
1059 {
1060 // Just edit the first one
1061 int idx = selections[0];
1062 wxString filter = m_FootprintFilterListBox->GetString( idx );
1063
1064 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Filter:" ), _( "Edit Footprint Filter" ), filter );
1065
1066 if( dlg.ShowModal() == wxID_OK && !dlg.GetValue().IsEmpty() )
1067 {
1068 m_FootprintFilterListBox->SetString( (unsigned) idx, dlg.GetValue() );
1069 OnModify();
1070 }
1071 }
1072}
1073
1074
1075void DIALOG_LIB_SYMBOL_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
1076{
1077 m_OptionPartsInterchangeable->Enable( m_unitSpinCtrl->GetValue() > 1 );
1078 m_pinNameOffset.Enable( m_PinsNameInsideButt->GetValue() );
1079
1080 if( m_grid->IsCellEditControlShown() )
1081 {
1082 int row = m_grid->GetGridCursorRow();
1083 int col = m_grid->GetGridCursorCol();
1084
1085 if( row == m_fields->GetFieldRow( FIELD_T::VALUE ) && col == FDC_VALUE && m_OptionPower->IsChecked() )
1086 {
1087 wxGridCellEditor* editor = m_grid->GetCellEditor( row, col );
1088 m_SymbolNameCtrl->ChangeValue( editor->GetValue() );
1089 editor->DecRef();
1090 }
1091 }
1092
1093 // Handle shown columns changes
1094 std::bitset<64> shownColumns = m_grid->GetShownColumns();
1095
1096 if( shownColumns != m_shownColumns )
1097 {
1098 m_shownColumns = shownColumns;
1099
1100 if( !m_grid->IsCellEditControlShown() )
1101 m_grid->SetGridWidthsDirty();
1102 }
1103
1104 // Handle a delayed focus. The delay allows us to:
1105 // a) change focus when the error was triggered from within a killFocus handler
1106 // b) show the correct notebook page in the background before the error dialog comes up
1107 // when triggered from an OK or a notebook page change
1108
1109 if( m_delayedFocusPage >= 0 && m_NoteBook->GetSelection() != m_delayedFocusPage )
1110 {
1111 m_NoteBook->ChangeSelection( (unsigned) m_delayedFocusPage );
1112 m_delayedFocusPage = -1;
1113 }
1114
1115 if( !m_delayedErrorMessage.IsEmpty() )
1116 {
1117 // We will re-enter this routine when the error dialog is displayed, so make
1118 // sure we don't keep putting up more dialogs.
1119 wxString msg = m_delayedErrorMessage;
1120 m_delayedErrorMessage = wxEmptyString;
1121
1122 // Do not use DisplayErrorMessage(); it screws up window order on Mac
1123 DisplayError( nullptr, msg );
1124 }
1125
1126 if( m_delayedFocusCtrl )
1127 {
1128 m_delayedFocusCtrl->SetFocus();
1129
1130 if( wxTextEntry* textEntry = dynamic_cast<wxTextEntry*>( m_delayedFocusCtrl ) )
1131 textEntry->SelectAll();
1132
1133 m_delayedFocusCtrl = nullptr;
1134 }
1135 else if( m_delayedFocusGrid )
1136 {
1137 m_delayedFocusGrid->SetFocus();
1140
1141 m_delayedFocusGrid->EnableCellEditControl( true );
1142 m_delayedFocusGrid->ShowCellEditControl();
1143
1144 m_delayedFocusGrid = nullptr;
1145 m_delayedFocusRow = -1;
1147 }
1148}
1149
1150
1152{
1153 bSizerLowerBasicPanel->Show( !aIsAlias );
1154 m_inheritanceSelectCombo->Enable( aIsAlias );
1155 m_inheritsStaticText->Enable( aIsAlias );
1156 m_grid->ForceRefresh();
1157}
1158
1159
1161{
1162 if( m_OptionPower->IsChecked() )
1163 {
1164 m_excludeFromSimCheckBox->SetValue( true );
1165 m_excludeFromBomCheckBox->SetValue( true );
1166 m_excludeFromBoardCheckBox->SetValue( true );
1167 m_excludeFromBomCheckBox->Enable( false );
1168 m_excludeFromBoardCheckBox->Enable( false );
1169 m_excludeFromSimCheckBox->Enable( false );
1170 m_spiceFieldsButton->Show( false );
1171 m_OptionLocalPower->Enable( true );
1172 }
1173 else
1174 {
1175 m_excludeFromBomCheckBox->Enable( true );
1176 m_excludeFromBoardCheckBox->Enable( true );
1177 m_excludeFromSimCheckBox->Enable( true );
1178 m_spiceFieldsButton->Show( true );
1179 m_OptionLocalPower->Enable( false );
1180 }
1181
1182 OnModify();
1183}
1184
1185
1186void DIALOG_LIB_SYMBOL_PROPERTIES::OnText( wxCommandEvent& event )
1187{
1188 OnModify();
1189}
1190
1191
1192void DIALOG_LIB_SYMBOL_PROPERTIES::OnCombobox( wxCommandEvent& event )
1193{
1194 OnModify();
1195}
1196
1197
1198void DIALOG_LIB_SYMBOL_PROPERTIES::OnCheckBox( wxCommandEvent& event )
1199{
1200 OnModify();
1201}
1202
1203
1205{
1206 m_unitNamesGrid->CommitPendingChanges( true /* aQuietMode */ );
1207
1208 int extra = m_unitNamesGrid->GetNumberRows() - m_unitSpinCtrl->GetValue();
1209 int needed = m_unitSpinCtrl->GetValue() - m_unitNamesGrid->GetNumberRows();
1210
1211 if( extra > 0 )
1212 {
1213 m_unitNamesGrid->DeleteRows( m_unitNamesGrid->GetNumberRows() - extra, extra );
1214 return true;
1215 }
1216
1217 if( needed > 0 )
1218 {
1219 m_unitNamesGrid->AppendRows( needed );
1220
1221 for( int row = m_unitNamesGrid->GetNumberRows() - needed; row < m_unitNamesGrid->GetNumberRows(); ++row )
1222 m_unitNamesGrid->SetCellValue( row, 0, LIB_SYMBOL::LetterSubReference( row + 1, 'A' ) );
1223
1224 return true;
1225 }
1226
1227 return false;
1228}
1229
1230
1232{
1233 if( updateUnitCount() )
1234 OnModify();
1235}
1236
1237
1239{
1240 // wait for kill focus to update unit count
1241}
1242
1243
1245{
1246 if( updateUnitCount() )
1247 OnModify();
1248}
1249
1250
1252{
1253 if( updateUnitCount() )
1254 OnModify();
1255}
1256
1257
1259{
1260 if( !m_grid->CommitPendingChanges() )
1261 aEvent.Veto();
1262}
1263
1264
1266{
1267 m_jumperGroupsGrid->OnAddRow(
1268 [&]() -> std::pair<int, int>
1269 {
1270 m_jumperGroupsGrid->AppendRows( 1 );
1271 OnModify();
1272
1273 return { m_jumperGroupsGrid->GetNumberRows() - 1, 0 };
1274 } );
1275}
1276
1277
1279{
1280 m_jumperGroupsGrid->OnDeleteRows(
1281 [&]( int row )
1282 {
1283 m_jumperGroupsGrid->DeleteRows( row, 1 );
1284 } );
1285
1286 OnModify();
1287}
1288
1289
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:544
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:397
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:85
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition lib_symbol.h:205
bool IsDerived() const
Definition lib_symbol.h:206
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".
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