KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_properties_panel.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 (C) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
23
25#include <font/fontconfig.h>
26#include <pgm_base.h>
27#include <common.h>
28#include <confirm.h>
29#include <connection_graph.h>
33#include <properties/property.h>
34#include <sch_commit.h>
35#include <sch_edit_frame.h>
36#include <sch_sheet.h>
37#include <symbol_edit_frame.h>
38#include <symbol_viewer_frame.h>
39#include <schematic.h>
40#include <sch_symbol.h>
41#include <lib_symbol.h>
42#include <sch_line.h>
43#include <sch_junction.h>
44#include <sch_field.h>
45#include <pin_map.h>
46#include <template_fieldnames.h>
48#include <string_utils.h>
49#include <tool/tool_manager.h>
50#include <tools/sch_actions.h>
53#include <wx_filename.h>
54
55
58
60 PROPERTIES_PANEL( aParent, aFrame ),
61 m_frame( aFrame ),
62 m_propMgr( PROPERTY_MANAGER::Instance() ),
63 m_unitEditorInstance( nullptr ),
64 m_checkboxEditorInstance( nullptr ),
65 m_colorEditorInstance( nullptr ),
66 m_fpEditorInstance( nullptr ),
67 m_urlEditorInstance( nullptr )
68{
69 addCategoryButton( _HKI( "Fields" ), _( "Add Field" ), BITMAPS::small_plus,
70 [this]()
71 {
73 } );
74
75 addCategoryButton( _HKI( "Custom Properties" ), _( "Add Custom Property" ), BITMAPS::small_plus,
76 [this]()
77 {
79 } );
80
82 _HKI( "Pin Map" ), _( "Edit Pin Map..." ), BITMAPS::small_edit,
83 [this]()
84 {
86 },
87 [this]()
88 {
89 return m_frame->IsType( FRAME_SCH ) && getSinglePinMappedSymbol() != nullptr;
90 },
91 /* aForceCategory */ true );
92
93 m_propMgr.Rebuild();
94 bool found = false;
95
96 wxASSERT( wxPGGlobalVars );
97
98 wxString editorKey = PG_UNIT_EDITOR::BuildEditorName( m_frame );
99
100 auto it = wxPGGlobalVars->m_mapEditorClasses.find( editorKey );
101
102 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
103 {
104 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( it->second );
105 m_unitEditorInstance->UpdateFrame( m_frame );
106 found = true;
107 }
108
109 if( !found )
110 {
111 PG_UNIT_EDITOR* new_editor = new PG_UNIT_EDITOR( m_frame );
112 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( wxPropertyGrid::RegisterEditorClass( new_editor ) );
113 }
114
115 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_CHECKBOX_EDITOR::EDITOR_NAME );
116
117 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
118 {
119 PG_CHECKBOX_EDITOR* cbEditor = new PG_CHECKBOX_EDITOR();
120 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( wxPropertyGrid::RegisterEditorClass( cbEditor ) );
121 }
122 else
123 {
124 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( it->second );
125 }
126
127 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_COLOR_EDITOR::EDITOR_NAME );
128
129 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
130 {
131 PG_COLOR_EDITOR* colorEditor = new PG_COLOR_EDITOR();
132 m_colorEditorInstance = static_cast<PG_COLOR_EDITOR*>( wxPropertyGrid::RegisterEditorClass( colorEditor ) );
133 }
134 else
135 {
136 m_colorEditorInstance = static_cast<PG_COLOR_EDITOR*>( it->second );
137 }
138
139 auto netlistCallback = [this]()
140 {
141 SCH_SELECTION& sel = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>()->GetSelection();
142 LIB_SYMBOL* libSymbol = nullptr;
143
144 for( EDA_ITEM* item : sel )
145 {
146 if( item->Type() == SCH_SYMBOL_T )
147 {
148 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
149
150 if( !libSymbol )
151 libSymbol = symbol->GetLibSymbolRef().get();
152 else if( libSymbol != symbol->GetLibSymbolRef().get() )
153 return std::string( "" );
154 }
155 }
156
157 if( !libSymbol )
158 return std::string( "" );
159
160 wxString symbolNetlist;
161 wxArrayString pins;
162
163 for( SCH_PIN* pin : libSymbol->GetGraphicalPins( 0 /* all units */, 1 /* single bodyStyle */ ) )
164 pins.push_back( pin->GetNumber() + ' ' + pin->GetShownName() );
165
166 if( !pins.IsEmpty() )
167 symbolNetlist << EscapeString( wxJoin( pins, '\t' ), CTX_LINE );
168
169 symbolNetlist << wxS( "\r" );
170
171 wxArrayString fpFilters = libSymbol->GetFPFilters();
172
173 if( !fpFilters.IsEmpty() )
174 symbolNetlist << EscapeString( wxJoin( fpFilters, ' ' ), CTX_LINE );
175
176 symbolNetlist << wxS( "\r" );
177
178 return symbolNetlist.ToStdString();
179 };
180
181 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
182
183 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
184 {
185 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
186 m_fpEditorInstance->UpdateFrame( m_frame );
187 m_fpEditorInstance->UpdateCallback( netlistCallback );
188 }
189 else
190 {
191 PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame, netlistCallback );
192 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
193 }
194
195 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
196
197 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
198 {
199 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
200 m_urlEditorInstance->UpdateFrame( m_frame );
201 }
202 else
203 {
204 PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
205 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
206 }
207
208 Bind( wxEVT_MENU, &SCH_PROPERTIES_PANEL::onContextMenu, this, ID_CTX_ADD_FIELD );
212}
213
214
216{
217 m_unitEditorInstance->UpdateFrame( nullptr );
218 m_fpEditorInstance->UpdateFrame( nullptr );
219 m_urlEditorInstance->UpdateFrame( nullptr );
220}
221
222
224{
225 SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
226 const SELECTION& selection = selectionTool->GetSelection();
227
228 if( selection.Empty() && m_frame->IsType( FRAME_SCH_SYMBOL_EDITOR ) )
229 {
230 SYMBOL_EDIT_FRAME* symbolFrame = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
231
232 if( symbolFrame->GetCurSymbol() )
233 {
234 aFallbackSelection.Clear();
235 aFallbackSelection.Add( symbolFrame->GetCurSymbol() );
236 return aFallbackSelection;
237 }
238 }
239
240 return selection;
241}
242
243
245{
246 SELECTION fallbackSelection;
247 const SELECTION& selection = getSelection( fallbackSelection );
248
249 return selection.Empty() ? nullptr : selection.Front();
250}
251
252
254{
255 SELECTION fallbackSelection;
256 const SELECTION& selection = getSelection( fallbackSelection );
257
258 // Will actually just be updatePropertyValues() if selection hasn't changed
259 rebuildProperties( selection );
260}
261
262
264{
265 SELECTION fallbackSelection;
266 const SELECTION& selection = getSelection( fallbackSelection );
267
268 rebuildProperties( selection );
269}
270
271
273{
276
277 for( EDA_ITEM* item : aSelection )
278 {
279 if( item->Type() == SCH_JUNCTION_T )
280 {
282
283 // Dummy property which allows concurrently-selected wires to be edited
284 m_propMgr.AddProperty( new DUMMY_PROPERTY<SCH_JUNCTION, WIRE_STYLE>( _HKI( "Wire Style" ) ) )
285 .SetAvailableFunc(
286 []( INSPECTABLE* )
287 {
289 } );
290
291 // Dummy property which allows concurrently-selected wires to be edited
292 m_propMgr.AddProperty( new DUMMY_PROPERTY<SCH_JUNCTION, int>( _HKI( "Line Width" ) ) )
293 .SetAvailableFunc(
294 []( INSPECTABLE* )
295 {
297 } );
298 }
299 else if( item->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_ITEM_LOCATE_BUS_T } ) )
300 {
302
303 // Dummy property which allows concurrently-selected junctions to be edited
304 m_propMgr.AddProperty( new DUMMY_PROPERTY<SCH_LINE, int>( _HKI( "Diameter" ) ) )
305 .SetAvailableFunc(
306 []( INSPECTABLE* )
307 {
309 } );
310 }
311 }
312
314}
315
316
318{
319 SELECTION fallbackSelection;
320 const SELECTION& selection = getSelection( fallbackSelection );
321
322 if( selection.Size() != 1 || selection.Front()->Type() != SCH_SYMBOL_T )
323 return nullptr;
324
325 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( selection.Front() );
326
327 return symbol->HasEffectiveAssociatedFootprint() ? symbol : nullptr;
328}
329
330
332{
334
335 if( !symbol || !m_frame->IsType( FRAME_SCH ) )
336 return;
337
338 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( m_frame );
339
340 DIALOG_SYMBOL_PROPERTIES dlg( editFrame, symbol );
341 dlg.SelectPinMapPage();
342
343 // The dialog can subsequently invoke a KIWAY_PLAYER as a quasimodal frame, so it must run
344 // quasimodally to keep that support working.
345 int retval = dlg.ShowQuasiModal();
346
347 if( retval == SYMBOL_PROPS_EDIT_OK )
348 {
349 editFrame->OnModify();
350 AfterCommit();
351 }
352 else if( retval == SYMBOL_PROPS_WANT_SET_VARIANT_SYMBOL )
353 {
355 }
356 else if( retval == SYMBOL_PROPS_WANT_CLEAR_VARIANT_SYMBOL )
357 {
359 }
360}
361
362
363wxPGProperty* SCH_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProperty ) const
364{
365 wxPGProperty* prop = PGPropertyFactory( aProperty, m_frame );
366
367 if( auto colorProp = dynamic_cast<PGPROPERTY_COLOR4D*>( prop ) )
368 {
369 COLOR4D bg = m_frame->GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
370 colorProp->SetBackgroundColor( bg );
371 }
372
374 prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
375 else if( aProperty->Name() == GetDefaultFieldName( FIELD_T::DATASHEET, UNTRANSLATED ) )
376 prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
377
378 return prop;
379}
380
381
382PROPERTY_BASE* SCH_PROPERTIES_PANEL::getPropertyFromEvent( const wxPropertyGridEvent& aEvent ) const
383{
384 EDA_ITEM* item = const_cast<SCH_PROPERTIES_PANEL*>( this )->getFrontItem();
385
386 if( !item || !item->IsSCH_ITEM() )
387 return nullptr;
388
389 SCH_ITEM* firstItem = static_cast<SCH_ITEM*>( item );
390
391 wxCHECK_MSG( firstItem, nullptr, wxT( "getPropertyFromEvent for a property with nothing selected!") );
392
393 PROPERTY_BASE* property = m_propMgr.GetProperty( firstItem, aEvent.GetPropertyName() );
394 wxCHECK_MSG( property, nullptr, wxT( "getPropertyFromEvent for a property not found on the selected item!" ) );
395
396 return property;
397}
398
399
400void SCH_PROPERTIES_PANEL::valueChanging( wxPropertyGridEvent& aEvent )
401{
403 return;
404
405 EDA_ITEM* frontItem = getFrontItem();
406
407 if( !frontItem )
408 return;
409
410 if( PROPERTY_BASE* property = getPropertyFromEvent( aEvent ) )
411 {
412 wxVariant newValue = aEvent.GetPropertyValue();
413
414 if( VALIDATOR_RESULT validationFailure = property->Validate( newValue.GetAny(), frontItem ) )
415 {
416 wxString errorMsg = wxString::Format( wxS( "%s: %s" ), wxGetTranslation( property->Name() ),
417 validationFailure->get()->Format( m_frame ) );
418 m_frame->ShowInfoBarError( errorMsg );
419 aEvent.Veto();
420 return;
421 }
422
423 aEvent.Skip();
424 }
425}
426
427
428void SCH_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
429{
431 return;
432
433 SELECTION fallbackSelection;
434 const SELECTION& selection = getSelection( fallbackSelection );
435
436 wxCHECK( getPropertyFromEvent( aEvent ), /* void */ );
437
438 wxVariant newValue = aEvent.GetPropertyValue();
439 SCH_COMMIT changes( m_frame );
440 SCH_SCREEN* screen = m_frame->GetScreen();
441
442 PROPERTY_COMMIT_HANDLER handler( &changes );
443
444 for( EDA_ITEM* edaItem : selection )
445 {
446 if( !edaItem->IsSCH_ITEM() )
447 continue;
448
449 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
450 PROPERTY_BASE* property = m_propMgr.GetProperty( item, aEvent.GetPropertyName() );
451 wxCHECK2( property, continue );
452
453 // Editing reference text in the schematic must go through the parent symbol in order to handle
454 // symbol instance data properly.
455 if( item->Type() == SCH_FIELD_T && static_cast<SCH_FIELD*>( item )->GetId() == FIELD_T::REFERENCE
456 && m_frame->IsType( FRAME_SCH )
457 && property->Name() == wxT( "Text" ) )
458 {
459 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParentSymbol() );
460 wxCHECK2( symbol, continue );
461
462 changes.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
463 symbol->SetRefProp( newValue.GetString() );
464 symbol->SyncOtherUnits( symbol->Schematic()->CurrentSheet(), changes, property );
465 continue;
466 }
467
468 // Editing field text in the schematic when a variant is active must use variant-aware
469 // SetText to properly store the value as a variant override.
470 if( item->Type() == SCH_FIELD_T
471 && m_frame->IsType( FRAME_SCH )
472 && property->Name() == wxT( "Text" ) )
473 {
474 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
475 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item->GetParentSymbol() );
476
477 if( symbol && symbol->Schematic() )
478 {
479 wxString variantName = symbol->Schematic()->GetCurrentVariant();
480
481 if( !variantName.IsEmpty() )
482 {
483 changes.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
484 field->SetText( newValue.GetString(), &symbol->Schematic()->CurrentSheet(), variantName );
485 symbol->SyncOtherUnits( symbol->Schematic()->CurrentSheet(), changes, nullptr, variantName );
486 continue;
487 }
488 }
489 }
490
491 // Changing a sheet's filename field requires file operations to match the dialog behavior.
492 if( item->Type() == SCH_FIELD_T
493 && m_frame->IsType( FRAME_SCH )
494 && property->Name() == wxT( "Text" ) )
495 {
496 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
497 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item->GetParent() );
498
499 if( sheet && field->GetId() == FIELD_T::SHEET_FILENAME )
500 {
501 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( m_frame );
502
503 if( !handleSheetFilenameChange( editFrame, sheet, changes, newValue.GetString() ) )
504 {
505 UpdateData();
506 return;
507 }
508
509 continue;
510 }
511 }
512
513 if( item->Type() == SCH_TABLECELL_T )
514 changes.Modify( item->GetParent(), screen, RECURSE_MODE::NO_RECURSE );
515 else
516 changes.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
517
518 item->Set( property, newValue );
519
520 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
521 {
522 symbol->SyncOtherUnits( symbol->Schematic()->CurrentSheet(), changes, property,
523 symbol->Schematic()->GetCurrentVariant() );
524 }
525 }
526
527 changes.Push( _( "Edit Properties" ) );
528
529 // Force a repaint of the items whose properties were changed
530 // This is necessary to update field displays in the schematic view
531 for( EDA_ITEM* edaItem : selection )
532 m_frame->UpdateItem( edaItem );
533
534 // Perform grid updates as necessary based on value change
535 AfterCommit();
536
537 aEvent.Skip();
538}
539
540
542 SCH_COMMIT& aChanges,
543 const wxString& aNewFilename )
544{
545 wxString newFilename = EnsureFileExtension( aNewFilename, FILEEXT::KiCadSchematicFileExtension );
546
547 if( newFilename.IsEmpty() || !IsFullFileNameValid( newFilename ) )
548 {
549 DisplayError( aFrame, _( "A sheet must have a valid file name." ) );
550 return false;
551 }
552
553 // Normalize separators to unix notation
554 newFilename.Replace( wxT( "\\" ), wxT( "/" ) );
555
556 wxString oldFilename = aSheet->GetFileName();
557 oldFilename.Replace( wxT( "\\" ), wxT( "/" ) );
558
559 if( newFilename == oldFilename )
560 return true;
561
562 if( !aFrame->ChangeSheetFile( aSheet, newFilename ) )
563 return false;
564
565 SCH_SCREEN* currentScreen = aFrame->GetCurrentSheet().LastScreen();
566 aChanges.Modify( aSheet, currentScreen, RECURSE_MODE::NO_RECURSE );
567 aSheet->SetFileName( newFilename );
568
569 return true;
570}
571
572
573bool SCH_PROPERTIES_PANEL::isKeyEditable( const wxPGProperty* aPGProp ) const
574{
575 PROPERTY_BASE* prop = static_cast<PROPERTY_BASE*>( aPGProp->GetClientData() );
576
577 if( !prop )
578 return false;
579
580 EDA_ITEM* item = const_cast<SCH_PROPERTIES_PANEL*>( this )->getFrontItem();
581
582 if( !item )
583 return false;
584
585 if( prop->Group() == _HKI( "Custom Properties" ) )
586 return true;
587
588 if( item->Type() == SCH_SYMBOL_T )
589 {
590 SCH_FIELD* field = static_cast<SCH_SYMBOL*>( item )->GetField( prop->Name() );
591 return field && !field->IsMandatory();
592 }
593 else if( item->Type() == SCH_SHEET_T )
594 {
595 SCH_FIELD* field = static_cast<SCH_SHEET*>( item )->GetField( prop->Name() );
596 return field && !field->IsMandatory();
597 }
598
599 return false;
600}
601
602
603bool SCH_PROPERTIES_PANEL::isKeyNameInUse( const wxString& aName ) const
604{
605 EDA_ITEM* item = const_cast<SCH_PROPERTIES_PANEL*>( this )->getFrontItem();
606
607 if( !item )
608 return false;
609
610 return m_propMgr.GetProperty( item, aName ) != nullptr;
611}
612
613
614void SCH_PROPERTIES_PANEL::onKeyRenamed( const wxString& aOldName, const wxString& aNewName )
615{
616 SELECTION fallbackSelection;
617 const SELECTION& selection = getSelection( fallbackSelection );
618
619 SCH_COMMIT changes( m_frame );
620 SCH_SCREEN* screen = m_frame->GetScreen();
621 PROPERTY_COMMIT_HANDLER handler( &changes );
622
623 for( EDA_ITEM* edaItem : selection )
624 {
625 if( !edaItem->IsSCH_ITEM() )
626 continue;
627
628 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
629 bool renamed = false;
630
631 if( item->Type() == SCH_SYMBOL_T )
632 {
633 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
634 SCH_FIELD* field = symbol->GetField( aOldName );
635
636 if( field && !field->IsMandatory() )
637 {
638 changes.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
639 field->SetName( aNewName );
640 renamed = true;
641 }
642 }
643 else if( item->Type() == SCH_SHEET_T )
644 {
645 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
646 SCH_FIELD* field = sheet->GetField( aOldName );
647
648 if( field && !field->IsMandatory() )
649 {
650 changes.Modify( sheet, screen, RECURSE_MODE::NO_RECURSE );
651 field->SetName( aNewName );
652 renamed = true;
653 }
654 }
655
656 if( !renamed )
657 {
658 if( wxString value; item->GetCustomProperty( aOldName, value ) )
659 {
660 changes.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
661 item->RemoveCustomProperty( aOldName );
662 item->SetCustomProperty( aNewName, value );
663 }
664 }
665 }
666
667 changes.Push( _( "Rename Property" ) );
668
669 AfterCommit();
670}
671
672
673bool SCH_PROPERTIES_PANEL::buildContextMenu( wxMenu& aMenu, wxPGProperty* aPGProp )
674{
675 if( aPGProp->IsCategory() )
676 {
677 if( aPGProp->GetLabel() == wxGetTranslation( _HKI( "Fields" ) ) )
678 aMenu.Append( ID_CTX_ADD_FIELD, _( "Add Field" ) );
679 else if( aPGProp->GetLabel() == wxGetTranslation( _HKI( "Custom Properties" ) ) )
680 aMenu.Append( ID_CTX_ADD_CUSTOM_PROPERTY, _( "Add Custom Property" ) );
681 }
682 else
683 {
684 PROPERTY_BASE* prop = static_cast<PROPERTY_BASE*>( aPGProp->GetClientData() );
685
686 if( !prop )
687 return false;
688
689 if( prop->Group() == _HKI( "Fields" ) )
690 {
691 if( isKeyEditable( aPGProp ) )
692 aMenu.Append( ID_CTX_REMOVE_FIELD, _( "Remove Field" ) );
693
694 aMenu.Append( ID_CTX_ADD_FIELD, _( "Add Field" ) );
695 }
696 else if( prop->Group() == _HKI( "Custom Properties" ) )
697 {
698 aMenu.Append( ID_CTX_REMOVE_CUSTOM_PROPERTY, _( "Remove Custom Property" ) );
699 aMenu.Append( ID_CTX_ADD_CUSTOM_PROPERTY, _( "Add Custom Property" ) );
700 }
701 }
702
703 return aMenu.GetMenuItemCount() > 0;
704}
705
706
707void SCH_PROPERTIES_PANEL::onContextMenu( wxCommandEvent& aEvent )
708{
709 switch( aEvent.GetId() )
710 {
711 case ID_CTX_ADD_FIELD: addBlankField(); break;
715 default: break;
716 }
717}
718
719
721{
722 SELECTION fallbackSelection;
723 const SELECTION& selection = getSelection( fallbackSelection );
724
726
727 if( selection.Empty() )
728 return;
729
730 wxString name;
731
732 for( int n = 0; ; ++n )
733 {
735 bool used = false;
736
737 for( EDA_ITEM* item : selection )
738 {
739 if( item->Type() == SCH_SYMBOL_T && static_cast<SCH_SYMBOL*>( item )->GetField( name ) )
740 used = true;
741 else if( item->Type() == SCH_SHEET_T && static_cast<SCH_SHEET*>( item )->GetField( name ) )
742 used = true;
743 }
744
745 if( !used )
746 break;
747 }
748
749 SCH_COMMIT changes( m_frame );
750 SCH_SCREEN* screen = m_frame->GetScreen();
751 PROPERTY_COMMIT_HANDLER handler( &changes );
752
753 for( EDA_ITEM* edaItem : selection )
754 {
755 if( !edaItem->IsSCH_ITEM() )
756 continue;
757
758 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
759
760 if( item->Type() == SCH_SYMBOL_T )
761 {
762 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
763 SCH_FIELD newField( symbol, FIELD_T::USER, name );
764
765 newField.SetVisible( false );
766 changes.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
767 symbol->AddField( newField );
768 }
769 else if( item->Type() == SCH_SHEET_T )
770 {
771 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
772 SCH_FIELD newField( sheet, FIELD_T::USER, name );
773
774 newField.SetVisible( false );
775 changes.Modify( sheet, screen, RECURSE_MODE::NO_RECURSE );
776 sheet->AddField( newField );
777 }
778 }
779
780 changes.Push( _( "Add Field" ) );
781 AfterCommit();
782
784
785 beginLabelEdit( name, true );
786}
787
788
790{
791 SELECTION fallbackSelection;
792 const SELECTION& selection = getSelection( fallbackSelection );
793
795
796 if( selection.Empty() )
797 return;
798
799 wxString name;
800
801 for( int n = 0; ; ++n )
802 {
803 name = wxString::Format( wxS( "Property%d" ), n );
804 bool used = false;
805
806 for( EDA_ITEM* item : selection )
807 {
808 if( wxString dummy; item->GetCustomProperty( name, dummy ) )
809 {
810 used = true;
811 break;
812 }
813 }
814
815 if( !used )
816 break;
817 }
818
819 SCH_COMMIT changes( m_frame );
820 SCH_SCREEN* screen = m_frame->GetScreen();
821 PROPERTY_COMMIT_HANDLER handler( &changes );
822
823 for( EDA_ITEM* edaItem : selection )
824 {
825 if( !edaItem->IsSCH_ITEM() )
826 continue;
827
828 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
829
830 changes.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
831 item->SetCustomProperty( name, wxEmptyString );
832 }
833
834 changes.Push( _( "Add Custom Property" ) );
835 AfterCommit();
836
838
839 beginLabelEdit( name, true );
840}
841
842
843void SCH_PROPERTIES_PANEL::removeField( const wxString& aName )
844{
845 SELECTION fallbackSelection;
846 const SELECTION& selection = getSelection( fallbackSelection );
847
848 SCH_COMMIT changes( m_frame );
849 SCH_SCREEN* screen = m_frame->GetScreen();
850 PROPERTY_COMMIT_HANDLER handler( &changes );
851
852 for( EDA_ITEM* edaItem : selection )
853 {
854 if( !edaItem->IsSCH_ITEM() )
855 continue;
856
857 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
858
859 if( item->Type() == SCH_SYMBOL_T )
860 {
861 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
862 SCH_FIELD* field = symbol->GetField( aName );
863
864 if( field && !field->IsMandatory() )
865 {
866 changes.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
867 symbol->RemoveField( aName );
868 }
869 }
870 else if( item->Type() == SCH_SHEET_T )
871 {
872 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
873 auto& fields = sheet->GetFields();
874
875 for( auto it = fields.begin(); it != fields.end(); ++it )
876 {
877 if( !it->IsMandatory() && it->GetName() == aName )
878 {
879 changes.Modify( sheet, screen, RECURSE_MODE::NO_RECURSE );
880 fields.erase( it );
881 break;
882 }
883 }
884 }
885 }
886
887 changes.Push( _( "Remove Field" ) );
888 AfterCommit();
889}
890
891
893{
894 SELECTION fallbackSelection;
895 const SELECTION& selection = getSelection( fallbackSelection );
896
897 SCH_COMMIT changes( m_frame );
898 SCH_SCREEN* screen = m_frame->GetScreen();
899 PROPERTY_COMMIT_HANDLER handler( &changes );
900
901 for( EDA_ITEM* edaItem : selection )
902 {
903 if( !edaItem->IsSCH_ITEM() )
904 continue;
905
906 SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
907
908 changes.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
909 item->RemoveCustomProperty( aName );
910 }
911
912 changes.Push( _( "Remove Custom Property" ) );
913 AfterCommit();
914}
915
916
918{
919 // Note: currently assuming that any newly-created item from the panel is either a field or
920 // a custom property because those are the types we currently support.
921 if( EDA_ITEM* item = getFrontItem() )
922 {
923 if( item->Type() == SCH_SYMBOL_T && static_cast<SCH_SYMBOL*>( item )->GetField( aKey ) )
924 removeField( aKey );
925 else if( item->Type() == SCH_SHEET_T && static_cast<SCH_SHEET*>( item )->GetField( aKey ) )
926 removeField( aKey );
927 else
928 removeCustomProperty( aKey );
929 }
930}
931
932
933void SCH_PROPERTIES_PANEL::OnLanguageChanged( wxCommandEvent& aEvent )
934{
936
937 aEvent.Skip();
938}
939
940
941bool SCH_PROPERTIES_PANEL::getItemValue( EDA_ITEM* aItem, PROPERTY_BASE* aProperty, wxVariant& aValue )
942{
943 // For SCH_FIELD "Text" property, return the variant-aware value when a variant is active
944 if( aItem->Type() == SCH_FIELD_T
945 && m_frame->IsType( FRAME_SCH )
946 && aProperty->Name() == wxT( "Text" ) )
947 {
948 SCH_FIELD* field = static_cast<SCH_FIELD*>( aItem );
949 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( field->GetParentSymbol() );
950
951 if( symbol && symbol->Schematic() )
952 {
953 wxString variantName = symbol->Schematic()->GetCurrentVariant();
954
955 if( !variantName.IsEmpty() )
956 {
957 wxString text = field->GetText( &symbol->Schematic()->CurrentSheet(), variantName );
958 aValue = wxVariant( text );
959 return true;
960 }
961 }
962 }
963
964 return PROPERTIES_PANEL::getItemValue( aItem, aProperty, aValue );
965}
const char * name
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
Dialog used to edit SCH_SYMBOL objects in a schematic.
void SelectPinMapPage()
Select the Pin Map page so the dialog opens directly on the pin-to-pad mapping editor (issue #2282).
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
bool GetCustomProperty(const wxString &aKey, wxString &aValue) const
Definition eda_item.cpp:188
void RemoveCustomProperty(const wxString &aKey)
Definition eda_item.cpp:161
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM * GetParent() const
Definition eda_item.h:112
void SetCustomProperty(const wxString &aKey, const wxString &aValue)
Definition eda_item.h:255
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue, bool aNotify=true)
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
bool IsSCH_ITEM() const
Definition view_item.h:97
Define a library symbol object.
Definition lib_symbol.h:119
std::vector< const SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
wxArrayString GetFPFilters() const
Definition lib_symbol.h:247
static const wxString EDITOR_NAME
Definition pg_editors.h:75
static const wxString EDITOR_NAME
Definition pg_editors.h:91
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
wxString m_contextMenuPropertyName
PROPERTIES_PANEL(wxWindow *aParent, EDA_BASE_FRAME *aFrame)
void beginLabelEdit(const wxString &aKey, bool aStartBlank=false)
void settlePendingLabelEdit()
Synchronously ends any in-progress label edit; potentially canceling a newly added row.
virtual void OnLanguageChanged(wxCommandEvent &aEvent)
virtual bool getItemValue(EDA_ITEM *aItem, PROPERTY_BASE *aProperty, wxVariant &aValue)
Utility to fetch a property value and convert to wxVariant Precondition: aItem is known to have prope...
virtual void rebuildProperties(const SELECTION &aSelection)
Generates the property grid for a given selection of items.
void addCategoryButton(const wxString &aGroupKey, const wxString &aTooltip, BITMAPS aBitmap, std::function< void()> aAction, std::function< bool()> aEnableFunc=nullptr, bool aForceCategory=false)
Registers an overlay button on a category caption row.
wxString m_pendingNewKey
Key of a freshly-added blank field/custom property awaiting a name from the user.
wxString Group() const
Definition property.h:365
const wxString & Name() const
Definition property.h:221
Provide class metadata.Helper macro to map type hashes to names.
wxString GetCurrentVariant() const
Return the current variant being edited.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:303
static TOOL_ACTION clearVariantSymbol
static TOOL_ACTION setVariantSymbol
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag and update other data struc...
SCH_SHEET_PATH & GetCurrentSheet() const
bool ChangeSheetFile(SCH_SHEET *aSheet, const wxString &aNewFilename, bool *aClearAnnotationNewItems=nullptr, bool *aIsUndoable=nullptr, const wxString *aSourceSheetFilename=nullptr)
Change the file backing a schematic sheet.
Definition sheet.cpp:175
bool IsMandatory() const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
FIELD_T GetId() const
Definition sch_field.h:142
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:287
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:281
bool getItemValue(EDA_ITEM *aItem, PROPERTY_BASE *aProperty, wxVariant &aValue) override
Utility to fetch a property value and convert to wxVariant Precondition: aItem is known to have prope...
void onContextMenu(wxCommandEvent &aEvent)
bool buildContextMenu(wxMenu &aMenu, wxPGProperty *aPGProp) override
bool isKeyEditable(const wxPGProperty *aPGProp) const override
wxPGProperty * createPGProperty(const PROPERTY_BASE *aProperty) const override
PROPERTY_MANAGER & m_propMgr
void removeCustomProperty(const wxString &aName)
PG_CHECKBOX_EDITOR * m_checkboxEditorInstance
PG_FPID_EDITOR * m_fpEditorInstance
const SELECTION & getSelection(SELECTION &aFallbackSelection)
Get the current selection from the selection tool.
void onEditPinMap()
Open the symbol properties dialog on its Pin Map page for the single selected symbol (issue #2282).
void valueChanging(wxPropertyGridEvent &aEvent) override
PG_UNIT_EDITOR * m_unitEditorInstance
bool isKeyNameInUse(const wxString &aName) const override
void removeField(const wxString &aName)
void valueChanged(wxPropertyGridEvent &aEvent) override
void onKeyRenamed(const wxString &aOldName, const wxString &aNewName) override
SCH_PROPERTIES_PANEL(wxWindow *aParent, SCH_BASE_FRAME *aFrame)
void onNewItemLeftBlank(const wxString &aKey) override
PG_COLOR_EDITOR * m_colorEditorInstance
EDA_ITEM * getFrontItem()
Get the front item of the current selection.
PROPERTY_BASE * getPropertyFromEvent(const wxPropertyGridEvent &aEvent) const
void OnLanguageChanged(wxCommandEvent &aEvent) override
bool handleSheetFilenameChange(SCH_EDIT_FRAME *aFrame, SCH_SHEET *aSheet, SCH_COMMIT &aChanges, const wxString &aNewFilename)
void rebuildProperties(const SELECTION &aSelection) override
Generates the property grid for a given selection of items.
PG_URL_EDITOR * m_urlEditorInstance
SCH_SYMBOL * getSinglePinMappedSymbol()
SCH_SELECTION & GetSelection()
SCH_SCREEN * LastScreen()
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:390
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:384
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:91
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a @aField to the list of fields.
Schematic symbol object.
Definition sch_symbol.h:75
void SetRefProp(const wxString &aRef)
void RemoveField(const wxString &aFieldName)
Remove a user field from the symbol.
void SyncOtherUnits(const SCH_SHEET_PATH &aSourceSheet, SCH_COMMIT &aCommit, PROPERTY_BASE *aProperty, const wxString &aVariantName=wxEmptyString)
Keep fields other than the reference, include/exclude flags, and alternate pin assignments in sync in...
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
bool HasEffectiveAssociatedFootprint() const
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
virtual void Add(EDA_ITEM *aItem)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
EDA_ITEM * Front() const
Definition selection.h:176
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:97
int Size() const
Returns the number of selected parts.
Definition selection.h:120
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
The symbol library editor main window.
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
wxString EnsureFileExtension(const wxString &aFilename, const wxString &aExtension)
It's annoying to throw up nag dialogs when the extension isn't right.
Definition common.cpp:848
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.
@ SYMBOL_PROPS_WANT_SET_VARIANT_SYMBOL
@ SYMBOL_PROPS_EDIT_OK
@ SYMBOL_PROPS_WANT_CLEAR_VARIANT_SYMBOL
#define _(s)
@ NO_RECURSE
Definition eda_item.h:52
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
@ FRAME_SCH
Definition frame_type.h:30
static const std::string KiCadSchematicFileExtension
@ LAYER_SCHEMATIC_BACKGROUND
Definition layer_ids.h:510
#define _HKI(x)
Definition page_info.cpp:40
wxPGProperty * PGPropertyFactory(const PROPERTY_BASE *aProperty, EDA_DRAW_FRAME *aFrame)
Customized abstract wxPGProperty class to handle coordinate/size units.
see class PGM_BASE
APIIMPORT wxPGGlobalVarsClass * wxPGGlobalVars
@ ID_CTX_REMOVE_CUSTOM_PROPERTY
@ ID_CTX_REMOVE_FIELD
@ ID_CTX_ADD_CUSTOM_PROPERTY
@ ID_CTX_ADD_FIELD
std::optional< std::unique_ptr< VALIDATION_ERROR > > VALIDATOR_RESULT
Null optional means validation succeeded.
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
bool IsFullFileNameValid(const wxString &aFullFilename)
Checks if a full filename is valid, i.e.
@ CTX_LINE
wxString GetUserFieldName(int aFieldNdx, TRANSLATION aTranslation)
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ UNTRANSLATED
KIBIS_PIN * pin
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_TABLECELL_T
Definition typeinfo.h:162
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_JUNCTION_T
Definition typeinfo.h:155
Definition of file extensions used in Kicad.