KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew/dialogs/dialog_shape_properties.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 Jean-Pierre Charras jp.charras at wanadoo.fr
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25/*
26 * Edit properties of Lines, Circles, Arcs and Polygons for PCBNew and Footprint Editor
27 */
28
29#include <pcb_base_edit_frame.h>
30#include <pcb_edit_frame.h>
31#include <wx/valnum.h>
32#include <board_commit.h>
35#include <tool/tool_manager.h>
36#include <tool/actions.h>
37#include <pcb_shape.h>
38#include <macros.h>
39#include <widgets/unit_binder.h>
40
42#include <tools/drawing_tool.h>
43
44
46{
47 std::unique_ptr<UNIT_BINDER> m_Binder;
48 wxTextCtrl* m_Ctrl;
49};
50
51
61class GEOM_SYNCER : public wxEvtHandler
62{
63public:
64 GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
65 m_shape( aShape ), m_boundCtrls( aBoundCtrls )
66 {
67 }
68
69 void BindCtrls( size_t aFrom, size_t aTo, std::function<void()> aCb )
70 {
71 wxCHECK( aFrom < m_boundCtrls.size(), /* void */ );
72 wxCHECK( aTo < m_boundCtrls.size(), /* void */ );
73
74 for( size_t i = aFrom; i <= aTo; ++i )
75 {
76 m_boundCtrls[i].m_Ctrl->Bind( wxEVT_TEXT,
77 [aCb]( wxCommandEvent& aEvent )
78 {
79 aCb();
80 } );
81 }
82 }
83
84 void SetShape( PCB_SHAPE& aShape )
85 {
86 m_shape = aShape;
87 updateAll();
88 }
89
90 virtual bool Validate( wxArrayString& aErrs ) const { return true; }
91
92protected:
93 virtual void updateAll() = 0;
94
95 wxTextCtrl* GetCtrl( size_t aIndex ) const
96 {
97 wxCHECK( aIndex < m_boundCtrls.size(), nullptr );
98 return m_boundCtrls[aIndex].m_Ctrl;
99 }
100
101 int GetIntValue( size_t aIndex ) const
102 {
103 wxCHECK( aIndex < m_boundCtrls.size(), 0.0 );
104 return static_cast<int>( m_boundCtrls[aIndex].m_Binder->GetValue() );
105 }
106
107 EDA_ANGLE GetAngleValue( size_t aIndex ) const
108 {
109 wxCHECK( aIndex < m_boundCtrls.size(), EDA_ANGLE() );
110 return m_boundCtrls[aIndex].m_Binder->GetAngleValue();
111 }
112
113 void ChangeValue( size_t aIndex, int aValue )
114 {
115 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
116 m_boundCtrls[aIndex].m_Binder->ChangeValue( aValue );
117 }
118
119 void ChangeAngleValue( size_t aIndex, const EDA_ANGLE& aValue )
120 {
121 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
122 m_boundCtrls[aIndex].m_Binder->ChangeAngleValue( aValue );
123 }
124
126
127 const PCB_SHAPE& GetShape() const { return m_shape; }
128
129private:
131 std::vector<BOUND_CONTROL>& m_boundCtrls;
132};
133
134
139{
140public:
142 {
155
157 };
158
159 RECTANGLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
160 GEOM_SYNCER( aShape, aBoundCtrls )
161 {
162 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
163 wxASSERT( GetShape().GetShape() == SHAPE_T::RECTANGLE );
164
166 [this]()
167 {
169 } );
170
172 [this]()
173 {
175 } );
176
178 [this]()
179 {
181 } );
182 }
183
184 bool Validate( wxArrayString& aErrs ) const override
185 {
187 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
188
189 if( p0 == p1 )
190 {
191 aErrs.push_back( _( "Rectangle cannot be zero-sized." ) );
192 return false;
193 }
194
195 return true;
196 }
197
198 void updateAll() override
199 {
203 }
204
206 {
208 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
209
210 GetShape().SetStart( p0 );
211 GetShape().SetEnd( p1 );
212
215 }
216
218 {
219 const VECTOR2I p0 = GetShape().GetStart();
220 const VECTOR2I p1 = GetShape().GetEnd();
221
222 ChangeValue( START_X, p0.x );
223 ChangeValue( START_Y, p0.y );
224 ChangeValue( END_X, p1.x );
225 ChangeValue( END_Y, p1.y );
226 }
227
229 {
231 const VECTOR2I size{ GetIntValue( CORNER_W ), GetIntValue( CORNER_H ) };
232
233 GetShape().SetStart( p0 );
234 GetShape().SetEnd( p0 + size );
235
238 }
239
241 {
242 const VECTOR2I p0 = GetShape().GetStart();
243
244 ChangeValue( CORNER_X, p0.x );
245 ChangeValue( CORNER_Y, p0.y );
246 ChangeValue( CORNER_W, GetShape().GetRectangleWidth() );
247 ChangeValue( CORNER_H, GetShape().GetRectangleHeight() );
248 }
249
251 {
253 const VECTOR2I size = { GetIntValue( CENTER_W ), GetIntValue( CENTER_H ) };
254
255 GetShape().SetStart( center - size / 2 );
256 GetShape().SetEnd( center + size / 2 );
257
260 }
261
263 {
264 const VECTOR2I c = GetShape().GetCenter();
265
266 ChangeValue( CENTER_X, c.x );
267 ChangeValue( CENTER_Y, c.y );
268 ChangeValue( CENTER_W, GetShape().GetRectangleWidth() );
269 ChangeValue( CENTER_H, GetShape().GetRectangleHeight() );
270 }
271};
272
273
275{
276public:
278 {
283
288
293
295 };
296
297 LINE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
298 GEOM_SYNCER( aShape, aBoundCtrls )
299 {
300 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
301 wxASSERT( GetShape().GetShape() == SHAPE_T::SEGMENT );
302
304 [this]()
305 {
306 OnEndsChange();
307 } );
308
310 [this]()
311 {
313 } );
314
316 [this]()
317 {
319 } );
320 }
321
322 void updateAll() override
323 {
324 updateEnds();
325 updatePolar();
327 }
328
330 {
332 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
333
334 GetShape().SetStart( p0 );
335 GetShape().SetEnd( p1 );
336
337 updatePolar();
339 }
340
342 {
343 const VECTOR2I p0 = GetShape().GetStart();
344 const VECTOR2I p1 = GetShape().GetEnd();
345
346 ChangeValue( START_X, p0.x );
347 ChangeValue( START_Y, p0.y );
348 ChangeValue( END_X, p1.x );
349 ChangeValue( END_Y, p1.y );
350 }
351
353 {
355 const int length = GetIntValue( LENGTH );
356 const EDA_ANGLE angle = GetAngleValue( ANGLE );
357
358 const VECTOR2I polar = GetRotated( VECTOR2I{ length, 0 }, angle );
359
360 GetShape().SetStart( p0 );
361 GetShape().SetEnd( p0 + polar );
362
363 updateEnds();
365 }
366
368 {
369 const VECTOR2I p0 = GetShape().GetStart();
370 const VECTOR2I p1 = GetShape().GetEnd();
371
374 ChangeValue( LENGTH, p0.Distance( p1 ) );
375 ChangeAngleValue( ANGLE, -EDA_ANGLE( p1 - p0 ) );
376 }
377
379 {
381 const VECTOR2I mid{ GetIntValue( MID_X ), GetIntValue( MID_Y ) };
382
383 GetShape().SetStart( start );
384 GetShape().SetEnd( mid - ( start - mid ) );
385
386 updateEnds();
387 updatePolar();
388 }
389
391 {
392 const VECTOR2I s = GetShape().GetStart();
393 const VECTOR2I c = GetShape().GetCenter();
394
395 ChangeValue( MID_X, c.x );
396 ChangeValue( MID_Y, c.y );
399 }
400};
401
402
404{
405public:
407 {
408 //CSA
414
421
423 };
424
425 ARC_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
426 GEOM_SYNCER( aShape, aBoundCtrls )
427 {
428 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
429 wxASSERT( GetShape().GetShape() == SHAPE_T::ARC );
430
432 [this]()
433 {
434 OnCSAChange();
435 } );
436
438 [this]()
439 {
440 OnSMEChange();
441 } );
442 }
443
444 bool Validate( wxArrayString& aErrs ) const override
445 {
446 const EDA_ANGLE angle = GetAngleValue( CSA_ANGLE );
447
448 if( angle == 0 )
449 {
450 aErrs.push_back( _( "Arc angle must be greater than 0" ) );
451 return false;
452 }
453
457
458 if( start == mid || mid == end || start == end )
459 {
460 aErrs.push_back( _( "Arc must have 3 distinct points" ) );
461 return false;
462 }
463 else
464 {
465 const VECTOR2D center = CalcArcCenter( start, end, angle );
466
467 double radius = ( center - start ).EuclideanNorm();
468 double max_offset = std::max( std::abs( center.x ), std::abs( center.y ) ) + radius;
469 VECTOR2I center_i = VECTOR2I( center.x, center.y );
470
471 if( max_offset >= ( std::numeric_limits<VECTOR2I::coord_type>::max() / 2.0 )
472 || center_i == start || center_i == end )
473 {
474 aErrs.push_back( wxString::Format( _( "Invalid Arc with radius %f and angle %f." ),
475 radius, angle.AsDegrees() ) );
476 return false;
477 }
478 }
479
480 return true;
481 }
482
483 void updateAll() override
484 {
485 updateCSA();
486 updateSME();
487 }
488
490 {
493 const int angle = GetIntValue( CSA_ANGLE );
494
496 GetShape().SetStart( start );
497 GetShape().SetArcAngleAndEnd( angle );
498
499 updateSME();
500 }
501
503 {
504 const VECTOR2I center = GetShape().GetCenter();
505 const VECTOR2I start = GetShape().GetStart();
506
509 ChangeValue( CSA_START_X, start.x );
510 ChangeValue( CSA_START_Y, start.y );
511 ChangeAngleValue( CSA_ANGLE, GetShape().GetArcAngle() );
512 }
513
515 {
519
520 GetShape().SetArcGeometry( p0, p1, p2 );
521
522 updateCSA();
523 }
524
526 {
527 const VECTOR2I p0 = GetShape().GetStart();
528 const VECTOR2I p1 = GetShape().GetArcMid();
529 const VECTOR2I p2 = GetShape().GetEnd();
530
533 ChangeValue( SME_MID_X, p1.x );
534 ChangeValue( SME_MID_Y, p1.y );
535 ChangeValue( SME_END_X, p2.x );
536 ChangeValue( SME_END_Y, p2.y );
537 }
538};
539
540
542{
543public:
545 {
553
555 };
556
557 CIRCLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
558 GEOM_SYNCER( aShape, aBoundCtrls )
559 {
560 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
561 wxASSERT( GetShape().GetShape() == SHAPE_T::CIRCLE );
562
564 [this]()
565 {
567 } );
568
570 [this]()
571 {
573 } );
574 }
575
576 void updateAll() override
577 {
580 }
581
582 bool Validate( wxArrayString& aErrs ) const override
583 {
584 if( GetIntValue( RADIUS ) <= 0 )
585 {
586 aErrs.push_back( _( "Radius must be greater than 0" ) );
587 return false;
588 }
589
590 return true;
591 }
592
594 {
596 const int radius = GetIntValue( RADIUS );
597
600
602 }
603
605 {
606 const VECTOR2I center = GetShape().GetCenter();
607
610 ChangeValue( RADIUS, GetShape().GetRadius() );
611 }
612
614 {
617
619 GetShape().SetEnd( pt );
620
622 }
623
625 {
626 const VECTOR2I center = GetShape().GetCenter();
627 const VECTOR2I pt = GetShape().GetEnd();
628
631 ChangeValue( PT_PT_X, pt.x );
632 ChangeValue( PT_PT_Y, pt.y );
633 }
634};
635
636
638{
639public:
641 {
650
652 };
653
654 BEZIER_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
655 GEOM_SYNCER( aShape, aBoundCtrls )
656 {
657 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
658 wxASSERT( GetShape().GetShape() == SHAPE_T::BEZIER );
659
661 [this]()
662 {
664 } );
665 }
666
667 void updateAll() override
668 {
669 updateBezier();
670 }
671
673 {
675 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
678
679 GetShape().SetStart( p0 );
680 GetShape().SetEnd( p1 );
681 GetShape().SetBezierC1( c1 );
682 GetShape().SetBezierC2( c2 );
683 }
684
686 {
687 const VECTOR2I p0 = GetShape().GetStart();
688 const VECTOR2I p1 = GetShape().GetEnd();
689 const VECTOR2I c1 = GetShape().GetBezierC1();
690 const VECTOR2I c2 = GetShape().GetBezierC2();
691
692 ChangeValue( START_X, p0.x );
693 ChangeValue( START_Y, p0.y );
694 ChangeValue( END_X, p1.x );
695 ChangeValue( END_Y, p1.y );
696 ChangeValue( CTRL1_X, c1.x );
697 ChangeValue( CTRL1_Y, c1.y );
698 ChangeValue( CTRL2_X, c2.x );
699 ChangeValue( CTRL2_Y, c2.y );
700 }
701};
702
704{
705public:
708
709private:
710 bool TransferDataToWindow() override;
711 bool TransferDataFromWindow() override;
712
713 void onFilledCheckbox( wxCommandEvent& event ) override;
714
715 void onLayerSelection( wxCommandEvent& event ) override;
716
717 void onTechLayersChanged( wxCommandEvent& event ) override;
718
719 bool Validate() override;
720
722 {
724
725 m_netSelector->Enable( isCopper );
726 m_netLabel->Enable( isCopper );
727 }
728
730 {
732
733 m_techLayersLabel->Enable( isExtCopper );
734 m_hasSolderMask->Enable( isExtCopper );
735
736 bool showMaskMargin = isExtCopper && m_hasSolderMask->GetValue();
737
738 m_solderMaskMarginLabel->Enable( showMaskMargin );
739 m_solderMaskMarginCtrl->Enable( showMaskMargin );
740 m_solderMaskMarginUnit->Enable( showMaskMargin );
741 }
742
743private:
746
749
750 std::vector<BOUND_CONTROL> m_boundCtrls;
751 std::unique_ptr<GEOM_SYNCER> m_geomSync;
753};
754
755
756static void AddXYPointToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
757 const wxString aName, bool aRelative,
758 std::vector<BOUND_CONTROL>& aBoundCtrls )
759{
760 // Name
761 // X [Ctrl] mm
762 // Y [Ctrl] mm
763 wxWindow* parent = aSizer.GetContainingWindow();
764
765 wxStaticText* titleLabel = new wxStaticText( parent, wxID_ANY, aName );
766 aSizer.Add( titleLabel, wxGBPosition( row, col ), wxGBSpan( 1, 3 ),
767 wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL | wxALL | wxEXPAND );
768 row++;
769
770 for( size_t coord = 0; coord < 2; ++coord )
771 {
772 wxStaticText* label =
773 new wxStaticText( parent, wxID_ANY, coord == 0 ? _( "X" ) : _( "Y" ) );
774 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
775 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
776
777 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, "" );
778 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
779 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
780
781 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
782 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
783 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
784
785 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
786
787 if( aRelative )
788 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::REL_X_COORD
790 else
791 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::ABS_X_COORD
793
794 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
795 row++;
796 }
797
798 if( !aSizer.IsColGrowable( col + 1 ) )
799 aSizer.AddGrowableCol( col + 1 );
800}
801
802
803void AddFieldToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
804 const wxString aName, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType,
805 bool aIsAngle, std::vector<BOUND_CONTROL>& aBoundCtrls )
806{
807 // Name [Ctrl] mm
808 wxWindow* parent = aSizer.GetContainingWindow();
809
810 wxStaticText* label = new wxStaticText( parent, wxID_ANY, aName );
811 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
812 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
813
814 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY );
815 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
816 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
817
818 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
819 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
820 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
821
822 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
823 binder->SetCoordType( aCoordType );
824
825 if( aIsAngle )
826 {
827 binder->SetPrecision( 4 );
828 binder->SetUnits( EDA_UNITS::DEGREES );
829 }
830
831 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
832
833 if( !aSizer.IsColGrowable( col + 1 ) )
834 aSizer.AddGrowableCol( col + 1 );
835}
836
837
838static std::map<SHAPE_T, int> s_lastTabForShape;
839
840
843 m_parent( aParent ),
844 m_item( aShape ),
845 m_thickness( aParent, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits ),
846 m_solderMaskMargin( aParent, m_solderMaskMarginLabel, m_solderMaskMarginCtrl, m_solderMaskMarginUnit ),
847 m_workingCopy( *m_item )
848{
849 SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
850 m_hash_key = TO_UTF8( GetTitle() );
851
852 wxFont infoFont = KIUI::GetInfoFont( this );
853 m_techLayersLabel->SetFont( infoFont );
854
855 // All the pages exist in the WxFB template, but we'll scrap the ones we don't
856 // use. Constructing on-demand would work fine too.
857 std::set<int> shownPages;
858
859 const auto showPage = [&]( wxSizer& aMainSizer, bool aSelect = false )
860 {
861 // Get the parent of the sizer, which is the panel
862 wxWindow* page = aMainSizer.GetContainingWindow();
863 wxCHECK( page, /* void */ );
864 page->Layout();
865
866 const int pageIdx = m_notebookShapeDefs->FindPage( page );
867 shownPages.insert( pageIdx );
868
869 if( aSelect )
870 m_notebookShapeDefs->SetSelection( pageIdx );
871 };
872
873 switch( m_item->GetShape() )
874 {
875 case SHAPE_T::RECTANGLE:
876 // For all these functions, it's very important that the fields are added in the same order
877 // as the CTRL_IDX enums in the GEOM_SYNCER classes.
878 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 0, _( "Start Point" ), false, m_boundCtrls);
879 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 3, _( "End Point" ), false, m_boundCtrls );
880
881 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 0, _( "Start Point" ), false, m_boundCtrls);
882 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 3, _( "Size" ), true, m_boundCtrls );
883
884 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 0, _( "Center" ), false, m_boundCtrls);
885 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 3, _( "Size" ), true, m_boundCtrls );
886
887 m_geomSync = std::make_unique<RECTANGLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
888
889 showPage( *m_gbsRectangleByCorners, true );
890 showPage( *m_gbsRectangleByCornerSize );
891 showPage( *m_gbsRectangleByCenterSize );
892 break;
893
894 case SHAPE_T::SEGMENT:
895
896 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 0, _( "Start Point" ), false, m_boundCtrls);
897 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 3, _( "End Point" ), false, m_boundCtrls);
898
899 AddXYPointToSizer( *aParent, *m_gbsLineByLengthAngle, 0, 0, _( "Start Point" ), false, m_boundCtrls);
902
903 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 0, _( "Start Point" ), false, m_boundCtrls );
904 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
905
906 m_geomSync = std::make_unique<LINE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
907
908 showPage( *m_gbsLineByEnds, true );
909 showPage( *m_gbsLineByLengthAngle );
910 showPage( *m_gbsLineByStartMid );
911 break;
912
913 case SHAPE_T::ARC:
914 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 0, _( "Center" ), false, m_boundCtrls);
915 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 3, _( "Start Point" ), false, m_boundCtrls);
916 AddFieldToSizer( *aParent, *m_gbsArcByCSA, 3, 0, _( "Start Angle" ), ORIGIN_TRANSFORMS::NOT_A_COORD, true, m_boundCtrls );
917
918 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 0, _( "Start Point" ), false, m_boundCtrls);
919 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 3, _( "Mid Point" ), false, m_boundCtrls);
920 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 3, 0, _( "End Point" ), false, m_boundCtrls);
921
922 m_geomSync = std::make_unique<ARC_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
923
924 showPage( *m_gbsArcByCSA, true );
925 showPage( *m_gbsArcBySME );
926 break;
927
928 case SHAPE_T::CIRCLE:
929 AddXYPointToSizer( *aParent, *m_gbsCircleCenterRadius, 0, 0, _( "Center" ), false, m_boundCtrls);
931
932 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 0, _( "Center" ), false, m_boundCtrls);
933 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 3, _( "Point on Circle" ), false, m_boundCtrls);
934
935 m_geomSync = std::make_unique<CIRCLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
936
937 showPage( *m_gbsCircleCenterRadius, true );
938 showPage( *m_gbsCircleCenterPoint );
939 break;
940
941 case SHAPE_T::BEZIER:
942 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 0, _( "Start Point" ), false, m_boundCtrls);
943 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 3, _( "End Point" ), false, m_boundCtrls);
944 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 0, _( "Control Point 1" ), false, m_boundCtrls);
945 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 3, _( "Control Point 2" ), false, m_boundCtrls);
946
947 m_geomSync = std::make_unique<BEZIER_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
948
949 showPage( *m_gbsBezier, TRUE );
950 break;
951
952 case SHAPE_T::POLY:
953 m_notebookShapeDefs->Hide();
954 // Nothing to do here...yet
955 break;
956
957 case SHAPE_T::UNDEFINED:
958 wxFAIL_MSG( "Undefined shape" );
959 break;
960 }
961
962 // Remove any tabs not used (Hide() doesn't work on Windows)
963 for( int i = m_notebookShapeDefs->GetPageCount() - 1; i >= 0; --i )
964 {
965 if( shownPages.count( i ) == 0 )
966 m_notebookShapeDefs->RemovePage( i );
967 }
968
969 // Used the last saved tab if any
970 if( s_lastTabForShape.count( m_item->GetShape() ) > 0 )
971 {
973 }
974
975 // Find the first control in the shown tab
976 wxWindow* tabPanel = m_notebookShapeDefs->GetCurrentPage();
977 for( size_t i = 0; i < m_boundCtrls.size(); ++i )
978 {
979 if( m_boundCtrls[i].m_Ctrl->IsDescendant( tabPanel ) )
980 {
981 m_boundCtrls[i].m_Ctrl->SetFocus();
982 break;
983 }
984 }
985
986 // Do not allow locking items in the footprint editor
987 m_locked->Show( dynamic_cast<PCB_EDIT_FRAME*>( aParent ) != nullptr );
988
989 // Configure the layers list selector
991 {
992 LSET forbiddenLayers = LSET::ForbiddenFootprintLayers();
993
994 // If someone went to the trouble of setting the layer in a text editor, then there's
995 // very little sense in nagging them about it.
996 forbiddenLayers.set( m_item->GetLayer(), false );
997
999 }
1000
1001 for( const auto& [ lineStyle, lineStyleDesc ] : lineTypeNames )
1002 m_lineStyleCombo->Append( lineStyleDesc.name, KiBitmapBundle( lineStyleDesc.bitmap ) );
1003
1007
1008 m_netSelector->SetBoard( aParent->GetBoard() );
1009 m_netSelector->SetNetInfo( &aParent->GetBoard()->GetNetInfo() );
1010
1012 {
1013 m_netLabel->Hide();
1014 m_netSelector->Hide();
1015 }
1016 else
1017 {
1018 int net = aShape->GetNetCode();
1019
1020 if( net >= 0 )
1021 {
1023 }
1024 else
1025 {
1028 }
1029 }
1030
1031 if( m_item->GetShape() == SHAPE_T::ARC || m_item->GetShape() == SHAPE_T::SEGMENT )
1032 m_filledCtrl->Show( false );
1033
1035
1036 // Now all widgets have the size fixed, call FinishDialogSettings
1038}
1039
1040
1042{
1043 wxCHECK_RET( aShape, wxT( "ShowGraphicItemPropertiesDialog() error: NULL item" ) );
1044
1045 DIALOG_SHAPE_PROPERTIES dlg( this, aShape );
1046
1047 if( dlg.ShowQuasiModal() == wxID_OK )
1048 {
1049 if( aShape->IsOnLayer( GetActiveLayer() ) )
1050 {
1052 drawingTool->SetStroke( aShape->GetStroke(), GetActiveLayer() );
1053 }
1054 }
1055}
1056
1057
1059{
1061 enableNetInfo();
1062
1064}
1065
1066
1068{
1069 if( m_filledCtrl->GetValue() )
1070 {
1071 m_lineStyleCombo->SetSelection( 0 );
1072 m_lineStyleLabel->Enable( false );
1073 m_lineStyleCombo->Enable( false );
1074 }
1075 else
1076 {
1078
1079 if( style == LINE_STYLE::DEFAULT )
1080 style = LINE_STYLE::SOLID;
1081
1082 if( (int) style < (int) lineTypeNames.size() )
1083 m_lineStyleCombo->SetSelection( (int) style );
1084
1085 m_lineStyleLabel->Enable( true );
1086 m_lineStyleCombo->Enable( true );
1087 }
1088}
1089
1090
1092{
1094}
1095
1096
1098{
1099 if( !m_item )
1100 return false;
1101
1102 // Not al shapes have a syncer (e.g. polygons)
1103 if( m_geomSync )
1104 m_geomSync->SetShape( *m_item );
1105
1106 m_filledCtrl->SetValue( m_item->IsFilled() );
1107 m_locked->SetValue( m_item->IsLocked() );
1108
1110
1111 int style = static_cast<int>( m_item->GetStroke().GetLineStyle() );
1112
1113 if( style >= 0 && style < (int) lineTypeNames.size() )
1114 m_lineStyleCombo->SetSelection( style );
1115 else
1116 m_lineStyleCombo->SetSelection( 0 );
1117
1119
1120 m_hasSolderMask->SetValue( m_item->HasSolderMask() );
1121
1122 if( m_item->GetLocalSolderMaskMargin().has_value() )
1124 else
1125 m_solderMaskMargin.SetValue( wxEmptyString );
1126
1127 enableNetInfo();
1129
1130 return DIALOG_SHAPE_PROPERTIES_BASE::TransferDataToWindow();
1131}
1132
1133
1135{
1136 if( !DIALOG_SHAPE_PROPERTIES_BASE::TransferDataFromWindow() )
1137 return false;
1138
1139 if( !m_item )
1140 return true;
1141
1143
1144 BOARD_COMMIT commit( m_parent );
1145 commit.Modify( m_item );
1146
1147 bool pushCommit = ( m_item->GetEditFlags() == 0 );
1148
1149 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
1150 // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
1151 if( !pushCommit )
1153
1155
1156 bool wasLocked = m_item->IsLocked();
1157
1158 m_item->SetFilled( m_filledCtrl->GetValue() );
1159 m_item->SetLocked( m_locked->GetValue() );
1160
1161 STROKE_PARAMS stroke = m_item->GetStroke();
1162
1163 stroke.SetWidth( m_thickness.GetIntValue() );
1164
1165 auto it = lineTypeNames.begin();
1166 std::advance( it, m_lineStyleCombo->GetSelection() );
1167
1168 if( it == lineTypeNames.end() )
1169 stroke.SetLineStyle( LINE_STYLE::SOLID );
1170 else
1171 stroke.SetLineStyle( it->first );
1172
1173 m_item->SetStroke( stroke );
1174
1175 m_item->SetLayer( ToLAYER_ID( layer ) );
1176
1177 m_item->SetHasSolderMask( m_hasSolderMask->GetValue() );
1178
1181 else
1183
1185
1186 if( m_item->IsOnCopperLayer() )
1188 else
1189 m_item->SetNetCode( -1 );
1190
1191 if( pushCommit )
1192 commit.Push( _( "Edit Shape Properties" ) );
1193
1194 // Save the tab
1196
1197 // Notify clients which treat locked and unlocked items differently (ie: POINT_EDITOR)
1198 if( wasLocked != m_item->IsLocked() )
1200
1201 return true;
1202}
1203
1204
1206{
1207 wxArrayString errors;
1208
1209 if( !DIALOG_SHAPE_PROPERTIES_BASE::Validate() )
1210 return false;
1211
1212 if( m_geomSync )
1213 m_geomSync->Validate( errors );
1214
1215 // Type specific checks.
1216 switch( m_item->GetShape() )
1217 {
1218 case SHAPE_T::ARC:
1219 if( m_thickness.GetValue() <= 0 )
1220 errors.Add( _( "Line width must be greater than zero." ) );
1221 break;
1222
1223 case SHAPE_T::CIRCLE:
1224 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1225 errors.Add( _( "Line width must be greater than zero for an unfilled circle." ) );
1226
1227 break;
1228
1229 case SHAPE_T::RECTANGLE:
1230 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1231 errors.Add( _( "Line width must be greater than zero for an unfilled rectangle." ) );
1232
1233 break;
1234
1235 case SHAPE_T::POLY:
1236 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1237 errors.Add( _( "Line width must be greater than zero for an unfilled polygon." ) );
1238
1239 break;
1240
1241 case SHAPE_T::SEGMENT:
1242 if( m_thickness.GetValue() <= 0 )
1243 errors.Add( _( "Line width must be greater than zero." ) );
1244
1245 break;
1246
1247 case SHAPE_T::BEZIER:
1248 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1249 errors.Add( _( "Line width must be greater than zero for an unfilled curve." ) );
1250
1251 break;
1252
1253 default:
1255 break;
1256 }
1257
1258 if( errors.GetCount() )
1259 {
1260 HTML_MESSAGE_BOX dlg( this, _( "Error List" ) );
1261 dlg.ListSet( errors );
1262 dlg.ShowModal();
1263 }
1264
1265 return errors.GetCount() == 0;
1266}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
ARC_GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
bool Validate(wxArrayString &aErrs) const override
BASE_SET & set(size_t pos)
Definition: base_set.h:116
BEZIER_GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
virtual void SetLocked(bool aLocked)
Definition: board_item.h:330
virtual bool IsLocked() const
Definition: board_item.cpp:75
virtual bool IsOnCopperLayer() const
Definition: board_item.h:150
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:879
bool Validate(wxArrayString &aErrs) const override
CIRCLE_GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
DIALOG_SHAPE_PROPERTIES(SCH_BASE_FRAME *aParent, SCH_SHAPE *aShape)
bool TransferDataToWindow() override
void onLayerSelection(wxCommandEvent &event) override
void onTechLayersChanged(wxCommandEvent &event) override
void onFilledCheckbox(wxCommandEvent &event) override
bool TransferDataFromWindow() override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
int ShowQuasiModal()
std::string m_hash_key
Definition: dialog_shim.h:230
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
Definition: drawing_tool.h:55
void SetStroke(const STROKE_PARAMS &aStroke, PCB_LAYER_ID aLayer)
Definition: drawing_tool.h:234
double AsDegrees() const
Definition: eda_angle.h:113
FRAME_T GetFrameType() const
The base class for create windows for drawing purpose.
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:133
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:213
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:212
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:792
bool IsFilled() const
Definition: eda_shape.h:98
SHAPE_T GetShape() const
Definition: eda_shape.h:132
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:108
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:739
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
void SetRadius(int aX)
Definition: eda_shape.h:196
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:209
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:873
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:340
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:210
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:940
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:810
static const TOOL_EVENT SelectedEvent
Definition: actions.h:292
A class that operates over a list of BOUND_CONTROLs and keeps them in sync with a PCB_SHAPE.
void ChangeValue(size_t aIndex, int aValue)
wxTextCtrl * GetCtrl(size_t aIndex) const
EDA_ANGLE GetAngleValue(size_t aIndex) const
void BindCtrls(size_t aFrom, size_t aTo, std::function< void()> aCb)
void SetShape(PCB_SHAPE &aShape)
virtual void updateAll()=0
int GetIntValue(size_t aIndex) const
virtual bool Validate(wxArrayString &aErrs) const
const PCB_SHAPE & GetShape() const
void ChangeAngleValue(size_t aIndex, const EDA_ANGLE &aValue)
std::vector< BOUND_CONTROL > & m_boundCtrls
GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
void ListSet(const wxString &aList)
Add a list of items.
int SetLayerSelection(int layer)
bool SetLayersHotkeys(bool value)
LINE_GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET ForbiddenFootprintLayers()
Layers which are not allowed within footprint definitions.
Definition: lset.cpp:692
void SetNetInfo(const NETINFO_LIST *aNetInfoList)
void SetBoard(BOARD *aBoard)
int GetSelectedNetcode()
void SetIndeterminateString(const wxString &aString)
void SetSelectedNetcode(int aNetcode)
void SetIndeterminate()
COORD_TYPES_T
The supported Display Origin Transform types.
Common, abstract interface for edit frames.
void ShowGraphicItemPropertiesDialog(PCB_SHAPE *aShape)
virtual PCB_LAYER_ID GetActiveLayer() const
BOARD * GetBoard() const
The main frame for Pcbnew.
void SetBoardFrame(PCB_BASE_FRAME *aFrame)
void SetNotAllowedLayerSet(LSET aMask)
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_shape.h:186
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
bool HasSolderMask() const
Definition: pcb_shape.h:183
void SetHasSolderMask(bool aVal)
Definition: pcb_shape.h:182
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:170
wxString GetFriendlyName() const override
Definition: pcb_shape.h:64
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:89
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: pcb_shape.h:185
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_shape.cpp:200
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
Class that keeps a rectangle's various fields all up to date.
bool Validate(wxArrayString &aErrs) const override
RECTANGLE_GEOM_SYNCER(PCB_SHAPE &aShape, std::vector< BOUND_CONTROL > &aBoundCtrls)
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
void SetWidth(int aWidth)
LINE_STYLE GetLineStyle() const
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:167
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
int GetIntValue()
Definition: unit_binder.h:129
virtual long long int GetValue()
Return the current value in Internal Units.
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
bool IsNull() const
Return true if the control holds no value (ie: empty string, not 0).
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:561
#define _(s)
#define IN_EDIT
Item currently edited.
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:617
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition: layer_ids.h:628
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:710
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:155
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
void AddFieldToSizer(EDA_DRAW_FRAME &aFrame, wxGridBagSizer &aSizer, int row, int col, const wxString aName, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType, bool aIsAngle, std::vector< BOUND_CONTROL > &aBoundCtrls)
static std::map< SHAPE_T, int > s_lastTabForShape
static void AddXYPointToSizer(EDA_DRAW_FRAME &aFrame, wxGridBagSizer &aSizer, int row, int col, const wxString aName, bool aRelative, std::vector< BOUND_CONTROL > &aBoundCtrls)
static bool isCopper(const PNS::ITEM *aItem)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:398
const std::map< LINE_STYLE, struct LINE_STYLE_DESC > lineTypeNames
Conversion map between LINE_STYLE values and style names displayed.
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
std::unique_ptr< UNIT_BINDER > m_Binder
VECTOR2I center
int radius
VECTOR2I end
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
Definition: trigo.h:77
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
#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:695