KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_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-2023 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 along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include <font/fontconfig.h>
26#include <frame_type.h>
27#include <pgm_base.h>
28#include <pcb_base_edit_frame.h>
29#include <tool/tool_manager.h>
30#include <tools/pcb_actions.h>
32#include <eda_units.h>
36#include <board_commit.h>
38#include <board.h>
40#include <properties/property.h>
41#include <pcb_shape.h>
42#include <pcb_text.h>
43#include <pcb_track.h>
44#include <pcb_generator.h>
46#include <pad.h>
47#include <footprint.h>
48#include <pcb_field.h>
49#include <template_fieldnames.h>
51#include <string_utils.h>
53#include <widgets/ui_common.h>
54#include <widgets/unit_binder.h>
55
56#include <memory>
57#include <vector>
58#include <wx/combobox.h>
59
60static const wxString MISSING_FIELD_SENTINEL = wxS( "\uE000" );
61
63{
64public:
65 PCB_FOOTPRINT_FIELD_PROPERTY( const wxString& aName ) :
66 PROPERTY_BASE( aName ),
67 m_name( aName )
68 {
69 }
70
71 size_t OwnerHash() const override { return TYPE_HASH( FOOTPRINT ); }
72 size_t BaseHash() const override { return TYPE_HASH( FOOTPRINT ); }
73 size_t TypeHash() const override { return TYPE_HASH( wxString ); }
74
75 bool Writeable( INSPECTABLE* aObject ) const override
76 {
77 return PROPERTY_BASE::Writeable( aObject );
78 }
79
80 void setter( void* obj, wxAny& v ) override
81 {
82 wxString value;
83
84 if( !v.GetAs( &value ) )
85 return;
86
87 FOOTPRINT* footprint = reinterpret_cast<FOOTPRINT*>( obj );
88 PCB_FIELD* field = footprint->GetField( m_name );
89
90 wxString variantName;
91
92 if( footprint->GetBoard() )
93 variantName = footprint->GetBoard()->GetCurrentVariant();
94
95 if( !variantName.IsEmpty() )
96 {
97 // Store the value as a variant override
98 FOOTPRINT_VARIANT* variant = footprint->AddVariant( variantName );
99
100 if( variant )
101 variant->SetFieldValue( m_name, value );
102 }
103 else
104 {
105 // Set the base field value
106 if( !field )
107 {
108 PCB_FIELD* newField = new PCB_FIELD( footprint, FIELD_T::USER, m_name );
109 newField->SetText( value );
110 footprint->Add( newField );
111 }
112 else
113 {
114 field->SetText( value );
115 }
116 }
117 }
118
119 wxAny getter( const void* obj ) const override
120 {
121 const FOOTPRINT* footprint = reinterpret_cast<const FOOTPRINT*>( obj );
122 PCB_FIELD* field = footprint->GetField( m_name );
123
124 if( field )
125 {
126 wxString variantName;
127
128 if( footprint->GetBoard() )
129 variantName = footprint->GetBoard()->GetCurrentVariant();
130
131 wxString text;
132
133 if( !variantName.IsEmpty() )
134 text = footprint->GetFieldValueForVariant( variantName, m_name );
135 else
136 text = field->GetText();
137
138 return wxAny( text );
139 }
140 else
141 {
142 return wxAny( MISSING_FIELD_SENTINEL );
143 }
144 }
145
146private:
147 wxString m_name;
148};
149
151
152
153class PG_NET_SELECTOR_EDITOR : public wxPGEditor
154{
155public:
156 static const wxString EDITOR_NAME;
157
159
160 wxString GetName() const override { return EDITOR_NAME; }
161
162 wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty, const wxPoint& aPos,
163 const wxSize& aSize ) const override
164 {
165 NET_SELECTOR* editor = new NET_SELECTOR( aGrid->GetPanel(), wxID_ANY, aPos, aSize, 0 );
166
167 // wxPropertyGrid registers editors globally and the same PG_NET_SELECTOR_EDITOR
168 // instance is reused by every PCB_PROPERTIES_PANEL (board editor, footprint editor).
169 // Resolve the owning panel -- and therefore the live frame and board -- from the
170 // grid at use time instead of caching frame state on the editor. This avoids
171 // cross-frame state corruption and nullptr derefs when one panel is destroyed while
172 // another is still live.
173 if( PCB_PROPERTIES_PANEL* panel = dynamic_cast<PCB_PROPERTIES_PANEL*>( aGrid->GetParent() ) )
174 {
175 if( PCB_BASE_EDIT_FRAME* frame = panel->GetFrame() )
176 {
177 if( BOARD* board = frame->GetBoard() )
178 editor->SetNetInfo( &board->GetNetInfo() );
179 }
180 }
181
182 editor->SetIndeterminateString( INDETERMINATE_STATE );
183 UpdateControl( aProperty, editor );
184
185 editor->Bind( FILTERED_ITEM_SELECTED,
186 [=]( wxCommandEvent& aEvt )
187 {
188 auto& choices = const_cast<wxPGChoices&>( aProperty->GetChoices() );
189 wxString netname = editor->GetSelectedNetname();
190
191 if( choices.Index( netname ) == wxNOT_FOUND )
192 choices.Add( netname, editor->GetSelectedNetcode() );
193
194 wxVariant val( editor->GetSelectedNetcode() );
195 aGrid->ChangePropertyValue( aProperty, val );
196 } );
197
198 return editor;
199 }
200
201 void UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const override
202 {
203 if( NET_SELECTOR* editor = dynamic_cast<NET_SELECTOR*>( aCtrl ) )
204 {
205 if( aProperty->IsValueUnspecified() )
206 editor->SetIndeterminate();
207 else
208 editor->SetSelectedNetcode( (int) aProperty->GetValue().GetLong() );
209 }
210 }
211
212 bool GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty, wxWindow* aCtrl ) const override
213 {
214 NET_SELECTOR* editor = dynamic_cast<NET_SELECTOR*>( aCtrl );
215
216 if( !editor )
217 return false;
218
219 aVariant = static_cast<long>( editor->GetSelectedNetcode() );
220 return true;
221 }
222
223 bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aWindow, wxEvent& aEvent ) const override
224 {
225 return false;
226 }
227};
228
229
230const wxString PG_NET_SELECTOR_EDITOR::EDITOR_NAME = wxS( "PG_NET_SELECTOR_EDITOR" );
231
232
233class PG_TRACK_WIDTH_EDITOR : public wxPGEditor
234{
235public:
236 static const wxString EDITOR_NAME;
237
239 m_frame( aFrame )
240 {
241 if( m_frame )
242 {
243 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
244 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
245 }
246
248 }
249
250 wxString GetName() const override { return m_editorName; }
251
252 static wxString BuildEditorName( PCB_BASE_EDIT_FRAME* aFrame )
253 {
254 if( !aFrame )
255 return EDITOR_NAME + "NoFrame";
256
257 return EDITOR_NAME + aFrame->GetName();
258 }
259
261 {
262 m_frame = aFrame;
263
264 if( m_frame )
265 {
266 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
267 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
268 }
269 else
270 {
271 m_unitBinder = nullptr;
272 }
273 }
274
275 wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty, const wxPoint& aPos,
276 const wxSize& aSize ) const override
277 {
278 wxASSERT( m_unitBinder );
279
280 wxComboBox* editor = new wxComboBox( aGrid->GetPanel(), wxID_ANY, wxEmptyString, aPos, aSize, 0, nullptr,
281 wxCB_DROPDOWN | wxTE_PROCESS_ENTER );
282
283 m_unitBinder->SetControl( editor );
284 m_unitBinder->RequireEval();
285 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
286
288 UpdateControl( aProperty, editor );
289
290 std::shared_ptr<bool> popupShown = std::make_shared<bool>( false );
291 auto commitValue =
292 [this, aGrid, aProperty]()
293 {
294 wxVariant val( static_cast<long>( m_unitBinder->GetValue() ) );
295 aGrid->ChangePropertyValue( aProperty, val );
296 };
297
298 editor->Bind( wxEVT_COMBOBOX_DROPDOWN,
299 [popupShown]( wxCommandEvent& aEvent )
300 {
301 *popupShown = true;
302 aEvent.Skip();
303 } );
304
305 editor->Bind( wxEVT_COMBOBOX,
306 [commitValue, popupShown]( wxCommandEvent& aEvent )
307 {
308 // Choosing a preset from the dropdown should apply that preset immediately.
309 if( *popupShown )
310 commitValue();
311
312 aEvent.Skip();
313 } );
314
315 editor->Bind( wxEVT_COMBOBOX_CLOSEUP,
316 [aGrid, popupShown]( wxCommandEvent& aEvent )
317 {
318 aGrid->CallAfter( [popupShown]()
319 {
320 *popupShown = false;
321 } );
322
323 aEvent.Skip();
324 } );
325
326 editor->Bind( wxEVT_CHAR_HOOK,
327 [commitValue, popupShown]( wxKeyEvent& aEvent )
328 {
329 // Pressing Enter after typing a custom value should apply the typed value,
330 // not the first preset in the dropdown.
331 if( ( aEvent.GetKeyCode() == WXK_RETURN
332 || aEvent.GetKeyCode() == WXK_NUMPAD_ENTER )
333 && !*popupShown )
334 {
335 commitValue();
336 return;
337 }
338
339 aEvent.Skip();
340 } );
341
342 editor->Bind( wxEVT_KILL_FOCUS,
343 [commitValue, popupShown]( wxFocusEvent& aEvent )
344 {
345 // Clicking into another property cell should keep any typed custom value.
346 if( !*popupShown )
347 commitValue();
348
349 aEvent.Skip();
350 } );
351
352 return wxPGWindowList( editor, nullptr );
353 }
354
355 void UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const override
356 {
357 if( !m_unitBinder )
358 return;
359
360 wxComboBox* editor = dynamic_cast<wxComboBox*>( aCtrl );
361 wxCHECK( editor, /* void */ );
362
363 if( aProperty->IsValueUnspecified() )
364 m_unitBinder->ChangeValue( INDETERMINATE_STATE );
365 else
366 m_unitBinder->ChangeValue( aProperty->GetValue().GetLong() );
367 }
368
369 bool GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty, wxWindow* aCtrl ) const override
370 {
371 if( !m_unitBinder )
372 return false;
373
374 wxComboBox* editor = dynamic_cast<wxComboBox*>( aCtrl );
375 wxCHECK_MSG( editor, false, "PG_TRACK_WIDTH_EDITOR requires a combo box!" );
376
377 if( editor->GetValue() == INDETERMINATE_STATE )
378 {
379 aVariant.MakeNull();
380 return true;
381 }
382
383 long result = static_cast<long>( m_unitBinder->GetValue() );
384 bool changed = aVariant.IsNull() || result != aVariant.GetLong();
385
386 if( changed )
387 aVariant = result;
388
389 return changed;
390 }
391
392 bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aWindow, wxEvent& aEvent ) const override
393 {
394 return false;
395 }
396
397private:
398 void setTrackWidthOptions( wxComboBox* aEditor ) const
399 {
400 std::vector<long long int> trackWidths;
401
402 // 0 is the netclass place-holder.
403 for( unsigned ii = 1; ii < m_frame->GetDesignSettings().m_TrackWidthList.size(); ++ii )
404 trackWidths.push_back( m_frame->GetDesignSettings().m_TrackWidthList[ii] );
405
406 m_unitBinder->SetOptionsList( trackWidths );
407
408 wxString unitLabel = EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() );
409
410 for( unsigned ii = 0; ii < aEditor->GetCount(); ++ii )
411 aEditor->SetString( ii, aEditor->GetString( ii ) + wxS( " " ) + unitLabel );
412 }
413
415 std::unique_ptr<PROPERTY_EDITOR_UNIT_BINDER> m_unitBinder;
416 wxString m_editorName;
417};
418
419
420const wxString PG_TRACK_WIDTH_EDITOR::EDITOR_NAME = wxS( "PG_TRACK_WIDTH_EDITOR" );
421
422
424 PROPERTIES_PANEL( aParent, aFrame ),
425 m_frame( aFrame ),
426 m_propMgr( PROPERTY_MANAGER::Instance() )
427{
428 m_propMgr.Rebuild();
429 bool found = false;
430
431 wxASSERT( wxPGGlobalVars );
432
433 wxString editorKey = PG_UNIT_EDITOR::BuildEditorName( m_frame );
434
435 auto it = wxPGGlobalVars->m_mapEditorClasses.find( editorKey );
436
437 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
438 {
439 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( it->second );
440 m_unitEditorInstance->UpdateFrame( m_frame );
441 found = true;
442 }
443
444 if( !found )
445 {
446 PG_UNIT_EDITOR* new_editor = new PG_UNIT_EDITOR( m_frame );
447 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( wxPropertyGrid::RegisterEditorClass( new_editor ) );
448 }
449
450 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_TRACK_WIDTH_EDITOR::BuildEditorName( m_frame ) );
451
452 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
453 {
454 m_trackWidthEditorInstance = static_cast<PG_TRACK_WIDTH_EDITOR*>( it->second );
455 m_trackWidthEditorInstance->UpdateFrame( m_frame );
456 }
457 else
458 {
459 PG_TRACK_WIDTH_EDITOR* trackWidthEditor = new PG_TRACK_WIDTH_EDITOR( m_frame );
461 static_cast<PG_TRACK_WIDTH_EDITOR*>( wxPropertyGrid::RegisterEditorClass( trackWidthEditor ) );
462 }
463
464 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_CHECKBOX_EDITOR::EDITOR_NAME );
465
466 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
467 {
468 PG_CHECKBOX_EDITOR* cbEditor = new PG_CHECKBOX_EDITOR();
469 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( wxPropertyGrid::RegisterEditorClass( cbEditor ) );
470 }
471 else
472 {
473 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( it->second );
474 }
475
476 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_RATIO_EDITOR::EDITOR_NAME );
477
478 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
479 {
480 PG_RATIO_EDITOR* ratioEditor = new PG_RATIO_EDITOR();
481 m_ratioEditorInstance = static_cast<PG_RATIO_EDITOR*>( wxPropertyGrid::RegisterEditorClass( ratioEditor ) );
482 }
483 else
484 {
485 m_ratioEditorInstance = static_cast<PG_RATIO_EDITOR*>( it->second );
486 }
487
488 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_NET_SELECTOR_EDITOR::EDITOR_NAME );
489
490 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
491 {
493 m_netSelectorEditorInstance = static_cast<PG_NET_SELECTOR_EDITOR*>( wxPropertyGrid::RegisterEditorClass( netEditor ) );
494 }
495 else
496 {
497 m_netSelectorEditorInstance = static_cast<PG_NET_SELECTOR_EDITOR*>( it->second );
498 }
499
500 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
501
502 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
503 {
504 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
505 m_fpEditorInstance->UpdateFrame( m_frame );
506 }
507 else
508 {
509 PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame,
510 []()
511 {
512 return "";
513 });
514 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
515 }
516
517 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
518
519 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
520 {
521 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
522 m_urlEditorInstance->UpdateFrame( m_frame );
523 }
524 else
525 {
526 PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
527 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
528 }
529}
530
531
533{
534 m_unitEditorInstance->UpdateFrame( nullptr );
535 m_fpEditorInstance->UpdateFrame( nullptr );
536 m_urlEditorInstance->UpdateFrame( nullptr );
537 m_trackWidthEditorInstance->UpdateFrame( nullptr );
538
539 // Note: the shared PG_NET_SELECTOR_EDITOR does not cache frame state; it resolves the
540 // owning panel from the property grid on each CreateControls call, so no teardown is
541 // needed here.
542}
543
544
546{
547 PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
548 const SELECTION& selection = selectionTool->GetSelection();
549
550 if( selection.Empty() && m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
551 {
552 if( BOARD* board = m_frame->GetBoard() )
553 {
554 if( FOOTPRINT* footprint = board->GetFirstFootprint() )
555 {
556 aFallbackSelection.Clear();
557 aFallbackSelection.Add( footprint );
558 return aFallbackSelection;
559 }
560 }
561 }
562
563 return selection;
564}
565
566
568{
569 SELECTION fallbackSelection;
570 const SELECTION& selection = getSelection( fallbackSelection );
571
572 return selection.Empty() ? nullptr : selection.Front();
573}
574
575
577{
578 BOARD* board = m_frame->GetBoard();
579
580 if( !board )
581 return;
582
583 SELECTION fallbackSelection;
584 const SELECTION& selection = getSelection( fallbackSelection );
585
586 // TODO perhaps it could be called less often? use PROPERTIES_TOOL and catch MODEL_RELOAD?
587 updateLists( board );
588
589 // Will actually just be updatePropertyValues() if selection hasn't changed
590 rebuildProperties( selection );
591}
592
593
595{
596 if( !m_frame->GetBoard() )
597 return;
598
599 SELECTION fallbackSelection;
600 const SELECTION& selection = getSelection( fallbackSelection );
601
602 rebuildProperties( selection );
603}
604
605
607{
608 m_currentFieldNames.clear();
609
610 for( EDA_ITEM* item : aSelection )
611 {
612 if( item->Type() != PCB_FOOTPRINT_T )
613 continue;
614
615 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
616
617 for( PCB_FIELD* field : footprint->GetFields() )
618 {
619 wxCHECK2( field, continue );
620
621 m_currentFieldNames.insert( field->GetCanonicalName() );
622 }
623 }
624
625 const wxString groupFields = _HKI( "Fields" );
626
627 // Make sure value comes immediately after reference. (Reference is invariant, so was added by
628 // FOOTPRINT_DESC(). We *could* still add it here, but then the whole Fields section comes at
629 // the end, which isn't ideal.)
630 if( !m_propMgr.GetProperty( TYPE_HASH( FOOTPRINT ), _HKI( "Value" ) ) )
631 m_propMgr.AddProperty( new PCB_FOOTPRINT_FIELD_PROPERTY( _HKI( "Value" ) ), groupFields );
632
633 for( const wxString& name : m_currentFieldNames )
634 {
635 if( !m_propMgr.GetProperty( TYPE_HASH( FOOTPRINT ), name ) )
636 {
637 m_propMgr.AddProperty( new PCB_FOOTPRINT_FIELD_PROPERTY( name ), groupFields )
638 .SetAvailableFunc(
639 [name]( INSPECTABLE* )
640 {
642 } );
643 }
644 }
645
647}
648
649
650wxPGProperty* PCB_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProperty ) const
651{
652 if( aProperty->TypeHash() == TYPE_HASH( PCB_LAYER_ID ) )
653 {
654 wxASSERT( aProperty->HasChoices() );
655
656 const wxPGChoices& canonicalLayers = aProperty->Choices();
657 wxArrayString boardLayerNames;
658 wxArrayInt boardLayerIDs;
659
660 for( int ii = 0; ii < (int) aProperty->Choices().GetCount(); ++ii )
661 {
662 int layer = canonicalLayers.GetValue( ii );
663
664 boardLayerNames.push_back( m_frame->GetBoard()->GetLayerName( ToLAYER_ID( layer ) ) );
665 boardLayerIDs.push_back( canonicalLayers.GetValue( ii ) );
666 }
667
668 auto ret = new PGPROPERTY_COLORENUM( new wxPGChoices( boardLayerNames, boardLayerIDs ) );
669
670 ret->SetColorFunc(
671 [&]( int aValue ) -> wxColour
672 {
673 return m_frame->GetColorSettings()->GetColor( ToLAYER_ID( aValue ) ).ToColour();
674 } );
675
676 ret->SetLabel( wxGetTranslation( aProperty->Name() ) );
677 ret->SetName( aProperty->Name() );
678 ret->SetHelpString( wxGetTranslation( aProperty->Name() ) );
679 ret->SetClientData( const_cast<PROPERTY_BASE*>( aProperty ) );
680
681 return ret;
682 }
683
684 wxPGProperty* prop = PGPropertyFactory( aProperty, m_frame );
685
686 if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
687 prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
688 else if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
689 prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
690 // OwnerHash is the class that registered the property. Routed PCB_ARC items inherit
691 // PCB_TRACK::Width, so this catches track arcs without changing unrelated "Width" properties.
692 else if( aProperty->OwnerHash() == TYPE_HASH( PCB_TRACK ) && aProperty->Name() == _HKI( "Width" ) )
694
695 return prop;
696}
697
698
699PROPERTY_BASE* PCB_PROPERTIES_PANEL::getPropertyFromEvent( const wxPropertyGridEvent& aEvent ) const
700{
701 EDA_ITEM* item = const_cast<PCB_PROPERTIES_PANEL*>( this )->getFrontItem();
702
703 if( !item || !item->IsBOARD_ITEM() )
704 return nullptr;
705
706 BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( item );
707
708 wxCHECK_MSG( firstItem, nullptr, wxT( "getPropertyFromEvent for a property with nothing selected!") );
709
710 PROPERTY_BASE* property = m_propMgr.GetProperty( TYPE_HASH( *firstItem ), aEvent.GetPropertyName() );
711 wxCHECK_MSG( property, nullptr, wxT( "getPropertyFromEvent for a property not found on the selected item!" ) );
712
713 return property;
714}
715
716
717void PCB_PROPERTIES_PANEL::valueChanging( wxPropertyGridEvent& aEvent )
718{
720 return;
721
722 EDA_ITEM* item = getFrontItem();
723
724 PROPERTY_BASE* property = getPropertyFromEvent( aEvent );
725 wxCHECK( property, /* void */ );
726 wxCHECK( item, /* void */ );
727
728 wxVariant newValue = aEvent.GetPropertyValue();
729
730 if( VALIDATOR_RESULT validationFailure = property->Validate( newValue.GetAny(), item ) )
731 {
732 wxString errorMsg = wxString::Format( wxS( "%s: %s" ), wxGetTranslation( property->Name() ),
733 validationFailure->get()->Format( m_frame ) );
734 m_frame->ShowInfoBarError( errorMsg );
735 aEvent.Veto();
736 return;
737 }
738
739 aEvent.Skip();
740}
741
742
743void PCB_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
744{
746 return;
747
748 SELECTION fallbackSelection;
749 const SELECTION& selection = getSelection( fallbackSelection );
750
751 wxCHECK( getPropertyFromEvent( aEvent ), /* void */ );
752
753 wxVariant newValue = aEvent.GetPropertyValue();
754 BOARD_COMMIT changes( m_frame );
755
756 PROPERTY_COMMIT_HANDLER handler( &changes );
757
758 for( EDA_ITEM* edaItem : selection )
759 {
760 if( !edaItem->IsBOARD_ITEM() )
761 continue;
762
763 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( edaItem );
764 PROPERTY_BASE* property = m_propMgr.GetProperty( TYPE_HASH( *item ), aEvent.GetPropertyName() );
765 wxCHECK( property, /* void */ );
766
767 if( item->Type() == PCB_TABLECELL_T )
768 changes.Modify( item->GetParent(), nullptr, RECURSE_MODE::NO_RECURSE );
769 else if( item->Type() == PCB_GENERATOR_T )
770 changes.Modify( item, nullptr, RECURSE_MODE::RECURSE );
771 else
772 changes.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
773
774 // In the PCB Editor, we generally restrict pad movement to the footprint (like dragging)
775 if( item->Type() == PCB_PAD_T && m_frame
776 && m_frame->IsType( FRAME_PCB_EDITOR )
777 && !m_frame->GetPcbNewSettings()->m_AllowFreePads
778 && ( aEvent.GetPropertyName() == _HKI( "Position X" )
779 || aEvent.GetPropertyName() == _HKI( "Position Y" ) ) )
780 {
781 PAD* pad = static_cast<PAD*>( item );
782 FOOTPRINT* fp = pad->GetParentFootprint();
783
784 if( fp )
785 {
786 VECTOR2I oldPos = pad->GetPosition();
787 VECTOR2I newPos = oldPos;
788
789 if( aEvent.GetPropertyName() == _HKI( "Position X" ) )
790 newPos.x = (int) newValue.GetLong();
791 else
792 newPos.y = (int) newValue.GetLong();
793
794 VECTOR2I delta = newPos - oldPos;
795
796 if( delta.x != 0 || delta.y != 0 )
797 {
798 changes.Modify( fp );
799 fp->Move( delta );
800 }
801 }
802
803 continue;
804 }
805
806 // Handle variant-aware boolean properties for footprints
807 if( item->Type() == PCB_FOOTPRINT_T )
808 {
809 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
810 wxString variantName;
811
812 if( footprint->GetBoard() )
813 variantName = footprint->GetBoard()->GetCurrentVariant();
814
815 if( !variantName.IsEmpty() )
816 {
817 wxString propName = aEvent.GetPropertyName();
818
819 if( propName == _HKI( "Do not Populate" )
820 || propName == _HKI( "Exclude From Bill of Materials" )
821 || propName == _HKI( "Exclude From Position Files" ) )
822 {
823 FOOTPRINT_VARIANT* variant = footprint->GetVariant( variantName );
824
825 if( !variant )
826 variant = footprint->AddVariant( variantName );
827
828 if( variant )
829 {
830 bool boolValue = newValue.GetBool();
831
832 if( propName == _HKI( "Do not Populate" ) )
833 variant->SetDNP( boolValue );
834 else if( propName == _HKI( "Exclude From Bill of Materials" ) )
835 variant->SetExcludedFromBOM( boolValue );
836 else if( propName == _HKI( "Exclude From Position Files" ) )
837 variant->SetExcludedFromPosFiles( boolValue );
838
839 continue;
840 }
841 }
842 }
843 }
844
845 item->Set( property, newValue );
846 }
847
848 changes.Push( _( "Edit Properties" ) );
849
850 m_frame->Refresh();
851
852 // Perform grid updates as necessary based on value change
853 AfterCommit();
854
855 // PointEditor may need to update if locked/unlocked
856 if( aEvent.GetPropertyName() == _HKI( "Locked" ) )
857 m_frame->GetToolManager()->ProcessEvent( EVENTS::SelectedEvent );
858
859 aEvent.Skip();
860}
861
862
864{
865 wxPGChoices layersAll;
866 wxPGChoices layersCu;
867 wxPGChoices nets;
868 wxPGChoices fonts;
869
870 // Regenerate all layers
871 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().UIOrder() )
872 layersAll.Add( LSET::Name( layer ), layer );
873
874 for( PCB_LAYER_ID layer : LSET( aBoard->GetEnabledLayers() & LSET::AllCuMask() ).UIOrder() )
875 layersCu.Add( LSET::Name( layer ), layer );
876
877 m_propMgr.GetProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) )->SetChoices( layersAll );
878 m_propMgr.GetProperty( TYPE_HASH( PCB_SHAPE ), _HKI( "Layer" ) )->SetChoices( layersAll );
879
880 // Copper only properties
881 m_propMgr.GetProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) )->SetChoices( layersCu );
882 m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Bottom Backdrill Must-Cut" ) )->SetChoices( layersCu );
883 m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Top Backdrill Must-Cut" ) )->SetChoices( layersCu );
884 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Top" ) )->SetChoices( layersCu );
885 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Bottom" ) )->SetChoices( layersCu );
886 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Bottom Backdrill Must-Cut" ) )->SetChoices( layersCu );
887 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Top Backdrill Must-Cut" ) )->SetChoices( layersCu );
888 m_propMgr.GetProperty( TYPE_HASH( PCB_TUNING_PATTERN ), _HKI( "Layer" ) )->SetChoices( layersCu );
889
890 // Regenerate nets
891
892 std::vector<std::pair<wxString, int>> netNames;
893 netNames.reserve( aBoard->GetNetInfo().NetsByNetcode().size() );
894
895 for( const auto& [ netCode, netInfo ] : aBoard->GetNetInfo().NetsByNetcode() )
896 netNames.emplace_back( UnescapeString( netInfo->GetNetname() ), netCode );
897
898 std::sort( netNames.begin(), netNames.end(),
899 []( const auto& a, const auto& b )
900 {
901 return a.first.CmpNoCase( b.first ) < 0;
902 } );
903
904 for( const auto& [ netName, netCode ] : netNames )
905 nets.Add( netName, netCode );
906
907 auto netProperty = m_propMgr.GetProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net" ) );
908 netProperty->SetChoices( nets );
909
910 auto tuningNet = m_propMgr.GetProperty( TYPE_HASH( PCB_TUNING_PATTERN ), _HKI( "Net" ) );
911 tuningNet->SetChoices( nets );
912}
913
914
915bool PCB_PROPERTIES_PANEL::getItemValue( EDA_ITEM* aItem, PROPERTY_BASE* aProperty, wxVariant& aValue )
916{
917 // For FOOTPRINT variant-aware boolean properties, return variant-specific values
918 if( aItem->Type() == PCB_FOOTPRINT_T )
919 {
920 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
921 const wxString& propName = aProperty->Name();
922 wxString variantName;
923
924 if( footprint->GetBoard() )
925 variantName = footprint->GetBoard()->GetCurrentVariant();
926
927 if( propName == _HKI( "Do not Populate" ) )
928 {
929 aValue = wxVariant( footprint->GetDNPForVariant( variantName ) );
930 return true;
931 }
932 else if( propName == _HKI( "Exclude From Bill of Materials" ) )
933 {
934 aValue = wxVariant( footprint->GetExcludedFromBOMForVariant( variantName ) );
935 return true;
936 }
937 else if( propName == _HKI( "Exclude From Position Files" ) )
938 {
939 aValue = wxVariant( footprint->GetExcludedFromPosFilesForVariant( variantName ) );
940 return true;
941 }
942 }
943
944 return PROPERTIES_PANEL::getItemValue( aItem, aProperty, aValue );
945}
const char * name
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:234
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:324
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1030
wxString GetCurrentVariant() const
Definition board.h:406
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:998
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:106
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
Variant information for a footprint.
Definition footprint.h:217
void SetExcludedFromPosFiles(bool aExclude)
Definition footprint.h:237
void SetDNP(bool aDNP)
Definition footprint.h:231
void SetFieldValue(const wxString &aFieldName, const wxString &aValue)
Set a field value override for this variant.
Definition footprint.h:259
void SetExcludedFromBOM(bool aExclude)
Definition footprint.h:234
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
wxString GetFieldValueForVariant(const wxString &aVariantName, const wxString &aFieldName) const
Get a field value for a specific variant.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
bool GetDNPForVariant(const wxString &aVariantName) const
Get the DNP status for a specific variant.
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
bool GetExcludedFromPosFilesForVariant(const wxString &aVariantName) const
Get the exclude-from-position-files status for a specific variant.
FOOTPRINT_VARIANT * AddVariant(const wxString &aVariantName)
Add a new variant with the given name.
bool GetExcludedFromBOMForVariant(const wxString &aVariantName) const
Get the exclude-from-BOM status for a specific variant.
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue, bool aNotify=true)
bool IsBOARD_ITEM() const
Definition view_item.h:102
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:743
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
const NETCODES_MAP & NetsByNetcode() const
Return the netcode map, at least for python.
Definition netinfo.h:254
Definition pad.h:65
Common, abstract interface for edit frames.
size_t OwnerHash() const override
Return type-id of the Owner class.
size_t TypeHash() const override
Return type-id of the property type.
void setter(void *obj, wxAny &v) override
size_t BaseHash() const override
Return type-id of the Base class.
PCB_FOOTPRINT_FIELD_PROPERTY(const wxString &aName)
bool Writeable(INSPECTABLE *aObject) const override
wxAny getter(const void *obj) const override
PCB_BASE_EDIT_FRAME * m_frame
PG_NET_SELECTOR_EDITOR * m_netSelectorEditorInstance
void valueChanged(wxPropertyGridEvent &aEvent) override
Regenerates caches storing layer and net names.
PG_UNIT_EDITOR * m_unitEditorInstance
PG_RATIO_EDITOR * m_ratioEditorInstance
wxPGProperty * createPGProperty(const PROPERTY_BASE *aProperty) const override
void rebuildProperties(const SELECTION &aSelection) override
Generates the property grid for a given selection of items.
PCB_PROPERTIES_PANEL(wxWindow *aParent, PCB_BASE_EDIT_FRAME *aFrame)
const SELECTION & getSelection(SELECTION &aFallbackSelection)
Get the current selection from the selection tool.
EDA_ITEM * getFrontItem()
Get the front item of the current selection.
static std::set< wxString > m_currentFieldNames
PROPERTY_BASE * getPropertyFromEvent(const wxPropertyGridEvent &aEvent) const
PROPERTY_MANAGER & m_propMgr
PG_URL_EDITOR * m_urlEditorInstance
PG_CHECKBOX_EDITOR * m_checkboxEditorInstance
void updateLists(const BOARD *aBoard)
void valueChanging(wxPropertyGridEvent &aEvent) override
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...
PG_TRACK_WIDTH_EDITOR * m_trackWidthEditorInstance
PG_FPID_EDITOR * m_fpEditorInstance
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
static const wxString EDITOR_NAME
Definition pg_editors.h:75
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
wxPGWindowList CreateControls(wxPropertyGrid *aGrid, wxPGProperty *aProperty, const wxPoint &aPos, const wxSize &aSize) const override
bool OnEvent(wxPropertyGrid *aGrid, wxPGProperty *aProperty, wxWindow *aWindow, wxEvent &aEvent) const override
void UpdateControl(wxPGProperty *aProperty, wxWindow *aCtrl) const override
wxString GetName() const override
bool GetValueFromControl(wxVariant &aVariant, wxPGProperty *aProperty, wxWindow *aCtrl) const override
static const wxString EDITOR_NAME
PG_NET_SELECTOR_EDITOR()=default
static const wxString EDITOR_NAME
Definition pg_editors.h:117
std::unique_ptr< PROPERTY_EDITOR_UNIT_BINDER > m_unitBinder
void UpdateControl(wxPGProperty *aProperty, wxWindow *aCtrl) const override
void UpdateFrame(PCB_BASE_EDIT_FRAME *aFrame)
wxString GetName() const override
PG_TRACK_WIDTH_EDITOR(PCB_BASE_EDIT_FRAME *aFrame)
static const wxString EDITOR_NAME
PCB_BASE_EDIT_FRAME * m_frame
void setTrackWidthOptions(wxComboBox *aEditor) const
wxPGWindowList CreateControls(wxPropertyGrid *aGrid, wxPGProperty *aProperty, const wxPoint &aPos, const wxSize &aSize) const override
bool GetValueFromControl(wxVariant &aVariant, wxPGProperty *aProperty, wxWindow *aCtrl) const override
static wxString BuildEditorName(PCB_BASE_EDIT_FRAME *aFrame)
bool OnEvent(wxPropertyGrid *aGrid, wxPGProperty *aProperty, wxWindow *aWindow, wxEvent &aEvent) const override
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
static wxString BuildEditorName(EDA_DRAW_FRAME *aFrame)
PROPERTIES_PANEL(wxWindow *aParent, EDA_BASE_FRAME *aFrame)
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.
virtual size_t TypeHash() const =0
Return type-id of the property type.
PROPERTY_BASE(const wxString &aName, PROPERTY_DISPLAY aDisplay=PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType=ORIGIN_TRANSFORMS::NOT_A_COORD)
< Used to generate unique IDs. Must come up front so it's initialized before ctor.
Definition property.h:201
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition property.h:246
virtual bool Writeable(INSPECTABLE *aObject) const
Definition property.h:282
friend class INSPECTABLE
Definition property.h:459
const wxString & Name() const
Definition property.h:220
virtual const wxPGChoices & Choices() const
Return a limited set of possible values (e.g.
Definition property.h:226
virtual size_t OwnerHash() const =0
Return type-id of the Owner class.
Provide class metadata.Helper macro to map type hashes to names.
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
EDA_ITEM * Front() const
Definition selection.h:177
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:98
bool Empty() const
Checks if there is anything selected.
Definition selection.h:115
#define _(s)
@ RECURSE
Definition eda_item.h:53
@ NO_RECURSE
Definition eda_item.h:54
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:754
KICOMMON_API wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
#define _HKI(x)
Definition page_info.cpp:44
static const wxString MISSING_FIELD_SENTINEL
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
#define TYPE_HASH(x)
Definition property.h:74
std::optional< std::unique_ptr< VALIDATION_ERROR > > VALIDATOR_RESULT
Null optional means validation succeeded.
static const wxString MISSING_FIELD_SENTINEL
wxString UnescapeString(const wxString &aSource)
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
wxString GetCanonicalFieldName(FIELD_T aFieldType)
wxString result
Test unit parsing edge cases and error handling.
int delta
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:88
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
Functions to provide common constants and other functions to assist in making a consistent UI.
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687