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 ),
66 m_boundCtrls( aBoundCtrls )
67 {
68 }
69
70 void BindCtrls( size_t aFrom, size_t aTo, std::function<void()> aCb )
71 {
72 wxCHECK( aFrom < m_boundCtrls.size(), /* void */ );
73 wxCHECK( aTo < m_boundCtrls.size(), /* void */ );
74
75 for( size_t i = aFrom; i <= aTo; ++i )
76 {
77 m_boundCtrls[i].m_Ctrl->Bind( wxEVT_TEXT,
78 [aCb]( wxCommandEvent& aEvent )
79 {
80 aCb();
81 } );
82 }
83 }
84
85 void SetShape( PCB_SHAPE& aShape )
86 {
87 m_shape = aShape;
88 updateAll();
89 }
90
91 virtual bool Validate( wxArrayString& aErrs ) const { return true; }
92
93protected:
94 virtual void updateAll() = 0;
95
96 wxTextCtrl* GetCtrl( size_t aIndex ) const
97 {
98 wxCHECK( aIndex < m_boundCtrls.size(), nullptr );
99 return m_boundCtrls[aIndex].m_Ctrl;
100 }
101
102 int GetIntValue( size_t aIndex ) const
103 {
104 wxCHECK( aIndex < m_boundCtrls.size(), 0.0 );
105 return static_cast<int>( m_boundCtrls[aIndex].m_Binder->GetValue() );
106 }
107
108 EDA_ANGLE GetAngleValue( size_t aIndex ) const
109 {
110 wxCHECK( aIndex < m_boundCtrls.size(), EDA_ANGLE() );
111 return m_boundCtrls[aIndex].m_Binder->GetAngleValue();
112 }
113
114 void ChangeValue( size_t aIndex, int aValue )
115 {
116 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
117 m_boundCtrls[aIndex].m_Binder->ChangeValue( aValue );
118 }
119
120 void ChangeAngleValue( size_t aIndex, const EDA_ANGLE& aValue )
121 {
122 wxCHECK( aIndex < m_boundCtrls.size(), /* void */ );
123 m_boundCtrls[aIndex].m_Binder->ChangeAngleValue( aValue );
124 }
125
127
128 const PCB_SHAPE& GetShape() const { return m_shape; }
129
130private:
132 std::vector<BOUND_CONTROL>& m_boundCtrls;
133};
134
135
140{
141public:
143 {
156
158 };
159
160 RECTANGLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
161 GEOM_SYNCER( aShape, aBoundCtrls )
162 {
163 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
164 wxASSERT( GetShape().GetShape() == SHAPE_T::RECTANGLE );
165
167 [this]()
168 {
170 } );
171
173 [this]()
174 {
176 } );
177
179 [this]()
180 {
182 } );
183 }
184
185 bool Validate( wxArrayString& aErrs ) const override
186 {
188 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
189
190 if( p0 == p1 )
191 {
192 aErrs.push_back( _( "Rectangle cannot be zero-sized." ) );
193 return false;
194 }
195
196 return true;
197 }
198
199 void updateAll() override
200 {
204 }
205
207 {
209 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
210
211 GetShape().SetStart( p0 );
212 GetShape().SetEnd( p1 );
213
216 }
217
219 {
220 const VECTOR2I p0 = GetShape().GetStart();
221 const VECTOR2I p1 = GetShape().GetEnd();
222
223 ChangeValue( START_X, p0.x );
224 ChangeValue( START_Y, p0.y );
225 ChangeValue( END_X, p1.x );
226 ChangeValue( END_Y, p1.y );
227 }
228
230 {
232 const VECTOR2I size{ GetIntValue( CORNER_W ), GetIntValue( CORNER_H ) };
233
234 GetShape().SetStart( p0 );
235 GetShape().SetEnd( p0 + size );
236
239 }
240
242 {
243 const VECTOR2I p0 = GetShape().GetStart();
244
245 ChangeValue( CORNER_X, p0.x );
246 ChangeValue( CORNER_Y, p0.y );
247 ChangeValue( CORNER_W, GetShape().GetRectangleWidth() );
248 ChangeValue( CORNER_H, GetShape().GetRectangleHeight() );
249 }
250
252 {
254 const VECTOR2I size = { GetIntValue( CENTER_W ), GetIntValue( CENTER_H ) };
255
256 GetShape().SetStart( center - size / 2 );
257 GetShape().SetEnd( center + size / 2 );
258
261 }
262
264 {
265 const VECTOR2I c = GetShape().GetCenter();
266
267 ChangeValue( CENTER_X, c.x );
268 ChangeValue( CENTER_Y, c.y );
269 ChangeValue( CENTER_W, GetShape().GetRectangleWidth() );
270 ChangeValue( CENTER_H, GetShape().GetRectangleHeight() );
271 }
272};
273
274
276{
277public:
279 {
284
289
294
296 };
297
298 LINE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
299 GEOM_SYNCER( aShape, aBoundCtrls )
300 {
301 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
302 wxASSERT( GetShape().GetShape() == SHAPE_T::SEGMENT );
303
305 [this]()
306 {
307 OnEndsChange();
308 } );
309
311 [this]()
312 {
314 } );
315
317 [this]()
318 {
320 } );
321 }
322
323 void updateAll() override
324 {
325 updateEnds();
326 updatePolar();
328 }
329
331 {
333 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
334
335 GetShape().SetStart( p0 );
336 GetShape().SetEnd( p1 );
337
338 updatePolar();
340 }
341
343 {
344 const VECTOR2I p0 = GetShape().GetStart();
345 const VECTOR2I p1 = GetShape().GetEnd();
346
347 ChangeValue( START_X, p0.x );
348 ChangeValue( START_Y, p0.y );
349 ChangeValue( END_X, p1.x );
350 ChangeValue( END_Y, p1.y );
351 }
352
354 {
356 const int length = GetIntValue( LENGTH );
357 const EDA_ANGLE angle = GetAngleValue( ANGLE );
358
359 const VECTOR2I polar = GetRotated( VECTOR2I{ length, 0 }, angle );
360
361 GetShape().SetStart( p0 );
362 GetShape().SetEnd( p0 + polar );
363
364 updateEnds();
366 }
367
369 {
370 const VECTOR2I p0 = GetShape().GetStart();
371 const VECTOR2I p1 = GetShape().GetEnd();
372
375 ChangeValue( LENGTH, p0.Distance( p1 ) );
376 ChangeAngleValue( ANGLE, -EDA_ANGLE( p1 - p0 ) );
377 }
378
380 {
382 const VECTOR2I mid{ GetIntValue( MID_X ), GetIntValue( MID_Y ) };
383
384 GetShape().SetStart( start );
385 GetShape().SetEnd( mid - ( start - mid ) );
386
387 updateEnds();
388 updatePolar();
389 }
390
392 {
393 const VECTOR2I s = GetShape().GetStart();
394 const VECTOR2I c = GetShape().GetCenter();
395
396 ChangeValue( MID_X, c.x );
397 ChangeValue( MID_Y, c.y );
400 }
401};
402
403
405{
406public:
408 {
409 //CSA
415
422
424 };
425
426 ARC_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
427 GEOM_SYNCER( aShape, aBoundCtrls )
428 {
429 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
430 wxASSERT( GetShape().GetShape() == SHAPE_T::ARC );
431
433 [this]()
434 {
435 OnCSAChange();
436 } );
437
439 [this]()
440 {
441 OnSMEChange();
442 } );
443 }
444
445 bool Validate( wxArrayString& aErrs ) const override
446 {
447 const EDA_ANGLE angle = GetAngleValue( CSA_ANGLE );
448
449 if( angle == ANGLE_0 )
450 {
451 aErrs.push_back( _( "Arc angle must be greater than 0" ) );
452 return false;
453 }
454
458
459 if( start == mid || mid == end || start == end )
460 {
461 aErrs.push_back( _( "Arc must have 3 distinct points" ) );
462 return false;
463 }
464 else
465 {
466 const VECTOR2D center = CalcArcCenter( start, end, angle );
467
468 double radius = ( center - start ).EuclideanNorm();
469 double max_offset = std::max( std::abs( center.x ), std::abs( center.y ) ) + radius;
470 VECTOR2I center_i = VECTOR2I( center.x, center.y );
471
472 if( max_offset >= ( std::numeric_limits<VECTOR2I::coord_type>::max() / 2.0 )
473 || center_i == start || center_i == end )
474 {
475 aErrs.push_back( wxString::Format( _( "Invalid Arc with radius %f and angle %f." ),
476 radius, angle.AsDegrees() ) );
477 return false;
478 }
479 }
480
481 return true;
482 }
483
484 void updateAll() override
485 {
486 updateCSA();
487 updateSME();
488 }
489
491 {
494 const EDA_ANGLE angle( GetIntValue( CSA_ANGLE ), DEGREES_T );
495
497 GetShape().SetStart( start );
498 GetShape().SetArcAngleAndEnd( angle );
499
500 updateSME();
501 }
502
504 {
505 const VECTOR2I center = GetShape().GetCenter();
506 const VECTOR2I start = GetShape().GetStart();
507
510 ChangeValue( CSA_START_X, start.x );
511 ChangeValue( CSA_START_Y, start.y );
512 ChangeAngleValue( CSA_ANGLE, GetShape().GetArcAngle() );
513 }
514
516 {
520
521 GetShape().SetArcGeometry( p0, p1, p2 );
522
523 updateCSA();
524 }
525
527 {
528 const VECTOR2I p0 = GetShape().GetStart();
529 const VECTOR2I p1 = GetShape().GetArcMid();
530 const VECTOR2I p2 = GetShape().GetEnd();
531
534 ChangeValue( SME_MID_X, p1.x );
535 ChangeValue( SME_MID_Y, p1.y );
536 ChangeValue( SME_END_X, p2.x );
537 ChangeValue( SME_END_Y, p2.y );
538 }
539};
540
541
543{
544public:
546 {
554
556 };
557
558 CIRCLE_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
559 GEOM_SYNCER( aShape, aBoundCtrls )
560 {
561 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
562 wxASSERT( GetShape().GetShape() == SHAPE_T::CIRCLE );
563
565 [this]()
566 {
568 } );
569
571 [this]()
572 {
574 } );
575 }
576
577 void updateAll() override
578 {
581 }
582
583 bool Validate( wxArrayString& aErrs ) const override
584 {
585 if( GetIntValue( RADIUS ) <= 0 )
586 {
587 aErrs.push_back( _( "Radius must be greater than 0" ) );
588 return false;
589 }
590
591 return true;
592 }
593
595 {
597 const int radius = GetIntValue( RADIUS );
598
601
603 }
604
606 {
607 const VECTOR2I center = GetShape().GetCenter();
608
611 ChangeValue( RADIUS, GetShape().GetRadius() );
612 }
613
615 {
618
620 GetShape().SetEnd( pt );
621
623 }
624
626 {
627 const VECTOR2I center = GetShape().GetCenter();
628 const VECTOR2I pt = GetShape().GetEnd();
629
632 ChangeValue( PT_PT_X, pt.x );
633 ChangeValue( PT_PT_Y, pt.y );
634 }
635};
636
637
639{
640public:
642 {
651
653 };
654
655 BEZIER_GEOM_SYNCER( PCB_SHAPE& aShape, std::vector<BOUND_CONTROL>& aBoundCtrls ) :
656 GEOM_SYNCER( aShape, aBoundCtrls )
657 {
658 wxASSERT( aBoundCtrls.size() == NUM_CTRLS );
659 wxASSERT( GetShape().GetShape() == SHAPE_T::BEZIER );
660
662 [this]()
663 {
665 } );
666 }
667
668 void updateAll() override
669 {
670 updateBezier();
671 }
672
674 {
676 const VECTOR2I p1{ GetIntValue( END_X ), GetIntValue( END_Y ) };
679
680 GetShape().SetStart( p0 );
681 GetShape().SetEnd( p1 );
682 GetShape().SetBezierC1( c1 );
683 GetShape().SetBezierC2( c2 );
684 }
685
687 {
688 const VECTOR2I p0 = GetShape().GetStart();
689 const VECTOR2I p1 = GetShape().GetEnd();
690 const VECTOR2I c1 = GetShape().GetBezierC1();
691 const VECTOR2I c2 = GetShape().GetBezierC2();
692
693 ChangeValue( START_X, p0.x );
694 ChangeValue( START_Y, p0.y );
695 ChangeValue( END_X, p1.x );
696 ChangeValue( END_Y, p1.y );
697 ChangeValue( CTRL1_X, c1.x );
698 ChangeValue( CTRL1_Y, c1.y );
699 ChangeValue( CTRL2_X, c2.x );
700 ChangeValue( CTRL2_Y, c2.y );
701 }
702};
703
705{
706public:
709
710private:
711 bool TransferDataToWindow() override;
712 bool TransferDataFromWindow() override;
713
714 void onLayerSelection( wxCommandEvent& event ) override;
715
716 void onTechLayersChanged( wxCommandEvent& event ) override;
717
718 bool Validate() override;
719
721 {
723
724 m_netSelector->Enable( isCopper );
725 m_netLabel->Enable( isCopper );
726 }
727
729 {
731
732 m_techLayersLabel->Enable( isExtCopper );
733 m_hasSolderMask->Enable( isExtCopper );
734
735 bool showMaskMargin = isExtCopper && m_hasSolderMask->GetValue();
736
737 m_solderMaskMarginLabel->Enable( showMaskMargin );
738 m_solderMaskMarginCtrl->Enable( showMaskMargin );
739 m_solderMaskMarginUnit->Enable( showMaskMargin );
740 }
741
742private:
745
748
749 std::vector<BOUND_CONTROL> m_boundCtrls;
750 std::unique_ptr<GEOM_SYNCER> m_geomSync;
752};
753
754
755static void AddXYPointToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
756 const wxString aName, bool aRelative,
757 std::vector<BOUND_CONTROL>& aBoundCtrls )
758{
759 // Name
760 // X [Ctrl] mm
761 // Y [Ctrl] mm
762 wxWindow* parent = aSizer.GetContainingWindow();
763
764 wxStaticText* titleLabel = new wxStaticText( parent, wxID_ANY, aName );
765 aSizer.Add( titleLabel, wxGBPosition( row, col ), wxGBSpan( 1, 3 ),
766 wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL | wxALL | wxEXPAND );
767 row++;
768
769 for( size_t coord = 0; coord < 2; ++coord )
770 {
771 wxStaticText* label =
772 new wxStaticText( parent, wxID_ANY, coord == 0 ? _( "X" ) : _( "Y" ) );
773 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
774 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
775
776 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, "" );
777 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
778 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 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
789 else
790 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::ABS_X_COORD
792
793 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
794 row++;
795 }
796
797 if( !aSizer.IsColGrowable( col + 1 ) )
798 aSizer.AddGrowableCol( col + 1 );
799}
800
801
802void AddFieldToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
803 const wxString aName, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType,
804 bool aIsAngle, std::vector<BOUND_CONTROL>& aBoundCtrls )
805{
806 // Name [Ctrl] mm
807 wxWindow* parent = aSizer.GetContainingWindow();
808
809 wxStaticText* label = new wxStaticText( parent, wxID_ANY, aName );
810 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
811 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
812
813 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY );
814 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
815 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
816
817 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
818 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
819 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
820
821 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
822 binder->SetCoordType( aCoordType );
823
824 if( aIsAngle )
825 {
826 binder->SetPrecision( 4 );
827 binder->SetUnits( EDA_UNITS::DEGREES );
828 }
829
830 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
831
832 if( !aSizer.IsColGrowable( col + 1 ) )
833 aSizer.AddGrowableCol( col + 1 );
834}
835
836
837static std::map<SHAPE_T, int> s_lastTabForShape;
838
839
842 m_parent( aParent ),
843 m_item( aShape ),
844 m_thickness( aParent, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits ),
845 m_solderMaskMargin( aParent, m_solderMaskMarginLabel, m_solderMaskMarginCtrl, m_solderMaskMarginUnit ),
846 m_workingCopy( *m_item )
847{
848 m_workingCopy.SetParentGroup( nullptr );
849 m_workingCopy.SetParent( nullptr );
850
851 SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
852 m_hash_key = TO_UTF8( GetTitle() );
853
854 wxFont infoFont = KIUI::GetSmallInfoFont( this );
855 m_techLayersLabel->SetFont( infoFont );
856
857 // All the pages exist in the WxFB template, but we'll scrap the ones we don't
858 // use. Constructing on-demand would work fine too.
859 std::set<int> shownPages;
860
861 const auto showPage = [&]( wxSizer& aMainSizer, bool aSelect = false )
862 {
863 // Get the parent of the sizer, which is the panel
864 wxWindow* page = aMainSizer.GetContainingWindow();
865 wxCHECK( page, /* void */ );
866 page->Layout();
867
868 const int pageIdx = m_notebookShapeDefs->FindPage( page );
869 shownPages.insert( pageIdx );
870
871 if( aSelect )
872 m_notebookShapeDefs->SetSelection( pageIdx );
873 };
874
875 switch( m_item->GetShape() )
876 {
877 case SHAPE_T::RECTANGLE:
878 // For all these functions, it's very important that the fields are added in the same order
879 // as the CTRL_IDX enums in the GEOM_SYNCER classes.
880 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 0, _( "Start Point" ), false, m_boundCtrls );
881 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 3, _( "End Point" ), false, m_boundCtrls );
882
883 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 0, _( "Start Point" ), false, m_boundCtrls );
884 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 3, _( "Size" ), true, m_boundCtrls );
885
886 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 0, _( "Center" ), false, m_boundCtrls );
887 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 3, _( "Size" ), true, m_boundCtrls );
888
889 m_geomSync = std::make_unique<RECTANGLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
890
891 showPage( *m_gbsRectangleByCorners, true );
892 showPage( *m_gbsRectangleByCornerSize );
893 showPage( *m_gbsRectangleByCenterSize );
894 break;
895
896 case SHAPE_T::SEGMENT:
897
898 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 0, _( "Start Point" ), false, m_boundCtrls );
899 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 3, _( "End Point" ), false, m_boundCtrls );
900
901 AddXYPointToSizer( *aParent, *m_gbsLineByLengthAngle, 0, 0, _( "Start Point" ), false, m_boundCtrls);
904
905 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 0, _( "Start Point" ), false, m_boundCtrls );
906 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
907
908 m_geomSync = std::make_unique<LINE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
909
910 showPage( *m_gbsLineByEnds, true );
911 showPage( *m_gbsLineByLengthAngle );
912 showPage( *m_gbsLineByStartMid );
913 break;
914
915 case SHAPE_T::ARC:
916 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 0, _( "Center" ), false, m_boundCtrls);
917 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 3, _( "Start Point" ), false, m_boundCtrls);
918 AddFieldToSizer( *aParent, *m_gbsArcByCSA, 3, 0, _( "Start Angle" ), ORIGIN_TRANSFORMS::NOT_A_COORD, true, m_boundCtrls );
919
920 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 0, _( "Start Point" ), false, m_boundCtrls );
921 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
922 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 3, 0, _( "End Point" ), false, m_boundCtrls );
923
924 m_geomSync = std::make_unique<ARC_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
925
926 showPage( *m_gbsArcByCSA, true );
927 showPage( *m_gbsArcBySME );
928 break;
929
930 case SHAPE_T::CIRCLE:
931 AddXYPointToSizer( *aParent, *m_gbsCircleCenterRadius, 0, 0, _( "Center" ), false, m_boundCtrls);
933
934 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 0, _( "Center" ), false, m_boundCtrls );
935 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 3, _( "Point on Circle" ), false, m_boundCtrls );
936
937 m_geomSync = std::make_unique<CIRCLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
938
939 showPage( *m_gbsCircleCenterRadius, true );
940 showPage( *m_gbsCircleCenterPoint );
941 break;
942
943 case SHAPE_T::BEZIER:
944 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 0, _( "Start Point" ), false, m_boundCtrls );
945 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 3, _( "End Point" ), false, m_boundCtrls );
946 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 0, _( "Control Point 1" ), false, m_boundCtrls );
947 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 3, _( "Control Point 2" ), false, m_boundCtrls );
948
949 m_geomSync = std::make_unique<BEZIER_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
950
951 showPage( *m_gbsBezier, TRUE );
952 break;
953
954 case SHAPE_T::POLY:
955 m_notebookShapeDefs->Hide();
956 // Nothing to do here...yet
957 break;
958
959 case SHAPE_T::UNDEFINED:
960 wxFAIL_MSG( "Undefined shape" );
961 break;
962 }
963
964 // Remove any tabs not used (Hide() doesn't work on Windows)
965 for( int i = (int) m_notebookShapeDefs->GetPageCount() - 1; i >= 0; --i )
966 {
967 if( shownPages.count( i ) == 0 )
968 m_notebookShapeDefs->RemovePage( i );
969 }
970
971 // Used the last saved tab if any
972 if( s_lastTabForShape.count( m_item->GetShape() ) > 0
973 && s_lastTabForShape[m_item->GetShape()] < (int) m_notebookShapeDefs->GetPageCount()
974 && s_lastTabForShape[m_item->GetShape()] >= 0 )
975 {
977 }
978
979 // Find the first control in the shown tab
980 wxWindow* tabPanel = m_notebookShapeDefs->GetCurrentPage();
981
982 for( size_t i = 0; i < m_boundCtrls.size(); ++i )
983 {
984 if( m_boundCtrls[i].m_Ctrl->IsDescendant( tabPanel ) )
985 {
986 m_boundCtrls[i].m_Ctrl->SetFocus();
987 break;
988 }
989 }
990
991 // Do not allow locking items in the footprint editor
992 m_locked->Show( dynamic_cast<PCB_EDIT_FRAME*>( aParent ) != nullptr );
993
994 // Configure the layers list selector
996 {
997 LSET forbiddenLayers = LSET::ForbiddenFootprintLayers();
998
999 // If someone went to the trouble of setting the layer in a text editor, then there's
1000 // very little sense in nagging them about it.
1001 forbiddenLayers.set( m_item->GetLayer(), false );
1002
1003 m_LayerSelectionCtrl->SetNotAllowedLayerSet( forbiddenLayers );
1004 }
1005
1006 for( const auto& [ lineStyle, lineStyleDesc ] : lineTypeNames )
1007 m_lineStyleCombo->Append( lineStyleDesc.name, KiBitmapBundle( lineStyleDesc.bitmap ) );
1008
1012
1013 m_netSelector->SetBoard( aParent->GetBoard() );
1014 m_netSelector->SetNetInfo( &aParent->GetBoard()->GetNetInfo() );
1015
1017 {
1018 m_netLabel->Hide();
1019 m_netSelector->Hide();
1020 }
1021 else
1022 {
1023 int net = aShape->GetNetCode();
1024
1025 if( net >= 0 )
1026 {
1028 }
1029 else
1030 {
1033 }
1034 }
1035
1036 if( m_item->GetShape() == SHAPE_T::ARC || m_item->GetShape() == SHAPE_T::SEGMENT )
1037 {
1038 m_fillLabel->Show( false );
1039 m_fillCtrl->Show( false );
1040 }
1041
1043
1044 // Now all widgets have the size fixed, call FinishDialogSettings
1046}
1047
1048
1050{
1051 wxCHECK_RET( aShape, wxT( "ShowGraphicItemPropertiesDialog() error: NULL item" ) );
1052
1053 DIALOG_SHAPE_PROPERTIES dlg( this, aShape );
1054
1055 if( dlg.ShowQuasiModal() == wxID_OK )
1056 {
1057 if( aShape->IsOnLayer( GetActiveLayer() ) )
1058 {
1060 drawingTool->SetStroke( aShape->GetStroke(), GetActiveLayer() );
1061 }
1062 }
1063}
1064
1065
1067{
1069 enableNetInfo();
1070
1072}
1073
1074
1076{
1078}
1079
1080
1082{
1083 if( !m_item )
1084 return false;
1085
1086 // Not al shapes have a syncer (e.g. polygons)
1087 if( m_geomSync )
1088 m_geomSync->SetShape( *m_item );
1089
1090 m_fillCtrl->SetSelection( m_item->GetFillModeProp() );
1091 m_locked->SetValue( m_item->IsLocked() );
1092
1094
1095 int style = static_cast<int>( m_item->GetStroke().GetLineStyle() );
1096
1097 if( style >= 0 && style < (int) lineTypeNames.size() )
1098 m_lineStyleCombo->SetSelection( style );
1099 else
1100 m_lineStyleCombo->SetSelection( 0 );
1101
1103
1104 m_hasSolderMask->SetValue( m_item->HasSolderMask() );
1105
1106 if( m_item->GetLocalSolderMaskMargin().has_value() )
1108 else
1109 m_solderMaskMargin.SetValue( wxEmptyString );
1110
1111 enableNetInfo();
1113
1114 return DIALOG_SHAPE_PROPERTIES_BASE::TransferDataToWindow();
1115}
1116
1117
1119{
1120 if( !DIALOG_SHAPE_PROPERTIES_BASE::TransferDataFromWindow() )
1121 return false;
1122
1123 if( !m_item )
1124 return true;
1125
1127
1128 BOARD_COMMIT commit( m_parent );
1129 commit.Modify( m_item );
1130
1131 bool pushCommit = ( m_item->GetEditFlags() == 0 );
1132
1133 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
1134 // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
1135 if( !pushCommit )
1137
1138 // Ensure parent and parent group item are restored. m_workingCopy had previously
1139 // these parents cleared and not restored for some PCB_SHAPE types (i.e. POLY)
1143
1144 bool wasLocked = m_item->IsLocked();
1145
1146 m_item->SetFillModeProp( (UI_FILL_MODE) m_fillCtrl->GetSelection() );
1147 m_item->SetLocked( m_locked->GetValue() );
1148
1150
1151 auto it = lineTypeNames.begin();
1152 std::advance( it, m_lineStyleCombo->GetSelection() );
1153
1154 if( it == lineTypeNames.end() )
1155 m_item->SetLineStyle( LINE_STYLE::SOLID );
1156 else
1157 m_item->SetLineStyle( it->first );
1158
1159 m_item->SetLayer( ToLAYER_ID( layer ) );
1160
1161 m_item->SetHasSolderMask( m_hasSolderMask->GetValue() );
1162
1165 else
1167
1169
1170 if( m_item->IsOnCopperLayer() )
1172 else
1173 m_item->SetNetCode( -1 );
1174
1175 if( pushCommit )
1176 commit.Push( _( "Edit Shape Properties" ) );
1177
1178 // Save the tab
1180
1181 // Notify clients which treat locked and unlocked items differently (ie: POINT_EDITOR)
1182 if( wasLocked != m_item->IsLocked() )
1184
1185 return true;
1186}
1187
1188
1190{
1191 wxArrayString errors;
1192
1193 if( !DIALOG_SHAPE_PROPERTIES_BASE::Validate() )
1194 return false;
1195
1196 if( m_geomSync )
1197 m_geomSync->Validate( errors );
1198
1199 // Type specific checks.
1200 switch( m_item->GetShape() )
1201 {
1202 case SHAPE_T::ARC:
1203 if( m_thickness.GetValue() <= 0 )
1204 errors.Add( _( "Line width must be greater than zero." ) );
1205 break;
1206
1207 case SHAPE_T::CIRCLE:
1208 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1209 errors.Add( _( "Line width must be greater than zero for an unfilled circle." ) );
1210
1211 break;
1212
1213 case SHAPE_T::RECTANGLE:
1214 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1215 errors.Add( _( "Line width must be greater than zero for an unfilled rectangle." ) );
1216
1217 break;
1218
1219 case SHAPE_T::POLY:
1220 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1221 errors.Add( _( "Line width must be greater than zero for an unfilled polygon." ) );
1222
1223 break;
1224
1225 case SHAPE_T::SEGMENT:
1226 if( m_thickness.GetValue() <= 0 )
1227 errors.Add( _( "Line width must be greater than zero." ) );
1228
1229 break;
1230
1231 case SHAPE_T::BEZIER:
1232 if( m_fillCtrl->GetSelection() != UI_FILL_MODE::SOLID && m_thickness.GetValue() <= 0 )
1233 errors.Add( _( "Line width must be greater than zero for an unfilled curve." ) );
1234
1235 break;
1236
1237 default:
1239 break;
1240 }
1241
1242 if( errors.GetCount() )
1243 {
1244 HTML_MESSAGE_BOX dlg( this, _( "Error List" ) );
1245 dlg.ListSet( errors );
1246 dlg.ShowModal();
1247 }
1248
1249 return errors.GetCount() == 0;
1250}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:127
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:320
bool IsLocked() const override
Definition: board_item.cpp:76
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:207
virtual bool IsOnCopperLayer() const
Definition: board_item.h:148
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:897
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:194
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:144
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:138
virtual EDA_GROUP * GetParentGroup() const
Definition: eda_item.h:114
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition: eda_item.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:111
UI_FILL_MODE GetFillModeProp() const
Definition: eda_shape.cpp:567
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:964
void SetFillModeProp(UI_FILL_MODE)
Definition: eda_shape.cpp:554
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:2379
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:911
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:1046
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:342
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:1113
void SetWidth(int aWidth)
Definition: eda_shape.cpp:2372
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:983
static const TOOL_EVENT SelectedEvent
Definition: actions.h:341
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:729
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(const LSET &aMask)
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pcb_shape.h:193
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:190
void SetHasSolderMask(bool aVal)
Definition: pcb_shape.h:189
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:177
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:192
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_shape.cpp:207
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: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)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:404
@ DEGREES_T
Definition: eda_angle.h:31
#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:663
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition: layer_ids.h:674
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:747
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:393
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: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