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 (C) 1992-2023 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 {
252 const VECTOR2I center = { GetIntValue( CENTER_X ), GetIntValue( CENTER_Y ) };
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
495 GetShape().SetCenter( center );
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
507 ChangeValue( CSA_CENTER_X, center.x );
508 ChangeValue( CSA_CENTER_Y, center.y );
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 {
595 const VECTOR2I center{ GetIntValue( CENTER_X ), GetIntValue( CENTER_Y ) };
596 const int radius = GetIntValue( RADIUS );
597
598 GetShape().SetCenter( center );
599 GetShape().SetRadius( radius );
600
602 }
603
605 {
606 const VECTOR2I center = GetShape().GetCenter();
607
608 ChangeValue( CENTER_X, center.x );
609 ChangeValue( CENTER_Y, center.y );
610 ChangeValue( RADIUS, GetShape().GetRadius() );
611 }
612
614 {
617
618 GetShape().SetCenter( center );
619 GetShape().SetEnd( pt );
620
622 }
623
625 {
626 const VECTOR2I center = GetShape().GetCenter();
627 const VECTOR2I pt = GetShape().GetEnd();
628
629 ChangeValue( CENTER_PT_X, center.x );
630 ChangeValue( CENTER_PT_Y, center.y );
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
721 // Show/hide the widgets used in net selection (shown only for copper layers)
723 {
725
726 m_netSelector->Show( isCopper );
727 m_netLabel->Show( 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,
759 std::vector<BOUND_CONTROL>& aBoundCtrls )
760{
761 // Name
762 // X [Ctrl] mm
763 // Y [Ctrl] mm
764 wxWindow* parent = aSizer.GetContainingWindow();
765
766 wxStaticText* titleLabel = new wxStaticText( parent, wxID_ANY, aName );
767 aSizer.Add( titleLabel, wxGBPosition( row, col ), wxGBSpan( 1, 3 ),
768 wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL | wxALL | wxEXPAND );
769 row++;
770
771 for( size_t coord = 0; coord < 2; ++coord )
772 {
773 wxStaticText* label =
774 new wxStaticText( parent, wxID_ANY, coord == 0 ? _( "X" ) : _( "Y" ) );
775 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
776 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
777
778 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY, "" );
779 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
780 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
781
782 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
783 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
784 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
785
786 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
787
788 if( aRelative )
789 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::REL_X_COORD
791 else
792 binder->SetCoordType( coord == 0 ? ORIGIN_TRANSFORMS::ABS_X_COORD
794
795 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
796 row++;
797 }
798
799 if( !aSizer.IsColGrowable( col + 1 ) )
800 aSizer.AddGrowableCol( col + 1 );
801}
802
803
804void AddFieldToSizer( EDA_DRAW_FRAME& aFrame, wxGridBagSizer& aSizer, int row, int col,
805 const wxString aName, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType,
806 bool aIsAngle, std::vector<BOUND_CONTROL>& aBoundCtrls )
807{
808 // Name [Ctrl] mm
809 wxWindow* parent = aSizer.GetContainingWindow();
810
811 wxStaticText* label = new wxStaticText( parent, wxID_ANY, aName );
812 aSizer.Add( label, wxGBPosition( row, col ), wxDefaultSpan,
813 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxLEFT, 5 );
814
815 wxTextCtrl* ctrl = new wxTextCtrl( parent, wxID_ANY );
816 aSizer.Add( ctrl, wxGBPosition( row, col + 1 ), wxDefaultSpan,
817 wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
818
819 wxStaticText* units = new wxStaticText( parent, wxID_ANY, _( "mm" ) );
820 aSizer.Add( units, wxGBPosition( row, col + 2 ), wxDefaultSpan,
821 wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 5 );
822
823 auto binder = std::make_unique<UNIT_BINDER>( &aFrame, label, ctrl, units );
824 binder->SetCoordType( aCoordType );
825
826 if( aIsAngle )
827 {
828 binder->SetPrecision( 4 );
829 binder->SetUnits( EDA_UNITS::DEGREES );
830 }
831
832 aBoundCtrls.push_back( BOUND_CONTROL{ std::move( binder ), ctrl } );
833
834 if( !aSizer.IsColGrowable( col + 1 ) )
835 aSizer.AddGrowableCol( col + 1 );
836}
837
838
839static std::map<SHAPE_T, int> s_lastTabForShape;
840
841
844 m_parent( aParent ),
845 m_item( aShape ),
846 m_thickness( aParent, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits ),
847 m_solderMaskMargin( aParent, m_solderMaskMarginLabel, m_solderMaskMarginCtrl, m_solderMaskMarginUnit ),
848 m_workingCopy( *m_item )
849{
850 SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
851 m_hash_key = TO_UTF8( GetTitle() );
852
853 wxFont infoFont = KIUI::GetInfoFont( this );
854 m_techLayersLabel->SetFont( infoFont );
855
856 // All the pages exist in the WxFB template, but we'll scrap the ones we don't
857 // use. Constructing on-demand would work fine too.
858 std::set<int> shownPages;
859
860 const auto showPage = [&]( wxSizer& aMainSizer, bool aSelect = false )
861 {
862 // Get the parent of the sizer, which is the panel
863 wxWindow* page = aMainSizer.GetContainingWindow();
864 wxCHECK( page, /* void */ );
865 page->Layout();
866
867 const int pageIdx = m_notebookShapeDefs->FindPage( page );
868 shownPages.insert( pageIdx );
869
870 if( aSelect )
871 m_notebookShapeDefs->SetSelection( pageIdx );
872 };
873
874 switch( m_item->GetShape() )
875 {
876 case SHAPE_T::RECTANGLE:
877 // For all these functions, it's very important that the fields are added in the same order
878 // as the CTRL_IDX enums in the GEOM_SYNCER classes.
879 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 0, _( "Start Point" ), false, m_boundCtrls);
880 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 3, _( "End Point" ), false, m_boundCtrls );
881
882 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 0, _( "Start Point" ), false, m_boundCtrls);
883 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 3, _( "Size" ), true, m_boundCtrls );
884
885 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 0, _( "Center" ), false, m_boundCtrls);
886 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 3, _( "Size" ), true, m_boundCtrls );
887
888 m_geomSync = std::make_unique<RECTANGLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
889
890 showPage( *m_gbsRectangleByCorners, true );
891 showPage( *m_gbsRectangleByCornerSize );
892 showPage( *m_gbsRectangleByCenterSize );
893 break;
894
895 case SHAPE_T::SEGMENT:
896
897 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 0, _( "Start Point" ), false, m_boundCtrls);
898 AddXYPointToSizer( *aParent, *m_gbsLineByEnds, 0, 3, _( "End Point" ), false, m_boundCtrls);
899
900 AddXYPointToSizer( *aParent, *m_gbsLineByLengthAngle, 0, 0, _( "Start Point" ), false, m_boundCtrls);
903
904 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 0, _( "Start Point" ), false, m_boundCtrls );
905 AddXYPointToSizer( *aParent, *m_gbsLineByStartMid, 0, 3, _( "Mid Point" ), false, m_boundCtrls );
906
907 m_geomSync = std::make_unique<LINE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
908
909 showPage( *m_gbsLineByEnds, true );
910 showPage( *m_gbsLineByLengthAngle );
911 showPage( *m_gbsLineByStartMid );
912 break;
913
914 case SHAPE_T::ARC:
915 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 0, _( "Center" ), false, m_boundCtrls);
916 AddXYPointToSizer( *aParent, *m_gbsArcByCSA, 0, 3, _( "Start Point" ), false, m_boundCtrls);
917 AddFieldToSizer( *aParent, *m_gbsArcByCSA, 3, 0, _( "Start Angle" ), ORIGIN_TRANSFORMS::NOT_A_COORD, true, m_boundCtrls );
918
919 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 0, _( "Start Point" ), false, m_boundCtrls);
920 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 0, 3, _( "Mid Point" ), false, m_boundCtrls);
921 AddXYPointToSizer( *aParent, *m_gbsArcBySME, 3, 0, _( "End Point" ), false, m_boundCtrls);
922
923 m_geomSync = std::make_unique<ARC_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
924
925 showPage( *m_gbsArcByCSA, true );
926 showPage( *m_gbsArcBySME );
927 break;
928
929 case SHAPE_T::CIRCLE:
930 AddXYPointToSizer( *aParent, *m_gbsCircleCenterRadius, 0, 0, _( "Center" ), false, m_boundCtrls);
932
933 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 0, _( "Center" ), false, m_boundCtrls);
934 AddXYPointToSizer( *aParent, *m_gbsCircleCenterPoint, 0, 3, _( "Point on Circle" ), false, m_boundCtrls);
935
936 m_geomSync = std::make_unique<CIRCLE_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
937
938 showPage( *m_gbsCircleCenterRadius, true );
939 showPage( *m_gbsCircleCenterPoint );
940 break;
941
942 case SHAPE_T::BEZIER:
943 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 0, _( "Start Point" ), false, m_boundCtrls);
944 AddXYPointToSizer( *aParent, *m_gbsBezier, 0, 3, _( "End Point" ), false, m_boundCtrls);
945 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 0, _( "Control Point 1" ), false, m_boundCtrls);
946 AddXYPointToSizer( *aParent, *m_gbsBezier, 3, 3, _( "Control Point 2" ), false, m_boundCtrls);
947
948 m_geomSync = std::make_unique<BEZIER_GEOM_SYNCER>( m_workingCopy, m_boundCtrls );
949
950 showPage( *m_gbsBezier, TRUE );
951 break;
952
953 case SHAPE_T::POLY:
954 m_notebookShapeDefs->Hide();
955 // Nothing to do here...yet
956 break;
957
958 case SHAPE_T::UNDEFINED:
959 wxFAIL_MSG( "Undefined shape" );
960 break;
961 }
962
963 // Remove any tabs not used (Hide() doesn't work on Windows)
964 for( int i = m_notebookShapeDefs->GetPageCount() - 1; i >= 0; --i )
965 {
966 if( shownPages.count( i ) == 0 )
967 m_notebookShapeDefs->RemovePage( i );
968 }
969
970 // Used the last saved tab if any
971 if( s_lastTabForShape.count( m_item->GetShape() ) > 0 )
972 {
974 }
975
976 // Find the first control in the shown tab
977 wxWindow* tabPanel = m_notebookShapeDefs->GetCurrentPage();
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
1006
1010
1011 m_netSelector->SetBoard( aParent->GetBoard() );
1012 m_netSelector->SetNetInfo( &aParent->GetBoard()->GetNetInfo() );
1013
1015 {
1016 m_netLabel->Hide();
1017 m_netSelector->Hide();
1018 }
1019 else
1020 {
1021 int net = aShape->GetNetCode();
1022
1023 if( net >= 0 )
1024 {
1026 }
1027 else
1028 {
1031 }
1032 }
1033
1034 if( m_item->GetShape() == SHAPE_T::ARC || m_item->GetShape() == SHAPE_T::SEGMENT )
1035 m_filledCtrl->Show( false );
1036
1038
1039 // Now all widgets have the size fixed, call FinishDialogSettings
1041}
1042
1043
1045{
1046 wxCHECK_RET( aShape, wxT( "ShowGraphicItemPropertiesDialog() error: NULL item" ) );
1047
1048 DIALOG_SHAPE_PROPERTIES dlg( this, aShape );
1049
1050 if( dlg.ShowQuasiModal() == wxID_OK )
1051 {
1052 if( aShape->IsOnLayer( GetActiveLayer() ) )
1053 {
1055 drawingTool->SetStroke( aShape->GetStroke(), GetActiveLayer() );
1056 }
1057 }
1058}
1059
1060
1062{
1064 {
1066 }
1067
1069}
1070
1071
1073{
1074 if( m_filledCtrl->GetValue() )
1075 {
1076 m_lineStyleCombo->SetSelection( 0 );
1077 m_lineStyleLabel->Enable( false );
1078 m_lineStyleCombo->Enable( false );
1079 }
1080 else
1081 {
1083
1084 if( style == LINE_STYLE::DEFAULT )
1085 style = LINE_STYLE::SOLID;
1086
1087 if( (int) style < (int) lineTypeNames.size() )
1088 m_lineStyleCombo->SetSelection( (int) style );
1089
1090 m_lineStyleLabel->Enable( true );
1091 m_lineStyleCombo->Enable( true );
1092 }
1093}
1094
1095
1097{
1099}
1100
1101
1103{
1104 if( !m_item )
1105 return false;
1106
1107 // Not al shapes have a syncer (e.g. polygons)
1108 if( m_geomSync )
1109 m_geomSync->SetShape( *m_item );
1110
1111 m_filledCtrl->SetValue( m_item->IsFilled() );
1112 m_locked->SetValue( m_item->IsLocked() );
1113
1115
1116 int style = static_cast<int>( m_item->GetStroke().GetLineStyle() );
1117
1118 if( style == -1 )
1119 m_lineStyleCombo->SetStringSelection( DEFAULT_STYLE );
1120 else if( style < (int) lineTypeNames.size() )
1121 m_lineStyleCombo->SetSelection( style );
1122 else
1123 wxFAIL_MSG( "Line type not found in the type lookup map" );
1124
1126
1127 m_hasSolderMask->SetValue( m_item->HasSolderMask() );
1128
1129 if( m_item->GetLocalSolderMaskMargin().has_value() )
1131 else
1132 m_solderMaskMargin.SetValue( wxEmptyString );
1133
1136
1137 return DIALOG_SHAPE_PROPERTIES_BASE::TransferDataToWindow();
1138}
1139
1140
1142{
1143 if( !DIALOG_SHAPE_PROPERTIES_BASE::TransferDataFromWindow() )
1144 return false;
1145
1146 if( !m_item )
1147 return true;
1148
1150
1151 BOARD_COMMIT commit( m_parent );
1152 commit.Modify( m_item );
1153
1154 bool pushCommit = ( m_item->GetEditFlags() == 0 );
1155
1156 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
1157 // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
1158 if( !pushCommit )
1160
1162
1163 bool wasLocked = m_item->IsLocked();
1164
1165 m_item->SetFilled( m_filledCtrl->GetValue() );
1166 m_item->SetLocked( m_locked->GetValue() );
1167
1168 STROKE_PARAMS stroke = m_item->GetStroke();
1169
1170 stroke.SetWidth( m_thickness.GetIntValue() );
1171
1172 auto it = lineTypeNames.begin();
1173 std::advance( it, m_lineStyleCombo->GetSelection() );
1174
1175 if( it == lineTypeNames.end() )
1176 stroke.SetLineStyle( LINE_STYLE::DEFAULT );
1177 else
1178 stroke.SetLineStyle( it->first );
1179
1180 m_item->SetStroke( stroke );
1181
1182 m_item->SetLayer( ToLAYER_ID( layer ) );
1183
1184 m_item->SetHasSolderMask( m_hasSolderMask->GetValue() );
1185
1188 else
1190
1192
1193 if( m_item->IsOnCopperLayer() )
1195 else
1196 m_item->SetNetCode( -1 );
1197
1198 if( pushCommit )
1199 commit.Push( _( "Edit Shape Properties" ) );
1200
1201 // Save the tab
1203
1204 // Notify clients which treat locked and unlocked items differently (ie: POINT_EDITOR)
1205 if( wasLocked != m_item->IsLocked() )
1207
1208 return true;
1209}
1210
1211
1213{
1214 wxArrayString errors;
1215
1216 if( !DIALOG_SHAPE_PROPERTIES_BASE::Validate() )
1217 return false;
1218
1219 if( m_geomSync )
1220 m_geomSync->Validate( errors );
1221
1222 // Type specific checks.
1223 switch( m_item->GetShape() )
1224 {
1225 case SHAPE_T::ARC:
1226 if( m_thickness.GetValue() <= 0 )
1227 errors.Add( _( "Line width must be greater than zero." ) );
1228 break;
1229
1230 case SHAPE_T::CIRCLE:
1231 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1232 errors.Add( _( "Line width must be greater than zero for an unfilled circle." ) );
1233
1234 break;
1235
1236 case SHAPE_T::RECTANGLE:
1237 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1238 errors.Add( _( "Line width must be greater than zero for an unfilled rectangle." ) );
1239
1240 break;
1241
1242 case SHAPE_T::POLY:
1243 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1244 errors.Add( _( "Line width must be greater than zero for an unfilled polygon." ) );
1245
1246 break;
1247
1248 case SHAPE_T::SEGMENT:
1249 if( m_thickness.GetValue() <= 0 )
1250 errors.Add( _( "Line width must be greater than zero." ) );
1251
1252 break;
1253
1254 case SHAPE_T::BEZIER:
1255 if( !m_filledCtrl->GetValue() && m_thickness.GetValue() <= 0 )
1256 errors.Add( _( "Line width must be greater than zero for an unfilled curve." ) );
1257
1258 break;
1259
1260 default:
1262 break;
1263 }
1264
1265 if( errors.GetCount() )
1266 {
1267 HTML_MESSAGE_BOX dlg( this, _( "Error List" ) );
1268 dlg.ListSet( errors );
1269 dlg.ShowModal();
1270 }
1271
1272 return errors.GetCount() == 0;
1273}
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:115
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:328
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:871
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:222
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:778
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:725
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:858
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:924
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:796
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:36
static LSET ForbiddenFootprintLayers()
Layers which are not allowed within footprint definitions.
Definition: lset.cpp:802
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:79
int GetWidth() const
Definition: stroke_params.h:89
void SetLineStyle(LINE_STYLE aLineStyle)
Definition: stroke_params.h:93
void SetWidth(int aWidth)
Definition: stroke_params.h:90
LINE_STYLE GetLineStyle() const
Definition: stroke_params.h:92
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:557
#define _(s)
#define IN_EDIT
Item currently edited.
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
bool IsExternalCopperLayer(int aLayerId)
Tests whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition: layer_ids.h:542
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:820
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:154
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
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
std::unique_ptr< UNIT_BINDER > m_Binder
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:691