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
19 * along with this program. If not, see <https://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 <tools/edit_tool.h>
38#include <math/util.h>
39#include <base_units.h>
40#include <pcb_drill_chart.h>
41#include <pcb_drill_map.h>
42#include <pcb_shape.h>
43#include <eda_units.h>
47#include <board_commit.h>
49#include <board.h>
51#include <properties/property.h>
52#include <pcb_dimension.h>
53#include <pcb_shape.h>
54#include <pcb_text.h>
55#include <pcb_track.h>
56#include <pcb_generator.h>
59#include <pad.h>
60#include <footprint.h>
61#include <pcb_field.h>
62#include <template_fieldnames.h>
64#include <string_utils.h>
66#include <widgets/ui_common.h>
67#include <widgets/unit_binder.h>
68
69#include <memory>
70#include <vector>
71#include <wx/combobox.h>
72
73#include <wx/msgdlg.h>
74
75#include <cmath>
76
77
78class PG_NET_SELECTOR_EDITOR : public wxPGEditor
79{
80public:
81 static const wxString EDITOR_NAME;
82
84
85 wxString GetName() const override { return EDITOR_NAME; }
86
87 wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty, const wxPoint& aPos,
88 const wxSize& aSize ) const override
89 {
90 NET_SELECTOR* editor = new NET_SELECTOR( aGrid->GetPanel(), wxID_ANY, aPos, aSize, 0 );
91
92 // wxPropertyGrid registers editors globally and the same PG_NET_SELECTOR_EDITOR
93 // instance is reused by every PCB_PROPERTIES_PANEL (board editor, footprint editor).
94 // Resolve the owning panel -- and therefore the live frame and board -- from the
95 // grid at use time instead of caching frame state on the editor. This avoids
96 // cross-frame state corruption and nullptr derefs when one panel is destroyed while
97 // another is still live.
98 if( PCB_PROPERTIES_PANEL* panel = dynamic_cast<PCB_PROPERTIES_PANEL*>( aGrid->GetParent() ) )
99 {
100 if( PCB_BASE_EDIT_FRAME* frame = panel->GetFrame() )
101 {
102 if( BOARD* board = frame->GetBoard() )
103 editor->SetNetInfo( &board->GetNetInfo() );
104 }
105 }
106
107 editor->SetIndeterminateString( INDETERMINATE_STATE );
108 UpdateControl( aProperty, editor );
109
110 editor->Bind( FILTERED_ITEM_SELECTED,
111 [=]( wxCommandEvent& aEvt )
112 {
113 auto& choices = const_cast<wxPGChoices&>( aProperty->GetChoices() );
114 wxString netname = editor->GetSelectedNetname();
115
116 if( choices.Index( netname ) == wxNOT_FOUND )
117 choices.Add( netname, editor->GetSelectedNetcode() );
118
119 wxVariant val( editor->GetSelectedNetcode() );
120 aGrid->ChangePropertyValue( aProperty, val );
121 } );
122
123 return editor;
124 }
125
126 void UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const override
127 {
128 if( NET_SELECTOR* editor = dynamic_cast<NET_SELECTOR*>( aCtrl ) )
129 {
130 if( aProperty->IsValueUnspecified() )
131 editor->SetIndeterminate();
132 else
133 editor->SetSelectedNetcode( (int) aProperty->GetValue().GetLong() );
134 }
135 }
136
137 bool GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty, wxWindow* aCtrl ) const override
138 {
139 NET_SELECTOR* editor = dynamic_cast<NET_SELECTOR*>( aCtrl );
140
141 if( !editor )
142 return false;
143
144 aVariant = static_cast<long>( editor->GetSelectedNetcode() );
145 return true;
146 }
147
148 bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aWindow, wxEvent& aEvent ) const override
149 {
150 return false;
151 }
152};
153
154
155const wxString PG_NET_SELECTOR_EDITOR::EDITOR_NAME = wxS( "PG_NET_SELECTOR_EDITOR" );
156
157class PG_TRACK_WIDTH_EDITOR : public wxPGEditor
158{
159public:
160 static const wxString EDITOR_NAME;
161
163 m_frame( aFrame )
164 {
165 if( m_frame )
166 {
167 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
168 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
169 }
170
172 }
173
174 wxString GetName() const override { return m_editorName; }
175
176 static wxString BuildEditorName( PCB_BASE_EDIT_FRAME* aFrame )
177 {
178 if( !aFrame )
179 return EDITOR_NAME + "NoFrame";
180
181 return EDITOR_NAME + aFrame->GetName();
182 }
183
185 {
186 m_frame = aFrame;
187
188 if( m_frame )
189 {
190 m_unitBinder = std::make_unique<PROPERTY_EDITOR_UNIT_BINDER>( m_frame );
191 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
192 }
193 else
194 {
195 m_unitBinder = nullptr;
196 }
197 }
198
199 wxPGWindowList CreateControls( wxPropertyGrid* aGrid, wxPGProperty* aProperty, const wxPoint& aPos,
200 const wxSize& aSize ) const override
201 {
202 wxASSERT( m_unitBinder );
203
204 wxComboBox* editor = new wxComboBox( aGrid->GetPanel(), wxID_ANY, wxEmptyString, aPos, aSize, 0, nullptr,
205 wxCB_DROPDOWN | wxTE_PROCESS_ENTER );
206
207 m_unitBinder->SetControl( editor );
208 m_unitBinder->RequireEval();
209 m_unitBinder->SetUnits( m_frame->GetUserUnits() );
210
212 UpdateControl( aProperty, editor );
213
214 std::shared_ptr<bool> popupShown = std::make_shared<bool>( false );
215 auto commitValue =
216 [this, aGrid, aProperty, editor]()
217 {
218 if( !m_unitBinder || editor->GetValue() == INDETERMINATE_STATE )
219 return;
220
221 wxVariant val( static_cast<long>( m_unitBinder->GetValue() ) );
222 aGrid->ChangePropertyValue( aProperty, val );
223 };
224
225 editor->Bind( wxEVT_COMBOBOX_DROPDOWN,
226 [popupShown]( wxCommandEvent& aEvent )
227 {
228 *popupShown = true;
229 aEvent.Skip();
230 } );
231
232 editor->Bind( wxEVT_COMBOBOX,
233 [commitValue, popupShown]( wxCommandEvent& aEvent )
234 {
235 // Choosing a preset from the dropdown should apply that preset immediately.
236 if( *popupShown )
237 commitValue();
238
239 aEvent.Skip();
240 } );
241
242 editor->Bind( wxEVT_COMBOBOX_CLOSEUP,
243 [aGrid, popupShown]( wxCommandEvent& aEvent )
244 {
245 aGrid->CallAfter( [popupShown]()
246 {
247 *popupShown = false;
248 } );
249
250 aEvent.Skip();
251 } );
252
253 editor->Bind( wxEVT_CHAR_HOOK,
254 [commitValue, editor, popupShown]( wxKeyEvent& aEvent )
255 {
256 // Pressing Enter after typing a custom value should apply the typed value,
257 // not the first preset in the dropdown.
258 if( ( aEvent.GetKeyCode() == WXK_RETURN
259 || aEvent.GetKeyCode() == WXK_NUMPAD_ENTER )
260 && !*popupShown )
261 {
262 if( editor->GetValue() != INDETERMINATE_STATE )
263 {
264 commitValue();
265 return;
266 }
267
268 // Let the property grid accept an unchanged mixed value.
269 }
270
271 aEvent.Skip();
272 } );
273
274 editor->Bind( wxEVT_KILL_FOCUS,
275 [commitValue, popupShown]( wxFocusEvent& aEvent )
276 {
277 // Clicking into another property cell should keep any typed custom value.
278 if( !*popupShown )
279 commitValue();
280
281 aEvent.Skip();
282 } );
283
284 return wxPGWindowList( editor, nullptr );
285 }
286
287 void UpdateControl( wxPGProperty* aProperty, wxWindow* aCtrl ) const override
288 {
289 if( !m_unitBinder )
290 return;
291
292 wxComboBox* editor = dynamic_cast<wxComboBox*>( aCtrl );
293 wxCHECK( editor, /* void */ );
294
295 if( aProperty->IsValueUnspecified() )
296 editor->ChangeValue( INDETERMINATE_STATE );
297 else
298 m_unitBinder->ChangeValue( aProperty->GetValue().GetLong() );
299 }
300
301 bool GetValueFromControl( wxVariant& aVariant, wxPGProperty* aProperty, wxWindow* aCtrl ) const override
302 {
303 if( !m_unitBinder )
304 return false;
305
306 wxComboBox* editor = dynamic_cast<wxComboBox*>( aCtrl );
307 wxCHECK_MSG( editor, false, "PG_TRACK_WIDTH_EDITOR requires a combo box!" );
308
309 if( editor->GetValue() == INDETERMINATE_STATE )
310 {
311 aVariant.MakeNull();
312 return true;
313 }
314
315 long result = static_cast<long>( m_unitBinder->GetValue() );
316 bool changed = aVariant.IsNull() || result != aVariant.GetLong();
317
318 if( changed )
319 aVariant = result;
320
321 return changed;
322 }
323
324 bool OnEvent( wxPropertyGrid* aGrid, wxPGProperty* aProperty, wxWindow* aWindow, wxEvent& aEvent ) const override
325 {
326 return false;
327 }
328
329private:
330 void setTrackWidthOptions( wxComboBox* aEditor ) const
331 {
332 std::vector<long long int> trackWidths;
333
334 // 0 is the netclass place-holder.
335 for( unsigned ii = 1; ii < m_frame->GetDesignSettings().m_TrackWidthList.size(); ++ii )
336 trackWidths.push_back( m_frame->GetDesignSettings().m_TrackWidthList[ii] );
337
338 m_unitBinder->SetOptionsList( trackWidths );
339
340 wxString unitLabel = EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() );
341
342 for( unsigned ii = 0; ii < aEditor->GetCount(); ++ii )
343 aEditor->SetString( ii, aEditor->GetString( ii ) + wxS( " " ) + unitLabel );
344 }
345
347 std::unique_ptr<PROPERTY_EDITOR_UNIT_BINDER> m_unitBinder;
348 wxString m_editorName;
349};
350
351
352const wxString PG_TRACK_WIDTH_EDITOR::EDITOR_NAME = wxS( "PG_TRACK_WIDTH_EDITOR" );
353
355 PROPERTIES_PANEL( aParent, aFrame ),
356 m_frame( aFrame ),
357 m_propMgr( PROPERTY_MANAGER::Instance() ),
358 m_scaleConfirmPending( false )
359{
360 addCategoryButton( _HKI( "Custom Properties" ), _( "Add Custom Property" ), BITMAPS::small_plus,
361 [this]()
362 {
364 } );
365
366 addCategoryButton( _HKI( "Fields" ), _( "Add Field" ), BITMAPS::small_plus,
367 [this]()
368 {
370 } );
371
372 m_propMgr.Rebuild();
373 bool found = false;
374
375 wxASSERT( wxPGGlobalVars );
376
377 wxString editorKey = PG_UNIT_EDITOR::BuildEditorName( m_frame );
378
379 auto it = wxPGGlobalVars->m_mapEditorClasses.find( editorKey );
380
381 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
382 {
383 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( it->second );
384 m_unitEditorInstance->UpdateFrame( m_frame );
385 found = true;
386 }
387
388 if( !found )
389 {
390 PG_UNIT_EDITOR* new_editor = new PG_UNIT_EDITOR( m_frame );
391 m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( wxPropertyGrid::RegisterEditorClass( new_editor ) );
392 }
393
394 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_TRACK_WIDTH_EDITOR::BuildEditorName( m_frame ) );
395
396 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
397 {
398 m_trackWidthEditorInstance = static_cast<PG_TRACK_WIDTH_EDITOR*>( it->second );
399 m_trackWidthEditorInstance->UpdateFrame( m_frame );
400 }
401 else
402 {
403 PG_TRACK_WIDTH_EDITOR* trackWidthEditor = new PG_TRACK_WIDTH_EDITOR( m_frame );
405 static_cast<PG_TRACK_WIDTH_EDITOR*>( wxPropertyGrid::RegisterEditorClass( trackWidthEditor ) );
406 }
407
408 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_CHECKBOX_EDITOR::EDITOR_NAME );
409
410 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
411 {
412 PG_CHECKBOX_EDITOR* cbEditor = new PG_CHECKBOX_EDITOR();
413 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( wxPropertyGrid::RegisterEditorClass( cbEditor ) );
414 }
415 else
416 {
417 m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( it->second );
418 }
419
420 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_RATIO_EDITOR::EDITOR_NAME );
421
422 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
423 {
424 PG_RATIO_EDITOR* ratioEditor = new PG_RATIO_EDITOR();
425 m_ratioEditorInstance = static_cast<PG_RATIO_EDITOR*>( wxPropertyGrid::RegisterEditorClass( ratioEditor ) );
426 }
427 else
428 {
429 m_ratioEditorInstance = static_cast<PG_RATIO_EDITOR*>( it->second );
430 }
431
432 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_NET_SELECTOR_EDITOR::EDITOR_NAME );
433
434 if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
435 {
437 m_netSelectorEditorInstance = static_cast<PG_NET_SELECTOR_EDITOR*>( wxPropertyGrid::RegisterEditorClass( netEditor ) );
438 }
439 else
440 {
441 m_netSelectorEditorInstance = static_cast<PG_NET_SELECTOR_EDITOR*>( it->second );
442 }
443
444 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
445
446 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
447 {
448 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
449 m_fpEditorInstance->UpdateFrame( m_frame );
450 }
451 else
452 {
453 PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame,
454 []()
455 {
456 return "";
457 });
458 m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
459 }
460
461 it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
462
463 if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
464 {
465 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
466 m_urlEditorInstance->UpdateFrame( m_frame );
467 }
468 else
469 {
470 PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
471 m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
472 }
473
474 Bind( wxEVT_MENU, &PCB_PROPERTIES_PANEL::onContextMenu, this, ID_CTX_ADD_FIELD );
478}
479
480
482{
483 m_unitEditorInstance->UpdateFrame( nullptr );
484 m_fpEditorInstance->UpdateFrame( nullptr );
485 m_urlEditorInstance->UpdateFrame( nullptr );
486 m_trackWidthEditorInstance->UpdateFrame( nullptr );
487
488 // Note: the shared PG_NET_SELECTOR_EDITOR does not cache frame state; it resolves the
489 // owning panel from the property grid on each CreateControls call, so no teardown is
490 // needed here.
491}
492
493
495{
496 PCB_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
497 const SELECTION& selection = selectionTool->GetSelection();
498
499 if( selection.Empty() && m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) )
500 {
501 if( BOARD* board = m_frame->GetBoard() )
502 {
503 if( FOOTPRINT* footprint = board->GetFirstFootprint() )
504 {
505 aFallbackSelection.Clear();
506 aFallbackSelection.Add( footprint );
507 return aFallbackSelection;
508 }
509 }
510 }
511
512 return selection;
513}
514
515
517{
518 SELECTION fallbackSelection;
519 const SELECTION& selection = getSelection( fallbackSelection );
520
521 return selection.Empty() ? nullptr : selection.Front();
522}
523
524
526{
527 BOARD* board = m_frame->GetBoard();
528
529 if( !board )
530 return;
531
532 SELECTION fallbackSelection;
533 const SELECTION& selection = getSelection( fallbackSelection );
534
535 // TODO perhaps it could be called less often? use PROPERTIES_TOOL and catch MODEL_RELOAD?
536 updateLists( board );
537
538 // Will actually just be updatePropertyValues() if selection hasn't changed
539 rebuildProperties( selection );
540}
541
542
544{
545 if( !m_frame->GetBoard() )
546 return;
547
548 SELECTION fallbackSelection;
549 const SELECTION& selection = getSelection( fallbackSelection );
550
551 rebuildProperties( selection );
552}
553
554
555bool PCB_PROPERTIES_PANEL::isKeyEditable( const wxPGProperty* aPGProp ) const
556{
557 PROPERTY_BASE* prop = static_cast<PROPERTY_BASE*>( aPGProp->GetClientData() );
558
559 if( !prop )
560 return false;
561
562 EDA_ITEM* item = const_cast<PCB_PROPERTIES_PANEL*>( this )->getFrontItem();
563
564 if( !item )
565 return false;
566
567 if( prop->Group() == _HKI( "Custom Properties" ) )
568 return true;
569
570 if( item->Type() != PCB_FOOTPRINT_T )
571 return false;
572
573 PCB_FIELD* field = static_cast<FOOTPRINT*>( item )->GetField( prop->Name() );
574
575 return field && !field->IsMandatory() && !field->IsPrivate();
576}
577
578
579bool PCB_PROPERTIES_PANEL::isKeyNameInUse( const wxString& aName ) const
580{
581 EDA_ITEM* item = const_cast<PCB_PROPERTIES_PANEL*>( this )->getFrontItem();
582
583 if( !item )
584 return false;
585
586 return m_propMgr.GetProperty( item, aName ) != nullptr;
587}
588
589
590void PCB_PROPERTIES_PANEL::onKeyRenamed( const wxString& aOldName, const wxString& aNewName )
591{
592 SELECTION fallbackSelection;
593 const SELECTION& selection = getSelection( fallbackSelection );
594
595 BOARD_COMMIT changes( m_frame );
596 PROPERTY_COMMIT_HANDLER handler( &changes );
597
598 for( EDA_ITEM* item : selection )
599 {
600 if( !item->IsBOARD_ITEM() )
601 continue;
602
603 BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
604
605 if( boardItem->Type() == PCB_FOOTPRINT_T )
606 {
607 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( boardItem );
608 PCB_FIELD* field = footprint->GetField( aOldName );
609
610 if( field && !field->IsMandatory() )
611 {
612 changes.Modify( footprint, nullptr, RECURSE_MODE::NO_RECURSE );
613 field->SetName( aNewName );
614 continue;
615 }
616 }
617
618 wxString value;
619
620 if( boardItem->GetCustomProperty( aOldName, value ) )
621 {
622 changes.Modify( boardItem, nullptr, RECURSE_MODE::NO_RECURSE );
623 boardItem->RemoveCustomProperty( aOldName );
624 boardItem->SetCustomProperty( aNewName, value );
625 }
626 }
627
628 changes.Push( _( "Rename Property" ) );
629
630 AfterCommit();
631}
632
633
634bool PCB_PROPERTIES_PANEL::buildContextMenu( wxMenu& aMenu, wxPGProperty* aPGProp )
635{
636 if( aPGProp->IsCategory() )
637 {
638 if( aPGProp->GetLabel() == wxGetTranslation( _HKI( "Fields" ) ) )
639 aMenu.Append( ID_CTX_ADD_FIELD, _( "Add Field" ) );
640 else if( aPGProp->GetLabel() == wxGetTranslation( _HKI( "Custom Properties" ) ) )
641 aMenu.Append( ID_CTX_ADD_CUSTOM_PROPERTY, _( "Add Custom Property" ) );
642 }
643 else
644 {
645 PROPERTY_BASE* prop = static_cast<PROPERTY_BASE*>( aPGProp->GetClientData() );
646
647 if( !prop )
648 return false;
649
650 if( prop->Group() == _HKI( "Fields" ) )
651 {
652 if( isKeyEditable( aPGProp ) )
653 aMenu.Append( ID_CTX_REMOVE_FIELD, _( "Remove Field" ) );
654
655 aMenu.Append( ID_CTX_ADD_FIELD, _( "Add Field" ) );
656 }
657 else if( prop->Group() == _HKI( "Custom Properties" ) )
658 {
659 aMenu.Append( ID_CTX_REMOVE_CUSTOM_PROPERTY, _( "Remove Custom Property" ) );
660 aMenu.Append( ID_CTX_ADD_CUSTOM_PROPERTY, _( "Add Custom Property" ) );
661 }
662 }
663
664 return aMenu.GetMenuItemCount() > 0;
665}
666
667
668void PCB_PROPERTIES_PANEL::onContextMenu( wxCommandEvent& aEvent )
669{
670 switch( aEvent.GetId() )
671 {
672 case ID_CTX_ADD_FIELD: addBlankField(); break;
676 default:
677 break;
678 }
679}
680
681
683{
684 SELECTION fallbackSelection;
685 const SELECTION& selection = getSelection( fallbackSelection );
686
688
689 if( selection.Empty() )
690 return;
691
692 // Pick a unique untranslated placeholder name that doesn't collide with an existing field.
693 wxString name;
694
695 for( int n = 0; ; ++n )
696 {
698 bool used = false;
699
700 for( EDA_ITEM* item : selection )
701 {
702 if( item->Type() == PCB_FOOTPRINT_T && static_cast<FOOTPRINT*>( item )->HasField( name ) )
703 {
704 used = true;
705 break;
706 }
707 }
708
709 if( !used )
710 break;
711 }
712
713 BOARD_COMMIT changes( m_frame );
714 PROPERTY_COMMIT_HANDLER handler( &changes );
715
716 for( EDA_ITEM* item : selection )
717 {
718 if( item->Type() != PCB_FOOTPRINT_T )
719 continue;
720
721 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
722 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER, name );
723
724 field->SetText( wxEmptyString );
725 field->SetVisible( false );
726 changes.Modify( footprint, nullptr, RECURSE_MODE::NO_RECURSE );
727 footprint->Add( field );
728 }
729
730 changes.Push( _( "Add Field" ) );
731 AfterCommit();
732
734
735 beginLabelEdit( name, true );
736}
737
738
740{
741 SELECTION fallbackSelection;
742 const SELECTION& selection = getSelection( fallbackSelection );
743
745
746 if( selection.Empty() )
747 return;
748
749 wxString name;
750
751 for( int n = 0; ; ++n )
752 {
753 name = wxString::Format( wxS( "Property%d" ), n );
754 bool used = false;
755
756 for( EDA_ITEM* item : selection )
757 {
758 wxString dummy;
759
760 if( item->GetCustomProperty( name, dummy ) )
761 {
762 used = true;
763 break;
764 }
765 }
766
767 if( !used )
768 break;
769 }
770
771 BOARD_COMMIT changes( m_frame );
772 PROPERTY_COMMIT_HANDLER handler( &changes );
773
774 for( EDA_ITEM* item : selection )
775 {
776 if( item->IsBOARD_ITEM() )
777 {
778 changes.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
779 item->SetCustomProperty( name, wxEmptyString );
780 }
781 }
782
783 changes.Push( _( "Add Custom Property" ) );
784 AfterCommit();
785
787
788 beginLabelEdit( name, true );
789}
790
791
792void PCB_PROPERTIES_PANEL::removeField( const wxString& aName )
793{
794 SELECTION fallbackSelection;
795 const SELECTION& selection = getSelection( fallbackSelection );
796
797 BOARD_COMMIT changes( m_frame );
798 PROPERTY_COMMIT_HANDLER handler( &changes );
799
800 for( EDA_ITEM* item : selection )
801 {
802 if( item->Type() != PCB_FOOTPRINT_T )
803 continue;
804
805 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
806 PCB_FIELD* field = footprint->GetField( aName );
807
808 if( field && !field->IsMandatory() )
809 {
810 // BOARD_COMMIT's own field removal path only hides the field (e.g. when a user presses the delete key),
811 // we want to actually delete a custom field from this explicit menu action
812 changes.Modify( footprint, nullptr, RECURSE_MODE::NO_RECURSE );
813 footprint->Remove( field );
814 }
815 }
816
817 changes.Push( _( "Remove Field" ) );
818 AfterCommit();
819}
820
821
823{
824 SELECTION fallbackSelection;
825 const SELECTION& selection = getSelection( fallbackSelection );
826
827 BOARD_COMMIT changes( m_frame );
828 PROPERTY_COMMIT_HANDLER handler( &changes );
829
830 for( EDA_ITEM* item : selection )
831 {
832 if( item->IsBOARD_ITEM() )
833 {
834 changes.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
835 item->RemoveCustomProperty( aName );
836 }
837 }
838
839 changes.Push( _( "Remove Custom Property" ) );
840 AfterCommit();
841}
842
843
845{
846 // Note: currently assuming that any newly-created item from the panel is either a field or
847 // a custom property because those are the types we currently support.
848 if( EDA_ITEM* item = getFrontItem();
849 item && item->Type() == PCB_FOOTPRINT_T && static_cast<FOOTPRINT*>( item )->HasField( aKey ) )
850 {
851 removeField( aKey );
852 }
853 else
854 {
855 removeCustomProperty( aKey );
856 }
857}
858
859
861{
862 SELECTION filtered;
863 filtered.SetIsHover( aSelection.IsHover() );
864
865 for( EDA_ITEM* item : aSelection )
866 {
867 if( item->IsBOARD_ITEM() )
868 {
869 EDA_GROUP* parent = static_cast<BOARD_ITEM*>( item )->GetParentGroup();
870
871 if( parent && parent->AsEdaItem()->Type() == PCB_GENERATOR_T
872 && static_cast<PCB_GENERATOR*>( parent->AsEdaItem() )->ChildrenAreReadOnly() )
873 {
874 continue;
875 }
876 }
877
878 filtered.Add( item );
879 }
880
881 return filtered;
882}
883
884
886{
887 // Strip read-only generator children (like stitch vias)
888 SELECTION editableSelection = filterOutReadOnlyGenChildren( aRawSelection );
889 const SELECTION& aSelection = editableSelection;
890
892
893}
894
895
896wxPGProperty* PCB_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProperty ) const
897{
898 if( aProperty->TypeHash() == TYPE_HASH( PCB_LAYER_ID ) )
899 {
900 wxASSERT( aProperty->HasChoices() );
901
902 const wxPGChoices& canonicalLayers = aProperty->Choices();
903 wxArrayString boardLayerNames;
904 wxArrayInt boardLayerIDs;
905
906 for( int ii = 0; ii < (int) aProperty->Choices().GetCount(); ++ii )
907 {
908 int layer = canonicalLayers.GetValue( ii );
909
910 boardLayerNames.push_back( m_frame->GetBoard()->GetLayerName( ToLAYER_ID( layer ) ) );
911 boardLayerIDs.push_back( canonicalLayers.GetValue( ii ) );
912 }
913
914 auto ret = new PGPROPERTY_COLORENUM( new wxPGChoices( boardLayerNames, boardLayerIDs ) );
915
916 ret->SetColorFunc(
917 [&]( int aValue ) -> wxColour
918 {
919 return m_frame->GetColorSettings()->GetColor( ToLAYER_ID( aValue ) ).ToColour();
920 } );
921
922 ret->SetLabel( wxGetTranslation( aProperty->Name() ) );
923 ret->SetName( aProperty->Name() );
924 ret->SetHelpString( wxGetTranslation( aProperty->Name() ) );
925 ret->SetClientData( const_cast<PROPERTY_BASE*>( aProperty ) );
926
927 return ret;
928 }
929
930 wxPGProperty* prop = PGPropertyFactory( aProperty, m_frame );
931
933 prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
934 else if( aProperty->Name() == GetDefaultFieldName( FIELD_T::DATASHEET, UNTRANSLATED ) )
935 prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
936 // OwnerHash is the class that registered the property. Routed PCB_ARC items inherit
937 // PCB_TRACK::Width, so this catches track arcs without changing unrelated "Width" properties.
938 else if( aProperty->OwnerHash() == TYPE_HASH( PCB_TRACK ) && aProperty->Name() == _HKI( "Width" ) )
940
941 return prop;
942}
943
944
945PROPERTY_BASE* PCB_PROPERTIES_PANEL::getPropertyFromEvent( const wxPropertyGridEvent& aEvent ) const
946{
947 EDA_ITEM* item = const_cast<PCB_PROPERTIES_PANEL*>( this )->getFrontItem();
948
949 if( !item || !item->IsBOARD_ITEM() )
950 return nullptr;
951
952 BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( item );
953
954 wxCHECK_MSG( firstItem, nullptr, wxT( "getPropertyFromEvent for a property with nothing selected!") );
955
956 PROPERTY_BASE* property = m_propMgr.GetProperty( firstItem, aEvent.GetPropertyName() );
957 wxCHECK_MSG( property, nullptr, wxT( "getPropertyFromEvent for a property not found on the selected item!" ) );
958
959 return property;
960}
961
962
963void PCB_PROPERTIES_PANEL::valueChanging( wxPropertyGridEvent& aEvent )
964{
966 return;
967
968 EDA_ITEM* item = getFrontItem();
969
970 PROPERTY_BASE* property = getPropertyFromEvent( aEvent );
971 wxCHECK( property, /* void */ );
972 wxCHECK( item, /* void */ );
973
974 wxVariant newValue = aEvent.GetPropertyValue();
975
976 if( VALIDATOR_RESULT validationFailure = property->Validate( newValue.GetAny(), item ) )
977 {
978 wxString errorMsg = wxString::Format( wxS( "%s: %s" ), wxGetTranslation( property->Name() ),
979 validationFailure->get()->Format( m_frame ) );
980 m_frame->ShowInfoBarError( errorMsg );
981 aEvent.Veto();
982 return;
983 }
984
985 // Scaling a footprint that has pads is dangerous (the physical
986 // part keeps its original size), so confirm before applying.
987 const wxString propName = aEvent.GetPropertyName();
988
989 if( propName == _HKI( "Scale X" ) || propName == _HKI( "Scale Y" ) )
990 {
991 const double newScale = newValue.GetDouble();
992
993 // Zero, negative, and non-finite scales make the footprint transform degenerate.
994 if( !std::isfinite( newScale ) || newScale <= 0.0 )
995 {
996 m_frame->ShowInfoBarError( _( "Scale must be a positive number." ) );
997 aEvent.Veto();
998 return;
999 }
1000
1001 if( newScale != 1.0 )
1002 {
1003 SELECTION fallbackSelection;
1004 const SELECTION& selection = getSelection( fallbackSelection );
1005 int fpWithPads = 0;
1006
1007 for( EDA_ITEM* edaItem : selection )
1008 {
1009 if( edaItem->IsBOARD_ITEM() && static_cast<BOARD_ITEM*>( edaItem )->Type() == PCB_FOOTPRINT_T
1010 && !static_cast<FOOTPRINT*>( edaItem )->Pads().empty() )
1011 {
1012 fpWithPads++;
1013 }
1014 }
1015
1016 if( fpWithPads > 0 )
1017 {
1018 // A modal dialog must not be shown from this synchronous grid handler.
1019 // Veto the edit and re-drive it from the main loop once confirmed.
1020 aEvent.Veto();
1021
1023 return;
1024
1025 m_scaleConfirmPending = true;
1026
1027 wxString msg = wxString::Format( _( "%d footprint(s) in the selection have pads. Scaling changes "
1028 "the drawn pad positions but not the physical part. Continue?" ),
1029 fpWithPads );
1030
1031 CallAfter(
1032 [this, msg, propName, newValue]()
1033 {
1034 if( wxMessageBox( msg, _( "Scale footprint with pads?" ), wxYES_NO | wxICON_WARNING, this )
1035 == wxYES )
1036 {
1037 applyConfirmedScale( propName, newValue );
1038 }
1039
1040 m_scaleConfirmPending = false;
1041 } );
1042
1043 return;
1044 }
1045 }
1046 }
1047
1048 aEvent.Skip();
1049}
1050
1051
1052void PCB_PROPERTIES_PANEL::applyConfirmedScale( const wxString& aPropName, const wxVariant& aValue )
1053{
1054 wxPGProperty* pgProp = m_grid->GetPropertyByName( aPropName );
1055
1056 if( !pgProp )
1057 return;
1058
1059 // Re-drive the normal changed path now that we are back in the main loop.
1060 wxPropertyGridEvent evt( wxEVT_PG_CHANGED );
1061 evt.SetEventObject( m_grid );
1062 evt.SetProperty( pgProp );
1063 evt.SetPropertyValue( aValue );
1064
1065 valueChanged( evt );
1066}
1067
1068
1069void PCB_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
1070{
1072 return;
1073
1074 SELECTION fallbackSelection;
1075 SELECTION rawSelection = getSelection( fallbackSelection );
1076
1077 // Strip read-only generator children, last ditch sanity check
1078 SELECTION filtered = filterOutReadOnlyGenChildren( rawSelection );
1079
1080 if( filtered.Empty() )
1081 {
1082 aEvent.Veto();
1083 return;
1084 }
1085
1086 const SELECTION& selection = filtered;
1087
1088 wxCHECK( getPropertyFromEvent( aEvent ), /* void */ );
1089
1090 wxVariant newValue = aEvent.GetPropertyValue();
1091 BOARD_COMMIT changes( m_frame );
1092
1093 PROPERTY_COMMIT_HANDLER handler( &changes );
1094
1095 // Multi-footprint scale: rescale each footprint around the selection's
1096 // geometric (bbox) center instead of around its own anchor. Single-fp
1097 // edits go through the regular setter path (anchored at the fp itself).
1098 const wxString propName = aEvent.GetPropertyName();
1099 const bool isScaleX = ( propName == _HKI( "Scale X" ) );
1100 const bool isScaleY = ( propName == _HKI( "Scale Y" ) );
1101 int fpInSelection = 0;
1102 BOX2I selectionBBox;
1103
1104 if( isScaleX || isScaleY )
1105 {
1106 for( EDA_ITEM* edaItem : selection )
1107 {
1108 if( edaItem->IsBOARD_ITEM() && static_cast<BOARD_ITEM*>( edaItem )->Type() == PCB_FOOTPRINT_T )
1109 {
1110 fpInSelection++;
1111 selectionBBox.Merge( static_cast<FOOTPRINT*>( edaItem )->GetBoundingBox() );
1112 }
1113 }
1114 }
1115
1116 const bool useSelectionCenter = fpInSelection > 1;
1117 const VECTOR2I selectionCenter = useSelectionCenter ? selectionBBox.GetCenter() : VECTOR2I( 0, 0 );
1118
1119 // Driving length constraints touched by this edit solved again after commit lands
1120 std::vector<PCB_CONSTRAINT*> drivingConstraints;
1121
1122 for( EDA_ITEM* edaItem : selection )
1123 {
1124 if( !edaItem->IsBOARD_ITEM() )
1125 continue;
1126
1127 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( edaItem );
1128 PROPERTY_BASE* property = m_propMgr.GetProperty( item, aEvent.GetPropertyName() );
1129 wxCHECK( property, /* void */ );
1130
1131 if( item->Type() == PCB_TABLECELL_T )
1132 changes.Modify( item->GetParent(), nullptr, RECURSE_MODE::NO_RECURSE );
1133 else if( item->Type() == PCB_GENERATOR_T )
1134 changes.Modify( item, nullptr, RECURSE_MODE::RECURSE );
1135 else
1136 changes.Modify( item, nullptr, RECURSE_MODE::NO_RECURSE );
1137
1138 // In the PCB Editor, we generally restrict pad movement to the footprint (like dragging)
1139 if( item->Type() == PCB_PAD_T && m_frame
1140 && m_frame->IsType( FRAME_PCB_EDITOR )
1141 && !m_frame->GetPcbNewSettings()->m_AllowFreePads
1142 && ( aEvent.GetPropertyName() == _HKI( "Position X" )
1143 || aEvent.GetPropertyName() == _HKI( "Position Y" ) ) )
1144 {
1145 PAD* pad = static_cast<PAD*>( item );
1146 FOOTPRINT* fp = pad->GetParentFootprint();
1147
1148 if( fp )
1149 {
1150 VECTOR2I oldPos = pad->GetPosition();
1151 VECTOR2I newPos = oldPos;
1152
1153 if( aEvent.GetPropertyName() == _HKI( "Position X" ) )
1154 newPos.x = (int) newValue.GetLong();
1155 else
1156 newPos.y = (int) newValue.GetLong();
1157
1158 VECTOR2I delta = newPos - oldPos;
1159
1160 if( delta.x != 0 || delta.y != 0 )
1161 {
1162 changes.Modify( fp );
1163 fp->Move( delta );
1164 }
1165 }
1166
1167 continue;
1168 }
1169
1170 // Handle variant-aware boolean properties for footprints
1171 if( item->Type() == PCB_FOOTPRINT_T )
1172 {
1173 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
1174 wxString variantName;
1175
1176 if( footprint->GetBoard() )
1177 variantName = footprint->GetBoard()->GetCurrentVariant();
1178
1179 if( !variantName.IsEmpty() )
1180 {
1181 if( propName == _HKI( "Do not Populate" )
1182 || propName == _HKI( "Exclude From Bill of Materials" )
1183 || propName == _HKI( "Exclude From Simulation" )
1184 || propName == _HKI( "Exclude From Position Files" ) )
1185 {
1186 FOOTPRINT_VARIANT* variant = footprint->GetVariant( variantName );
1187
1188 if( !variant )
1189 variant = footprint->AddVariant( variantName );
1190
1191 if( variant )
1192 {
1193 bool boolValue = newValue.GetBool();
1194
1195 if( propName == _HKI( "Do not Populate" ) )
1196 variant->SetDNP( boolValue );
1197 else if( propName == _HKI( "Exclude From Bill of Materials" ) )
1198 variant->SetExcludedFromBOM( boolValue );
1199 else if( propName == _HKI( "Exclude From Simulation" ) )
1200 variant->SetExcludedFromSim( boolValue );
1201 else if( propName == _HKI( "Exclude From Position Files" ) )
1202 variant->SetExcludedFromPosFiles( boolValue );
1203
1204 continue;
1205 }
1206 }
1207 }
1208 }
1209
1210 if( useSelectionCenter && item->Type() == PCB_FOOTPRINT_T )
1211 {
1212 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1213 double newScale = newValue.GetDouble();
1214 double relSx = isScaleX ? newScale / fp->GetScaleX() : 1.0;
1215 double relSy = isScaleY ? newScale / fp->GetScaleY() : 1.0;
1216
1217 fp->RescaleAroundPoint( selectionCenter, relSx, relSy );
1218 continue;
1219 }
1220
1221 // Value mode and Driving value live partly in a board level constraint not item scoped
1222 // so route them through this commit for a shared undo step
1223 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1224 {
1225 PCB_DIMENSION_BASE* dim = static_cast<PCB_DIMENSION_BASE*>( item );
1226 BOARD* board = m_frame->GetBoard();
1227
1228 if( propName == _HKI( "Value Mode" ) )
1229 {
1230 DIM_VALUE_MODE mode = static_cast<DIM_VALUE_MODE>( newValue.GetLong() );
1231
1232 // Seed from current display value so a mode switch alone does not move geometry
1233 std::optional<int> length;
1234
1235 if( mode == DIM_VALUE_MODE::DRIVING )
1236 {
1237 PCB_CONSTRAINT* existing = FindDimensionLengthConstraint( board, dim );
1238
1239 if( existing && existing->GetValue() )
1240 length = KiROUND( *existing->GetValue() );
1241 else
1242 length = dim->GetMeasuredValue();
1243 }
1244
1245 // Arbitrary keeps measured text until edited
1246 std::optional<wxString> overrideText;
1247
1248 if( mode == DIM_VALUE_MODE::ARBITRARY && !dim->GetOverrideTextEnabled() )
1249 overrideText = dim->GetValueText();
1250
1252 board, dim, mode, length, overrideText,
1253 [&]( BOARD_ITEM* aItem ) { changes.Modify( aItem ); },
1254 [&]( BOARD_ITEM* aItem ) { changes.Add( aItem ); },
1255 [&]( BOARD_ITEM* aItem ) { changes.Remove( aItem ); } );
1256
1257 if( driving )
1258 drivingConstraints.push_back( driving );
1259
1260 continue;
1261 }
1262
1263 if( propName == _HKI( "Value" ) && dim->GetValueMode() == DIM_VALUE_MODE::DRIVING )
1264 {
1265 // Parsed in dimension own units to match display validator already blocked
1266 // non positive entries other selected dims failing parse left unchanged
1268 newValue.GetString() );
1269
1270 if( iu > 0.0 )
1271 {
1272 if( PCB_CONSTRAINT* driving = FindDimensionLengthConstraint( board, dim ) )
1273 {
1274 changes.Modify( driving );
1275 driving->SetValue( KiROUND( iu ) );
1276 drivingConstraints.push_back( driving );
1277 }
1278 }
1279
1280 continue;
1281 }
1282 }
1283
1284 item->Set( property, newValue );
1285
1286 // A chart's cells are generated from the settings just changed, so they say nothing
1287 // about the edit until the table has been laid out again
1288 if( item->Type() == PCB_DRILL_CHART_T && item->GetBoard() )
1289 {
1290 PCB_DRILL_CHART* chart = static_cast<PCB_DRILL_CHART*>( item );
1291 chart->RebuildCells( *item->GetBoard() );
1292 }
1293 }
1294
1295 changes.Push( _( "Edit Properties" ) );
1296
1297 // Edit is authoritative so hold shapes fixed while solving neighbors and fold into this
1298 // undo with a no op if nothing was touched
1299 if( CONSTRAINT_EDIT_TOOL* constraintTool = m_frame->GetToolManager()->GetTool<CONSTRAINT_EDIT_TOOL>() )
1300 {
1301 std::vector<PCB_SHAPE*> shapes;
1302 EDIT_TOOL::collectConstraintShapes( selection, shapes );
1303 constraintTool->SolveAfterEdit( shapes );
1304 }
1305
1306 // Driving length now on board so solve bound geometry to it and APPEND_UNDO folds follow
1307 // up moves into one undo
1308 if( !drivingConstraints.empty() )
1309 {
1310 BOARD_COMMIT solveCommit( m_frame );
1311
1312 for( PCB_CONSTRAINT* constraint : drivingConstraints )
1313 {
1314 ApplyConstraintImmediately( m_frame->GetBoard(), constraint, nullptr,
1315 [&]( BOARD_ITEM* aItem )
1316 {
1317 solveCommit.Modify( aItem );
1318 } );
1319 }
1320
1321 if( !solveCommit.Empty() )
1322 solveCommit.Push( _( "Apply Dimension Length" ), APPEND_UNDO );
1323 }
1324
1325 m_frame->Refresh();
1326
1327 // Perform grid updates as necessary based on value change
1328 AfterCommit();
1329
1330 // PointEditor may need to update if locked/unlocked
1331 if( aEvent.GetPropertyName() == _HKI( "Locked" ) )
1332 m_frame->GetToolManager()->ProcessEvent( EVENTS::SelectedEvent );
1333
1334 aEvent.Skip();
1335}
1336
1337
1339{
1340 wxPGChoices layersAll;
1341 wxPGChoices layersCu;
1342 wxPGChoices nets;
1343 wxPGChoices fonts;
1344
1345 // Regenerate all layers
1346 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().UIOrder() )
1347 layersAll.Add( LSET::Name( layer ), layer );
1348
1349 for( PCB_LAYER_ID layer : LSET( aBoard->GetEnabledLayers() & LSET::AllCuMask() ).UIOrder() )
1350 layersCu.Add( LSET::Name( layer ), layer );
1351
1352 m_propMgr.GetProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) )->SetChoices( layersAll );
1353 m_propMgr.GetProperty( TYPE_HASH( PCB_SHAPE ), _HKI( "Layer" ) )->SetChoices( layersAll );
1354
1355 // Copper only properties
1356 m_propMgr.GetProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) )->SetChoices( layersCu );
1357 m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Bottom Backdrill Must-Cut" ) )->SetChoices( layersCu );
1358 m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Top Backdrill Must-Cut" ) )->SetChoices( layersCu );
1359 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Top" ) )->SetChoices( layersCu );
1360 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Bottom" ) )->SetChoices( layersCu );
1361 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Bottom Backdrill Must-Cut" ) )->SetChoices( layersCu );
1362 m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Top Backdrill Must-Cut" ) )->SetChoices( layersCu );
1363 m_propMgr.GetProperty( TYPE_HASH( PCB_TUNING_PATTERN ), _HKI( "Layer" ) )->SetChoices( layersCu );
1364
1365 // Regenerate nets
1366
1367 std::vector<std::pair<wxString, int>> netNames;
1368 netNames.reserve( aBoard->GetNetInfo().NetsByNetcode().size() );
1369
1370 for( const auto& [ netCode, netInfo ] : aBoard->GetNetInfo().NetsByNetcode() )
1371 netNames.emplace_back( UnescapeString( netInfo->GetNetname() ), netCode );
1372
1373 std::sort( netNames.begin(), netNames.end(),
1374 []( const auto& a, const auto& b )
1375 {
1376 return a.first.CmpNoCase( b.first ) < 0;
1377 } );
1378
1379 for( const auto& [ netName, netCode ] : netNames )
1380 nets.Add( netName, netCode );
1381
1382 auto netProperty = m_propMgr.GetProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net" ) );
1383 netProperty->SetChoices( nets );
1384
1385 auto tuningNet = m_propMgr.GetProperty( TYPE_HASH( PCB_TUNING_PATTERN ), _HKI( "Net" ) );
1386 tuningNet->SetChoices( nets );
1387
1388 auto stitchNet = m_propMgr.GetProperty( TYPE_HASH( PCB_VIA_STITCH ), _HKI( "Net" ) );
1389 stitchNet->SetChoices( nets );
1390
1391 auto stitchGuardedNet = m_propMgr.GetProperty( TYPE_HASH( PCB_VIA_STITCH ), _HKI( "Guarded Net" ) );
1392 stitchGuardedNet->SetChoices( nets );
1393
1394 // Drill spans come from the stackup rather than from an enum, and enumerating them costs a
1395 // walk of the board, so only a board that actually has a map pays for it
1396 if( aBoard->DrillSymbolLayers().any() )
1397 {
1398 wxPGChoices spans;
1399 spans.Add( _( "All spans" ), -1 );
1400
1401 int index = 0;
1402
1403 for( const DRILL_SPAN& span : EnumerateDrillSpans( *aBoard ) )
1404 {
1405 spans.Add( wxString::Format( wxT( "%s - %s%s" ),
1406 aBoard->GetLayerName( span.TopLayer() ),
1407 aBoard->GetLayerName( span.BottomLayer() ),
1408 span.m_IsBackdrill ? _( " (backdrill)" )
1409 : wxString( wxEmptyString ) ),
1410 index++ );
1411 }
1412
1413 m_propMgr.GetProperty( TYPE_HASH( PCB_DRILL_MAP ), _HKI( "Hole Span" ) )
1414 ->SetChoices( spans );
1415 }
1416}
1417
1418
1419bool PCB_PROPERTIES_PANEL::getItemValue( EDA_ITEM* aItem, PROPERTY_BASE* aProperty, wxVariant& aValue )
1420{
1421 // For FOOTPRINT variant-aware boolean properties, return variant-specific values
1422 if( aItem->Type() == PCB_FOOTPRINT_T )
1423 {
1424 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
1425 const wxString& propName = aProperty->Name();
1426 wxString variantName;
1427
1428 if( footprint->GetBoard() )
1429 variantName = footprint->GetBoard()->GetCurrentVariant();
1430
1431 if( propName == _HKI( "Do not Populate" ) )
1432 {
1433 aValue = wxVariant( footprint->GetDNPForVariant( variantName ) );
1434 return true;
1435 }
1436 else if( propName == _HKI( "Exclude From Bill of Materials" ) )
1437 {
1438 aValue = wxVariant( footprint->GetExcludedFromBOMForVariant( variantName ) );
1439 return true;
1440 }
1441 else if( propName == _HKI( "Exclude From Simulation" ) )
1442 {
1443 aValue = wxVariant( footprint->GetExcludedFromSimForVariant( variantName ) );
1444 return true;
1445 }
1446 else if( propName == _HKI( "Exclude From Position Files" ) )
1447 {
1448 aValue = wxVariant( footprint->GetExcludedFromPosFilesForVariant( variantName ) );
1449 return true;
1450 }
1451 }
1452
1453 return PROPERTIES_PANEL::getItemValue( aItem, aProperty, aValue );
1454}
int index
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
CONSTRAINT_DIAGNOSIS ApplyConstraintImmediately(BOARD *aBoard, const PCB_CONSTRAINT *aConstraint, std::vector< PCB_SHAPE * > *aModified, const std::function< void(BOARD_ITEM *)> &aBeforeModify, const std::set< KIID > &aFixedShapes)
Solve a just-created constraint's cluster so the geometry snaps to satisfy it (SolidWorks-style),...
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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:266
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1207
const LSET & DrillSymbolLayers() const
Layers that currently have a drill map on them.
Definition board.h:603
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:936
wxString GetCurrentVariant() const
Definition board.h:521
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1183
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:653
constexpr const Vec GetCenter() const
Definition box2.h:227
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
bool Empty() const
Definition commit.h:142
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
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
Interactive authoring of geometric constraints (issue #2329).
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
virtual EDA_ITEM * AsEdaItem()=0
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
void SetCustomProperty(const wxString &aKey, const wxString &aValue)
Definition eda_item.h:255
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:231
static const TOOL_EVENT SelectedEvent
Definition actions.h:343
Variant information for a footprint.
Definition footprint.h:227
void SetExcludedFromPosFiles(bool aExclude)
Definition footprint.h:251
void SetExcludedFromSim(bool aExclude)
Definition footprint.h:248
void SetDNP(bool aDNP)
Definition footprint.h:242
void SetExcludedFromBOM(bool aExclude)
Definition footprint.h:245
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
bool GetExcludedFromSimForVariant(const wxString &aVariantName) const
Get the exclude-from-simulation status for a specific variant.
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
bool HasField(const wxString &aFieldName) const
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:404
double GetScaleX() const
Definition footprint.h:455
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.
void RescaleAroundPoint(const VECTOR2I &aCenter, double aSx, double aSy)
bool GetDNPForVariant(const wxString &aVariantName) const
Get the DNP status for a specific variant.
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.
double GetScaleY() const
Definition footprint.h:456
bool Set(PROPERTY_BASE *aProperty, wxAny &aValue, bool aNotify=true)
bool IsBOARD_ITEM() const
Definition view_item.h:98
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:739
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
const NETCODES_MAP & NetsByNetcode() const
Return the netcode map, at least for python.
Definition netinfo.h:260
Definition pad.h:61
Common, abstract interface for edit frames.
A geometric constraint between board items (issue #2329).
std::optional< double > GetValue() const
Abstract dimension API.
EDA_UNITS GetUnits() const
DIM_VALUE_MODE GetValueMode() const
Value mode from board state via DimensionValueMode.
int GetMeasuredValue() const
wxString GetValueText() const
bool GetOverrideTextEnabled() const
A drill chart placed on the board, kept in step with the holes.
void RebuildCells(const BOARD &aBoard, DRILL_SYMBOL_PROFILE *aAssignedProfile=nullptr)
Regenerate the cells from the board.
Turns on drill symbols at the holes, for one layer.
bool IsMandatory() const
void SetName(const wxString &aName)
Definition pcb_field.h:119
bool IsPrivate() const
Definition pcb_field.h:80
virtual bool ChildrenAreReadOnly() const
PCB_BASE_EDIT_FRAME * m_frame
PG_NET_SELECTOR_EDITOR * m_netSelectorEditorInstance
void onNewItemLeftBlank(const wxString &aKey) override
void valueChanged(wxPropertyGridEvent &aEvent) override
PG_UNIT_EDITOR * m_unitEditorInstance
PG_RATIO_EDITOR * m_ratioEditorInstance
wxPGProperty * createPGProperty(const PROPERTY_BASE *aProperty) const override
static SELECTION filterOutReadOnlyGenChildren(const SELECTION &aSelection)
Creates a new selection with any generator children removed that are part of a read only generator.
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.
void removeCustomProperty(const wxString &aName)
PROPERTY_BASE * getPropertyFromEvent(const wxPropertyGridEvent &aEvent) const
void removeField(const wxString &aName)
bool buildContextMenu(wxMenu &aMenu, wxPGProperty *aPGProp) override
bool isKeyEditable(const wxPGProperty *aPGProp) const override
PROPERTY_MANAGER & m_propMgr
PG_URL_EDITOR * m_urlEditorInstance
PG_CHECKBOX_EDITOR * m_checkboxEditorInstance
void applyConfirmedScale(const wxString &aPropName, const wxVariant &aValue)
Regenerates caches storing layer and net names.
void updateLists(const BOARD *aBoard)
void onContextMenu(wxCommandEvent &aEvent)
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
void onKeyRenamed(const wxString &aOldName, const wxString &aNewName) override
PG_FPID_EDITOR * m_fpEditorInstance
bool isKeyNameInUse(const wxString &aName) const override
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)
wxString m_contextMenuPropertyName
PROPERTIES_PANEL(wxWindow *aParent, EDA_BASE_FRAME *aFrame)
wxPropertyGrid * m_grid
void beginLabelEdit(const wxString &aKey, bool aStartBlank=false)
void settlePendingLabelEdit()
Synchronously ends any in-progress label edit; potentially canceling a newly added row.
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.
virtual size_t TypeHash() const =0
Return type-id of the property type.
wxString Group() const
Definition property.h:365
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition property.h:247
const wxString & Name() const
Definition property.h:221
virtual const wxPGChoices & Choices() const
Return a limited set of possible values (e.g.
Definition property.h:227
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)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
void SetIsHover(bool aIsHover)
Definition selection.h:80
bool IsHover() const
Definition selection.h:85
EDA_ITEM * Front() const
Definition selection.h:176
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:97
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
PCB_CONSTRAINT * FindDimensionLengthConstraint(BOARD *aBoard, const PCB_DIMENSION_BASE *aDimension)
Self FIXED_LENGTH constraint whose members are exactly aDimension START and END or nullptr the drivin...
PCB_CONSTRAINT * SetDimensionValueMode(BOARD *aBoard, PCB_DIMENSION_BASE *aDimension, DIM_VALUE_MODE aMode, std::optional< int > aDrivingLengthIU, const std::optional< wxString > &aOverrideText, const std::function< void(BOARD_ITEM *)> &aBeforeModify, const std::function< void(BOARD_ITEM *)> &aStageAdd, const std::function< void(BOARD_ITEM *)> &aBeforeRemove)
Apply a value mode transition to aDimension Driving creates or updates the driving length with aDrivi...
DIM_VALUE_MODE
Mode a value bearing dimension value is in Driven mirrors measured geometry Driving forces geometry t...
std::vector< DRILL_SPAN > EnumerateDrillSpans(const BOARD &aBoard)
Every drill span present on the board, through-holes first.
#define _(s)
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
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: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
#define TYPE_HASH(x)
Definition property.h:74
std::optional< std::unique_ptr< VALIDATION_ERROR > > VALIDATOR_RESULT
Null optional means validation succeeded.
#define APPEND_UNDO
Definition sch_commit.h:39
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
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
@ UNTRANSLATED
wxString result
Test unit parsing edge cases and error handling.
int delta
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition typeinfo.h:259
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:83
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
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:683