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>
37#include <tool/tool_manager.h>
38#include <tool/actions.h>
39#include <pcb_shape.h>
40#include <macros.h>
41#include <widgets/unit_binder.h>
42
44#include <tools/drawing_tool.h>
45
46
48{
49 std::unique_ptr<UNIT_BINDER> m_Binder;
50 wxTextCtrl* m_Ctrl;
51};
52
53
63class GEOM_SYNCER : public wxEvtHandler
64{
65public:
66 GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
67 m_shape( aShape ),
68 m_boundCtrls( aBoundCtrls )
69 {
70 }
71
72 void BindCtrls( size_t aFrom, size_t aTo, std::function<void()> aCb )
73 {
74 wxCHECK( aFrom < m_boundCtrls.size(), /* void */ );
75 wxCHECK( aTo < m_boundCtrls.size(), /* void */ );
76
77 for( size_t i = aFrom; i <= aTo; ++i )
78 {
79 m_boundCtrls[i].m_Ctrl->Bind( wxEVT_TEXT,
80 [aCb]( wxCommandEvent& aEvent )
81 {
82 aCb();
83 } );
84 }
85 }
86
87 void SetShape( PCB_SHAPE& aShape )
88 {
89 m_shape = aShape;
90 updateAll();
91 }
92
93 virtual bool Validate( wxArrayString& aErrs ) const { return true; }
94
95protected:
96 virtual void updateAll() = 0;
97
98 wxTextCtrl* GetCtrl( size_t aIndex ) const
99 {
100 wxCHECK( aIndex < m_boundCtrls.size(), nullptr );
101 return m_boundCtrls[aIndex].m_Ctrl;
102 }
103
104 int GetIntValue( size_t aIndex ) const
105 {
106 wxCHECK( aIndex < m_boundCtrls.size(), 0.0 );
107 return static_cast<int>( m_boundCtrls[aIndex].m_Binder->GetValue() );
108 }
109
110 EDA_ANGLE GetAngleValue( size_t aIndex ) const
111 {
112 wxCHECK( aIndex < m_boundCtrls.size(), EDA_ANGLE() );
113 return m_boundCtrls[aIndex].m_Binder->GetAngleValue();
114 }
115
116 void ChangeValue( size_t aIndex, int aValue )
117 {
118 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
119 m_boundCtrls[aIndex].m_Binder->ChangeValue( aValue );
120 }
121
122 void ChangeAngleValue( size_t aIndex, const EDA_ANGLE& aValue )
123 {
124 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
125 m_boundCtrls[aIndex].m_Binder->ChangeAngleValue( aValue );
126 }
127
129
130 const PCB_SHAPE& GetShape() const { return m_shape; }
131
132private:
134 std::vector<BOUND_CONTROL>& m_boundCtrls;
135};
136
137
142{
143public:
145 {
158
160 };
161
162 RECTANGLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
163 GEOM_SYNCER( aShape, aBoundCtrls )
164 {
165 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
166 wxASSERT( GetShape().GetShape() == SHAPE_T::RECTANGLE );
167
169 [this]()
170 {
172 } );
173
175 [this]()
176 {
178 } );
179
181 [this]()
182 {
184 } );
185 }
186
187 bool Validate( wxArrayString& aErrs ) const override
188 {
190 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
191
192 if( p0 == p1 )
193 {
194 aErrs.push_back( _( "Rectangle cannot be zero-sized." ) );
195 return false;
196 }
197
198 return true;
199 }
200
201 void updateAll() override
202 {
206 }
207
209 {
211 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
212
213 GetShape().SetStart( p0 );
214 GetShape().SetEnd( p1 );
215
218 }
219
221 {
222 const VECTOR2I p0 = GetShape().GetStart();
223 const VECTOR2I p1 = GetShape().GetEnd();
224
225 ChangeValue( START_X, p0.x );
226 ChangeValue( START_Y, p0.y );
227 ChangeValue( END_X, p1.x );
228 ChangeValue( END_Y, p1.y );
229 }
230
232 {
234 const VECTOR2I size{ GetIntValue( CORNER_W ), GetIntValue( CORNER_H ) };
235
236 GetShape().SetStart( p0 );
237 GetShape().SetEnd( p0 + size );
238
241 }
242
244 {
245 const VECTOR2I p0 = GetShape().GetStart();
246
247 ChangeValue( CORNER_X, p0.x );
248 ChangeValue( CORNER_Y, p0.y );
249 ChangeValue( CORNER_W, GetShape().GetRectangleWidth() );
250 ChangeValue( CORNER_H, GetShape().GetRectangleHeight() );
251 }
252
254 {
256 const VECTOR2I size = { GetIntValue( CENTER_W ), GetIntValue( CENTER_H ) };
257
258 GetShape().SetStart( center - size / 2 );
259 GetShape().SetEnd( center + size / 2 );
260
263 }
264
266 {
267 const VECTOR2I c = GetShape().GetCenter();
268
269 ChangeValue( CENTER_X, c.x );
270 ChangeValue( CENTER_Y, c.y );
271 ChangeValue( CENTER_W, GetShape().GetRectangleWidth() );
272 ChangeValue( CENTER_H, GetShape().GetRectangleHeight() );
273 }
274};
275
276
278{
279public:
281 {
286
291
296
298 };
299
300 LINE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
301 GEOM_SYNCER( aShape, aBoundCtrls )
302 {
303 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
304 wxASSERT( GetShape().GetShape() == SHAPE_T::SEGMENT );
305
307 [this]()
308 {
309 OnEndsChange();
310 } );
311
313 [this]()
314 {
316 } );
317
319 [this]()
320 {
322 } );
323 }
324
325 void updateAll() override
326 {
327 updateEnds();
328 updatePolar();
330 }
331
333 {
335 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
336
337 GetShape().SetStart( p0 );
338 GetShape().SetEnd( p1 );
339
340 updatePolar();
342 }
343
345 {
346 const VECTOR2I p0 = GetShape().GetStart();
347 const VECTOR2I p1 = GetShape().GetEnd();
348
349 ChangeValue( START_X, p0.x );
350 ChangeValue( START_Y, p0.y );
351 ChangeValue( END_X, p1.x );
352 ChangeValue( END_Y, p1.y );
353 }
354
356 {
358 const int length = GetIntValue( LENGTH );
359 const EDA_ANGLE angle = GetAngleValue( ANGLE );
360
361 const VECTOR2I polar = GetRotated( VECTOR2I{ length, 0 }, angle );
362
363 GetShape().SetStart( p0 );
364 GetShape().SetEnd( p0 + polar );
365
366 updateEnds();
368 }
369
371 {
372 const VECTOR2I p0 = GetShape().GetStart();
373 const VECTOR2I p1 = GetShape().GetEnd();
374
377 ChangeValue( LENGTH, p0.Distance( p1 ) );
378 ChangeAngleValue( ANGLE, -EDA_ANGLE( p1 - p0 ) );
379 }
380
382 {
384 const VECTOR2I mid{ GetIntValue( MID_X ), GetIntValue( MID_Y ) };
385
386 GetShape().SetStart( start );
387 GetShape().SetEnd( mid - ( start - mid ) );
388
389 updateEnds();
390 updatePolar();
391 }
392
394 {
395 const VECTOR2I s = GetShape().GetStart();
396 const VECTOR2I c = GetShape().GetCenter();
397
398 ChangeValue( MID_X, c.x );
399 ChangeValue( MID_Y, c.y );
402 }
403};
404
405
407{
408public:
410 {
411 //CSA
417
424
426 };
427
428 ARC_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
429 GEOM_SYNCER( aShape, aBoundCtrls )
430 {
431 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
432 wxASSERT( GetShape().GetShape() == SHAPE_T::ARC );
433
435 [this]()
436 {
437 OnCSAChange();
438 } );
439
441 [this]()
442 {
443 OnSMEChange();
444 } );
445 }
446
447 bool Validate( wxArrayString& aErrs ) const override
448 {
449 const EDA_ANGLE angle = GetAngleValue( CSA_ANGLE );
450
451 if( angle == ANGLE_0 )
452 {
453 aErrs.push_back( _( "Arc angle must be greater than 0" ) );
454 return false;
455 }
456
460
461 if( start == mid || mid == end || start == end )
462 {
463 aErrs.push_back( _( "Arc must have 3 distinct points" ) );
464 return false;
465 }
466 else
467 {
468 const VECTOR2D center = CalcArcCenter( start, end, angle );
469
470 double radius = ( center - start ).EuclideanNorm();
471 double max_offset = std::max( std::abs( center.x ), std::abs( center.y ) ) + radius;
472 VECTOR2I center_i = VECTOR2I( center.x, center.y );
473
474 if( max_offset >= ( std::numeric_limits<VECTOR2I::coord_type>::max() / 2.0 )
475 || center_i == start || center_i == end )
476 {
477 aErrs.push_back( wxString::Format( _( "Invalid Arc with radius %f and angle %f." ),
478 radius, angle.AsDegrees() ) );
479 return false;
480 }
481 }
482
483 return true;
484 }
485
486 void updateAll() override
487 {
488 updateCSA();
489 updateSME();
490 }
491
493 {
496 const EDA_ANGLE angle{ GetAngleValue( CSA_ANGLE ) };
497
499 GetShape().SetStart( start );
500 GetShape().SetArcAngleAndEnd( angle );
501
502 updateSME();
503 }
504
506 {
507 const VECTOR2I center = GetShape().GetCenter();
508 const VECTOR2I start = GetShape().GetStart();
509
512 ChangeValue( CSA_START_X, start.x );
513 ChangeValue( CSA_START_Y, start.y );
514 ChangeAngleValue( CSA_ANGLE, GetShape().GetArcAngle() );
515 }
516
518 {
522
523 GetShape().SetArcGeometry( p0, p1, p2 );
524
525 updateCSA();
526 }
527
529 {
530 const VECTOR2I p0 = GetShape().GetStart();
531 const VECTOR2I p1 = GetShape().GetArcMid();
532 const VECTOR2I p2 = GetShape().GetEnd();
533
536 ChangeValue( SME_MID_X, p1.x );
537 ChangeValue( SME_MID_Y, p1.y );
538 ChangeValue( SME_END_X, p2.x );
539 ChangeValue( SME_END_Y, p2.y );
540 }
541};
542
543
545{
546public:
548 {
556
558 };
559
560 CIRCLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
561 GEOM_SYNCER( aShape, aBoundCtrls )
562 {
563 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
564 wxASSERT( GetShape().GetShape() == SHAPE_T::CIRCLE );
565
567 [this]()
568 {
570 } );
571
573 [this]()
574 {
576 } );
577 }
578
579 void updateAll() override
580 {
583 }
584
585 bool Validate( wxArrayString& aErrs ) const override
586 {
587 if( GetIntValue( RADIUS ) <= 0 )
588 {
589 aErrs.push_back( _( "Radius must be greater than 0" ) );
590 return false;
591 }
592
593 return true;
594 }
595
597 {
599 const int radius = GetIntValue( RADIUS );
600
603
605 }
606
608 {
609 const VECTOR2I center = GetShape().GetCenter();
610
613 ChangeValue( RADIUS, GetShape().GetRadius() );
614 }
615
617 {
620
622 GetShape().SetEnd( pt );
623
625 }
626
628 {
629 const VECTOR2I center = GetShape().GetCenter();
630 const VECTOR2I pt = GetShape().GetEnd();
631
634 ChangeValue( PT_PT_X, pt.x );
635 ChangeValue( PT_PT_Y, pt.y );
636 }
637};
638
639
641{
642public:
644 {
653
655 };
656
657 BEZIER_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
658 GEOM_SYNCER( aShape, aBoundCtrls )
659 {
660 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
661 wxASSERT( GetShape().GetShape() == SHAPE_T::BEZIER );
662
664 [this]()
665 {
667 } );
668 }
669
670 void updateAll() override
671 {
672 updateBezier();
673 }
674
676 {
678 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
681
682 GetShape().SetStart( p0 );
683 GetShape().SetEnd( p1 );
684 GetShape().SetBezierC1( c1 );
685 GetShape().SetBezierC2( c2 );
686 }
687
689 {
690 const VECTOR2I p0 = GetShape().GetStart();
691 const VECTOR2I p1 = GetShape().GetEnd();
692 const VECTOR2I c1 = GetShape().GetBezierC1();
693 const VECTOR2I c2 = GetShape().GetBezierC2();
694
695 ChangeValue( START_X, p0.x );
696 ChangeValue( START_Y, p0.y );
697 ChangeValue( END_X, p1.x );
698 ChangeValue( END_Y, p1.y );
699 ChangeValue( CTRL1_X, c1.x );
700 ChangeValue( CTRL1_Y, c1.y );
701 ChangeValue( CTRL2_X, c2.x );
702 ChangeValue( CTRL2_Y, c2.y );
703 }
704};
705
707{
708public:
711
712private:
713 bool TransferDataToWindow() override;
714 bool TransferDataFromWindow() override;
715
716 void onLayerSelection( wxCommandEvent& event ) override;
717
718 void onTechLayersChanged( wxCommandEvent& event ) override;
719
720 bool Validate() override;
721
723 {
725
726 m_netSelector->Enable( isCopper );
727 m_netLabel->Enable( isCopper );
728 }
729
731 {
733
734 m_techLayersLabel->Enable( isExtCopper );
735 m_hasSolderMask->Enable( isExtCopper );
736
737 bool showMaskMargin = isExtCopper && m_hasSolderMask->GetValue();
738
739 m_solderMaskMarginLabel->Enable( showMaskMargin );
740 m_solderMaskMarginCtrl->Enable( showMaskMargin );
741 m_solderMaskMarginUnit->Enable( showMaskMargin );
742 }
743
744private:
747
750
751 std::vector<BOUND_CONTROL> m_boundCtrls;
752 std::unique_ptr<GEOM_SYNCER> m_geomSync;
754};
755
756
757static void AddXYPointToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
758 const wxString& aName, bool aRelative, 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 = new wxStaticText( parent, wxID_ANY, coord == 0 ? _( "X:" ) : _( "Y:" ) );
773 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
774 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, col > 0 ? 20 : 5 );
775
776 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, "" );
777 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
778 wxEXPAND | wxALIGN_CENTER_VERTICAL, 5 );
779
780 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
781 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
782 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
783
784 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
785
786 if( aRelative )
787 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::REL_X_COORD : ORIGIN_TRANSFORMS::REL_Y_COORD );
788 else
789 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::ABS_X_COORD : ORIGIN_TRANSFORMS::ABS_Y_COORD );
790
791 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
792 row++;
793 }
794
795 if( !aSizer.IsColGrowable( col + 1 ) )
796 aSizer.AddGrowableCol( col + 1 );
797}
798
799
800void AddFieldToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
801 const wxString& aName, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType,
802 bool aIsAngle, std::vector<BOUND_CONTROL>& aBoundCtrls )
803{
804 // Name: [Ctrl] mm
805 wxWindow* parent = aSizer.GetContainingWindow();
806
807 wxStaticText* label = new wxStaticText( parent, wxID_ANY, aName + wxS( ":" ) );
808 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
809 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, col > 0 ? 20 : 5 );
810
811 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY );
812 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
813 wxEXPAND | wxALIGN_CENTER_VERTICAL, 5 );
814
815 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
816 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
817 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
818
819 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
820 binder->SetCoordType( aCoordType );
821
822 if( aIsAngle )
823 {
824 binder->SetPrecision( 4 );
825 binder->SetUnits( EDA_UNITS::DEGREES );
826 }
827
828 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
829
830 if( !aSizer.IsColGrowable( col + 1 ) )
831 aSizer.AddGrowableCol( col + 1 );
832}
833
834
835static std::map<SHAPE_T, int> s_lastTabForShape;
836
837
840 m_parent( aParent ),
841 m_item( aShape ),
842 m_thickness( aParent, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits ),
843 m_solderMaskMargin( aParent, m_solderMaskMarginLabel, m_solderMaskMarginCtrl, m_solderMaskMarginUnit ),
844 m_workingCopy( *m_item )
845{
846 SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
847 m_hash_key = TO_UTF8( GetTitle() );
848
849 wxFont infoFont = KIUI::GetSmallInfoFont( this );
850 m_techLayersLabel->SetFont( infoFont );
851
852 // All the pages exist in the WxFB template, but we'll scrap the ones we don't
853 // use. Constructing on-demand would work fine too.
854 std::set<int> shownPages;
855
856 const auto showPage =
857 [&]( wxSizer& aMainSizer, bool aSelect = false )
858 {
859 // Get the parent of the sizer, which is the panel
860 wxWindow* page = aMainSizer.GetContainingWindow();
861 wxCHECK( page, /* void */ );
862 page->Layout();
863
864 const int pageIdx = m_notebookShapeDefs->FindPage( page );
865 shownPages.insert( pageIdx );
866
867 if( aSelect )
868 m_notebookShapeDefs->SetSelection( pageIdx );
869 };
870
871 switch( m_item->GetShape() )
872 {
873 case SHAPE_T::RECTANGLE:
874 // For all these functions, it's very important that the fields are added in the same order
875 // as the CTRL_IDX enums in the GEOM_SYNCER classes.
876 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 0, _( "Start Point" ), false, m_boundCtrls );
877 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 3, _( "End Point" ), false, m_boundCtrls );
878
879 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 0, _( "Start Point" ), false, m_boundCtrls );
880 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 3, _( "Size" ), true, m_boundCtrls );
881
882 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 0, _( "Center" ), false, m_boundCtrls );
883 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 3, _( "Size" ), true, m_boundCtrls );
884
885 m_geomSync = std::make_unique<RECTANGLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
886
887 showPage( *m_gbsRectangleByCorners, true );
888 showPage( *m_gbsRectangleByCornerSize );
889 showPage( *m_gbsRectangleByCenterSize );
890 break;
891
892 case SHAPE_T::SEGMENT:
893
894 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 0, _( "Start Point" ), false, m_boundCtrls );
895 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 3, _( "End Point" ), false, m_boundCtrls );
896
897 AddXYPointToSizer( *aParent, *m_gbsLineByLengthAngle, 0, 0, _( "Start Point" ), false, m_boundCtrls);
900
901 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 0, _( "Start Point" ), false, m_boundCtrls );
902 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
903
904 m_geomSync = std::make_unique<LINE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
905
906 showPage( *m_gbsLineByEnds, true );
907 showPage( *m_gbsLineByLengthAngle );
908 showPage( *m_gbsLineByStartMid );
909 break;
910
911 case SHAPE_T::ARC:
912 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 0, _( "Center" ), false, m_boundCtrls);
913 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 3, _( "Start Point" ), false, m_boundCtrls);
914 AddFieldToSizer( *aParent, *m_gbsArcByCSA, 3, 0, _( "Included Angle" ), ORIGIN_TRANSFORMS::NOT_A_COORD, true, m_boundCtrls );
915
916 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 0, _( "Start Point" ), false, m_boundCtrls );
917 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
918 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 3, 0, _( "End Point" ), false, m_boundCtrls );
919
920 m_geomSync = std::make_unique<ARC_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
921
922 showPage( *m_gbsArcByCSA, true );
923 showPage( *m_gbsArcBySME );
924 break;
925
926 case SHAPE_T::CIRCLE:
927 AddXYPointToSizer( *aParent, *m_gbsCircleCenterRadius, 0, 0, _( "Center" ), false, m_boundCtrls);
929
930 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 0, _( "Center" ), false, m_boundCtrls );
931 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 3, _( "Point on Circle" ), false, m_boundCtrls );
932
933 m_geomSync = std::make_unique<CIRCLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
934
935 showPage( *m_gbsCircleCenterRadius, true );
936 showPage( *m_gbsCircleCenterPoint );
937 break;
938
939 case SHAPE_T::BEZIER:
940 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 0, _( "Start Point" ), false, m_boundCtrls );
941 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 3, _( "End Point" ), false, m_boundCtrls );
942 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 0, _( "Control Point 1" ), false, m_boundCtrls );
943 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 3, _( "Control Point 2" ), false, m_boundCtrls );
944
945 m_geomSync = std::make_unique<BEZIER_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
946
947 showPage( *m_gbsBezier, TRUE );
948 break;
949
950 case SHAPE_T::POLY:
951 m_notebookShapeDefs->Hide();
952 // Nothing to do here...yet
953 break;
954
955 case SHAPE_T::UNDEFINED:
956 wxFAIL_MSG( "Undefined shape" );
957 break;
958 }
959
960 // Remove any tabs not used (Hide() doesn't work on Windows)
961 for( int i = (int) m_notebookShapeDefs->GetPageCount() - 1; i >= 0; --i )
962 {
963 if( shownPages.count( i ) == 0 )
964 m_notebookShapeDefs->RemovePage( i );
965 }
966
967 // Used the last saved tab if any
968 if( s_lastTabForShape.count( m_item->GetShape() ) > 0
969 && s_lastTabForShape[m_item->GetShape()] < (int) m_notebookShapeDefs->GetPageCount()
970 && s_lastTabForShape[m_item->GetShape()] >= 0 )
971 {
973 }
974
975 // Find the first control in the shown tab
976 wxWindow* tabPanel = m_notebookShapeDefs->GetCurrentPage();
977
978 for( size_t i = 0; i < m_boundCtrls.size(); ++i )
979 {
980 if( m_boundCtrls[i].m_Ctrl->IsDescendant( tabPanel ) )
981 {
982 m_boundCtrls[i].m_Ctrl->SetFocus();
983 break;
984 }
985 }
986
987 // Do not allow locking items in the footprint editor
988 m_locked->Show( dynamic_cast<PCB_EDIT_FRAME*>( aParent ) != nullptr );
989
990 // Configure the layers list selector
992 {
993 LSET forbiddenLayers = LSET::ForbiddenFootprintLayers();
994
995 // If someone went to the trouble of setting the layer in a text editor, then there's
996 // very little sense in nagging them about it.
997 forbiddenLayers.set( m_item->GetLayer(), false );
998
1000 }
1001
1002 for( const auto& [ lineStyle, lineStyleDesc ] : lineTypeNames )
1003 m_lineStyleCombo->Append( lineStyleDesc.name, KiBitmapBundle( lineStyleDesc.bitmap ) );
1004
1008
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
1032 || m_item->GetShape() == SHAPE_T::SEGMENT
1033 || m_item->GetShape() == SHAPE_T::BEZIER )
1034 {
1035 m_fillLabel->Show( false );
1036 m_fillCtrl->Show( false );
1037 }
1038
1040
1041 // Now all widgets have the size fixed, call FinishDialogSettings
1043}
1044
1045
1047{
1048 wxCHECK_RET( aShape, wxT( "ShowGraphicItemPropertiesDialog() error: NULL item" ) );
1049
1050 DIALOG_SHAPE_PROPERTIES dlg( this, aShape );
1051
1052 if( dlg.ShowQuasiModal() == wxID_OK )
1053 {
1054 if( aShape->IsOnLayer( GetActiveLayer() ) )
1055 {
1057 drawingTool->SetStroke( aShape->GetStroke(), GetActiveLayer() );
1058 }
1059 }
1060}
1061
1062
1064{
1066 enableNetInfo();
1067
1069}
1070
1071
1073{
1075}
1076
1077
1079{
1080 if( !m_item )
1081 return false;
1082
1083 // Not al shapes have a syncer (e.g. polygons)
1084 if( m_geomSync )
1085 m_geomSync->SetShape( *m_item );
1086
1087 m_fillCtrl->SetSelection( m_item->GetFillModeProp() );
1088 m_locked->SetValue( m_item->IsLocked() );
1089
1091
1092 int style = static_cast<int>( m_item->GetStroke().GetLineStyle() );
1093
1094 if( style >= 0 && style < (int) lineTypeNames.size() )
1095 m_lineStyleCombo->SetSelection( style );
1096 else
1097 m_lineStyleCombo->SetSelection( 0 );
1098
1100
1101 m_hasSolderMask->SetValue( m_item->HasSolderMask() );
1102
1103 if( m_item->GetLocalSolderMaskMargin().has_value() )
1105 else
1106 m_solderMaskMargin.SetValue( wxEmptyString );
1107
1108 enableNetInfo();
1110
1111 return DIALOG_SHAPE_PROPERTIES_BASE::TransferDataToWindow();
1112}
1113
1114
1116{
1117 if( !DIALOG_SHAPE_PROPERTIES_BASE::TransferDataFromWindow() )
1118 return false;
1119
1120 if( !m_item )
1121 return true;
1122
1124
1125 BOARD_COMMIT commit( m_parent );
1126 commit.Modify( m_item );
1127
1128 bool pushCommit = ( m_item->GetEditFlags() == 0 );
1129
1130 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
1131 // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
1132 if( !pushCommit )
1134
1136
1137 bool wasLocked = m_item->IsLocked();
1138
1139 m_item->SetFillModeProp( (UI_FILL_MODE) m_fillCtrl->GetSelection() );
1140 m_item->SetLocked( m_locked->GetValue() );
1141
1143
1144 auto it = lineTypeNames.begin();
1145 std::advance( it, m_lineStyleCombo->GetSelection() );
1146
1147 if( it == lineTypeNames.end() )
1148 m_item->SetLineStyle( LINE_STYLE::SOLID );
1149 else
1150 m_item->SetLineStyle( it->first );
1151
1152 m_item->SetLayer( ToLAYER_ID( layer ) );
1153
1154 m_item->SetHasSolderMask( m_hasSolderMask->GetValue() );
1155
1158 else
1160
1162
1163 if( m_item->IsOnCopperLayer() )
1165 else
1166 m_item->SetNetCode( -1 );
1167
1168 if( pushCommit )
1169 commit.Push( _( "Edit Shape Properties" ) );
1170
1171 // Save the tab
1173
1174 // Notify clients which treat locked and unlocked items differently (ie: POINT_EDITOR)
1175 if( wasLocked != m_item->IsLocked() )
1177
1178 return true;
1179}
1180
1181
1183{
1184 wxArrayString errors;
1185
1186 if( !DIALOG_SHAPE_PROPERTIES_BASE::Validate() )
1187 return false;
1188
1189 if( m_geomSync )
1190 m_geomSync->Validate( errors );
1191
1192 // Type specific checks.
1193 switch( m_item->GetShape() )
1194 {
1195 case SHAPE_T::ARC:
1196 if( m_thickness.GetValue() <= 0 )
1197 errors.Add( _( "Line width must be greater than zero." ) );
1198 break;
1199
1200 case SHAPE_T::CIRCLE:
1201 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1202 errors.Add( _( "Line width must be greater than zero for an unfilled circle." ) );
1203
1204 break;
1205
1206 case SHAPE_T::RECTANGLE:
1207 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1208 errors.Add( _( "Line width must be greater than zero for an unfilled rectangle." ) );
1209
1210 break;
1211
1212 case SHAPE_T::POLY:
1213 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1214 errors.Add( _( "Line width must be greater than zero for an unfilled polygon." ) );
1215
1216 break;
1217
1218 case SHAPE_T::SEGMENT:
1219 if( m_thickness.GetValue() <= 0 )
1220 errors.Add( _( "Line width must be greater than zero." ) );
1221
1222 break;
1223
1224 case SHAPE_T::BEZIER:
1225 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1226 errors.Add( _( "Line width must be greater than zero for an unfilled curve." ) );
1227
1228 break;
1229
1230 default:
1232 break;
1233 }
1234
1235 if( errors.GetCount() )
1236 {
1237 HTML_MESSAGE_BOX dlg( this, _( "Error List" ) );
1238 dlg.ListSet( errors );
1239 dlg.ShowModal();
1240 }
1241
1242 return errors.GetCount() == 0;
1243}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
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.
void SetLocked(bool aLocked) override
Definition: board_item.h:323
bool IsLocked() const override
Definition: board_item.cpp:103
virtual bool IsOnCopperLayer() const
Definition: board_item.h:151
int GetMaxError() const
Definition: board_item.cpp:138
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:934
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
bool TransferDataFromWindow() override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
int ShowQuasiModal()
std::string m_hash_key
Definition: dialog_shim.h:236
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:225
double AsDegrees() const
Definition: eda_angle.h:116
FRAME_T GetFrameType() const
The base class for create windows for drawing purpose.
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:148
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
UI_FILL_MODE GetFillModeProp() const
Definition: eda_shape.cpp:560
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:258
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:257
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:956
void SetFillModeProp(UI_FILL_MODE)
Definition: eda_shape.cpp:547
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:2382
SHAPE_T GetShape() const
Definition: eda_shape.h:168
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:903
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
void SetRadius(int aX)
Definition: eda_shape.h:240
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:254
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:1041
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:343
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:255
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:1108
void SetWidth(int aWidth)
Definition: eda_shape.cpp:2375
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:975
static const TOOL_EVENT SelectedEvent
Definition: actions.h:342
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 const LSET & ForbiddenFootprintLayers()
Layers which are not allowed within footprint definitions.
Definition: lset.cpp:726
void SetNetInfo(const NETINFO_LIST *aNetInfoList)
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(const LSET &aMask)
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_shape.h:198
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:81
bool HasSolderMask() const
Definition: pcb_shape.h:195
void SetHasSolderMask(bool aVal)
Definition: pcb_shape.h:194
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:176
wxString GetFriendlyName() const override
Definition: pcb_shape.h:66
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:91
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: pcb_shape.h:197
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_shape.cpp:212
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:71
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)
int GetWidth() const
LINE_STYLE GetLineStyle() const
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:171
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:134
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)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:411
#define IN_EDIT
Item currently edited.
UI_FILL_MODE
Definition: eda_shape.h:68
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:665
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition: layer_ids.h:676
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:744
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
KICOMMON_API wxFont GetSmallInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:162
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:400
static void AddXYPointToSizer(EDA_DRAW_FRAME &aFrame, wxGridBagSizer &aSizer, int row, int col, const wxString &aName, bool aRelative, std::vector< BOUND_CONTROL > &aBoundCtrls)
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 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:429
const std::map< LINE_STYLE, struct LINE_STYLE_DESC > lineTypeNames
Conversion map between LINE_STYLE values and style names displayed.
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