KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_grid_item_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <algorithm>
23#include <functional>
24
25#include <board.h>
26#include <board_commit.h>
28#include <pcb_base_edit_frame.h>
29#include <pcb_edit_frame.h>
30#include <pcb_grid_item.h>
31#include <string_utils.h>
32#include <trigo.h>
33#include <widgets/unit_binder.h>
34
35#include <wx/notebook.h>
36
37
39{
40public:
42
43 bool TransferDataToWindow() override;
44 bool TransferDataFromWindow() override;
45
46private:
47 // The three notebook pages all edit the same extent rectangle; field order matches CTRL_IDX.
70
71 int getInt( size_t aIndex ) const
72 {
73 return static_cast<int>( m_boundFields[aIndex].m_Binder->GetValue() );
74 }
75
76 void changeValue( size_t aIndex, int aValue )
77 {
78 m_boundFields[aIndex].m_Binder->ChangeValue( aValue );
79 }
80
81 EDA_ANGLE getAngle( size_t aIndex ) const
82 {
83 return m_boundFields[aIndex].m_Binder->GetAngleValue();
84 }
85
86 void changeAngle( size_t aIndex, const EDA_ANGLE& aValue )
87 {
88 m_boundFields[aIndex].m_Binder->ChangeAngleValue( aValue );
89 }
90
91 void bindFields( size_t aFrom, size_t aTo, std::function<void()> aCb )
92 {
93 for( size_t i = aFrom; i <= aTo; ++i )
94 {
95 m_boundFields[i].m_Ctrl->Bind( wxEVT_TEXT,
96 [aCb]( wxCommandEvent& )
97 {
98 aCb();
99 } );
100 }
101 }
102
103 // The extent box is stored in the grid's local frame and drawn rotated by the
104 // orientation, so corner fields must map through the rotation to stay on the
105 // actual outline.
106 VECTOR2I toWorld( VECTOR2I aLocal ) const
107 {
108 RotatePoint( aLocal, m_workingCopy.GetOrientation() );
109 return aLocal;
110 }
111
112 VECTOR2I toLocal( VECTOR2I aWorld ) const
113 {
114 RotatePoint( aWorld, -m_workingCopy.GetOrientation() );
115 return aWorld;
116 }
117
118 void onCornersChange();
119 void onCornerSizeChange();
120 void onCenterSizeChange();
121 void onPolarChange();
122 void updateCorners();
123 void updateCornerSize();
124 void updateCenterSize();
125 void updatePolar();
126
127 void onGridType( wxCommandEvent& aEvent );
128 void saveSpacingToItem( PCB_GRID_TYPE aType );
129 void updateSpacingRows();
130 void updatePagesForType();
131
135 std::vector<BOUND_CONTROL> m_boundFields;
139};
140
141
144 m_parent( aParent ),
145 m_item( aItem ),
146 m_workingCopy( *aItem ),
150{
151 SetTitle( wxString::Format( GetTitle(), m_item->GetFriendlyName() ) );
152 m_hash_key = TO_UTF8( GetTitle() );
153
154 m_orientation.SetPrecision( 4 );
156
157 // Field order must match CTRL_IDX.
158 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 0, _( "Start Point" ), false, m_boundFields );
159 AddXYPointToSizer( *aParent, *m_gbsRectangleByCorners, 0, 3, _( "End Point" ), false, m_boundFields );
160
161 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 0, _( "Start Point" ), false, m_boundFields );
162 AddXYPointToSizer( *aParent, *m_gbsRectangleByCornerSize, 0, 3, _( "Size" ), true, m_boundFields );
163
164 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 0, _( "Center" ), false, m_boundFields );
165 AddXYPointToSizer( *aParent, *m_gbsRectangleByCenterSize, 0, 3, _( "Size" ), true, m_boundFields );
166
167 AddXYPointToSizer( *aParent, *m_gbsPolarCenterRadius, 0, 0, _( "Center" ), false, m_boundFields );
169 AddFieldToSizer( *aParent, *m_gbsPolarCenterRadius, 2, 3, _( "Angular extent" ), ORIGIN_TRANSFORMS::NOT_A_COORD, true, m_boundFields );
170
171 bindFields( START_X, END_Y, [this]() { onCornersChange(); } );
172 bindFields( CORNER_X, CORNER_H, [this]() { onCornerSizeChange(); } );
173 bindFields( CENTER_X, CENTER_H, [this]() { onCenterSizeChange(); } );
175
178 page->Layout();
179
180 m_gridTypeCtrl->Bind( wxEVT_CHOICE, &DIALOG_GRID_ITEM_PROPERTIES::onGridType, this );
181
182 // Corner fields are world coordinates on the rotated outline, so they track the angle.
183 m_orientationCtrl->Bind( wxEVT_TEXT,
184 [this]( wxCommandEvent& )
185 {
186 m_workingCopy.SetOrientation( m_orientation.GetAngleValue() );
189 } );
190
191 // Do not allow locking items in the footprint editor
192 m_locked->Show( dynamic_cast<PCB_EDIT_FRAME*>( aParent ) != nullptr );
193
195
196 // Now all widgets have the size fixed, call FinishDialogSettings
198}
199
200
202{
203 const VECTOR2I p0( getInt( START_X ), getInt( START_Y ) );
204 const VECTOR2I p1( getInt( END_X ), getInt( END_Y ) );
205
206 m_workingCopy.SetPosition( ( p0 + p1 ) / 2 );
207 m_workingCopy.SetExtent( toLocal( p1 - p0 ) / 2 );
208
211 updatePolar();
212}
213
214
216{
217 const VECTOR2I p0( getInt( CORNER_X ), getInt( CORNER_Y ) );
218 const VECTOR2I size( getInt( CORNER_W ), getInt( CORNER_H ) );
219
220 m_workingCopy.SetPosition( p0 + toWorld( size / 2 ) );
221 m_workingCopy.SetExtent( size / 2 );
222
225 updatePolar();
226}
227
228
230{
232 const VECTOR2I size( getInt( CENTER_W ), getInt( CENTER_H ) );
233
234 m_workingCopy.SetPosition( center );
235 m_workingCopy.SetExtent( size / 2 );
236
239 updatePolar();
240}
241
242
244{
246 m_workingCopy.SetRadiusExtent( getInt( POLAR_RADIUS ) );
247 m_workingCopy.SetPhiExtentDegrees( getAngle( POLAR_PHI ).AsDegrees() );
248
252}
253
254
256{
257 const VECTOR2I half = toWorld( m_workingCopy.GetExtent() );
258 const VECTOR2I p0 = m_workingCopy.GetPosition() - half;
259 const VECTOR2I p1 = m_workingCopy.GetPosition() + half;
260
261 changeValue( START_X, p0.x );
262 changeValue( START_Y, p0.y );
263 changeValue( END_X, p1.x );
264 changeValue( END_Y, p1.y );
265}
266
267
269{
270 const VECTOR2I p0 = m_workingCopy.GetPosition() - toWorld( m_workingCopy.GetExtent() );
271
272 changeValue( CORNER_X, p0.x );
273 changeValue( CORNER_Y, p0.y );
274 changeValue( CORNER_W, m_workingCopy.GetExtentX() );
275 changeValue( CORNER_H, m_workingCopy.GetExtentY() );
276}
277
278
280{
281 changeValue( CENTER_X, m_workingCopy.GetPosition().x );
282 changeValue( CENTER_Y, m_workingCopy.GetPosition().y );
283 changeValue( CENTER_W, m_workingCopy.GetExtentX() );
284 changeValue( CENTER_H, m_workingCopy.GetExtentY() );
285}
286
287
289{
290 changeValue( POLAR_CENTER_X, m_workingCopy.GetPosition().x );
291 changeValue( POLAR_CENTER_Y, m_workingCopy.GetPosition().y );
292 changeValue( POLAR_RADIUS, m_workingCopy.GetRadiusExtent() );
293 changeAngle( POLAR_PHI, m_workingCopy.GetPhiExtent() );
294}
295
296
298{
299 const bool polar = m_workingCopy.GetGridItemType() == PCB_GRID_TYPE::POLAR;
300
301 // RemovePage() keeps the window alive as a (visible) child, so hide it explicitly.
302 while( m_notebookGridDefs->GetPageCount() > 0 )
303 {
304 m_notebookGridDefs->GetPage( m_notebookGridDefs->GetPageCount() - 1 )->Hide();
305 m_notebookGridDefs->RemovePage( m_notebookGridDefs->GetPageCount() - 1 );
306 }
307
308 if( polar )
309 {
310 m_notebookGridDefs->AddPage( m_polarCenterRadius, _( "Center and Radius" ), true );
311 }
312 else
313 {
314 m_notebookGridDefs->AddPage( m_rectangleByCorners, _( "By Corners" ), false );
315 m_notebookGridDefs->AddPage( m_rectangleByCornerSize, _( "By Corner and Size" ), false );
316 m_notebookGridDefs->AddPage( m_rectangleByCenterSize, _( "By Center and Size" ), true );
317 }
318
319 Layout();
320}
321
322
323void DIALOG_GRID_ITEM_PROPERTIES::onGridType( wxCommandEvent& aEvent )
324{
325 // Capture the field values under their old meaning before relabelling.
326 saveSpacingToItem( m_workingCopy.GetGridItemType() );
327 m_workingCopy.SetGridItemType( static_cast<PCB_GRID_TYPE>( m_gridTypeCtrl->GetSelection() ) );
330}
331
332
334{
335 if( aType == PCB_GRID_TYPE::POLAR )
336 {
337 m_workingCopy.SetRadiusSpacing( m_spacingX.GetIntValue() );
338 m_workingCopy.SetPhiSpacingDegrees( m_spacingY.GetAngleValue().AsDegrees() );
339 }
340 else
341 {
342 m_workingCopy.SetSpacingX( m_spacingX.GetIntValue() );
343 m_workingCopy.SetSpacingY( m_spacingY.GetIntValue() );
344 }
345}
346
347
349{
350 if( m_workingCopy.GetGridItemType() == PCB_GRID_TYPE::POLAR )
351 {
352 m_spacingX.SetLabel( _( "Radius spacing:" ) );
353 m_spacingY.SetLabel( _( "Angular spacing:" ) );
354 m_spacingY.SetPrecision( 4 );
355 m_spacingY.SetUnits( EDA_UNITS::DEGREES );
356 m_spacingX.ChangeValue( m_workingCopy.GetRadiusSpacing() );
357 m_spacingY.ChangeAngleValue( m_workingCopy.GetPhiSpacing() );
358 }
359 else
360 {
361 m_spacingX.SetLabel( _( "Spacing X:" ) );
362 m_spacingY.SetLabel( _( "Spacing Y:" ) );
363 m_spacingY.SetPrecision( 0 );
364 m_spacingY.SetUnits( m_parent->GetUserUnits() );
365 m_spacingX.ChangeValue( m_workingCopy.GetSpacingX() );
366 m_spacingY.ChangeValue( m_workingCopy.GetSpacingY() );
367 }
368}
369
370
372{
376 updatePolar();
377
378 m_orientation.ChangeAngleValue( m_workingCopy.GetOrientation() );
379
380 m_gridTypeCtrl->SetSelection( static_cast<int>( m_workingCopy.GetGridItemType() ) );
383
384 m_tickIntervalCtrl->ChangeValue( wxString::Format( wxT( "%u" ), m_workingCopy.GetTickInterval() ) );
385 m_priorityCtrl->ChangeValue( wxString::Format( wxT( "%u" ), m_workingCopy.GetAssignedPriority() ) );
386
387 m_affectsCursor->SetValue( m_workingCopy.Affects().cursor );
388 m_affectsRouting->SetValue( m_workingCopy.Affects().routing );
389 m_affectsPlacement->SetValue( m_workingCopy.Affects().placement );
390
391 m_locked->SetValue( m_item->IsLocked() );
392
393 return true;
394}
395
396
398{
399 if( !DIALOG_GRID_ITEM_PROPERTIES_BASE::TransferDataFromWindow() )
400 return false;
401
402 saveSpacingToItem( m_workingCopy.GetGridItemType() );
403
404 m_workingCopy.SetOrientation( m_orientation.GetAngleValue() );
405
406 long tickInterval = 0;
407 m_tickIntervalCtrl->GetValue().ToLong( &tickInterval );
408 m_workingCopy.SetTickInterval( std::max( 0L, tickInterval ) );
409
410 long priority = 0;
411 m_priorityCtrl->GetValue().ToLong( &priority );
412 m_workingCopy.SetAssignedPriority( std::max( 0L, priority ) );
413
414 m_workingCopy.Affects().cursor = m_affectsCursor->GetValue();
415 m_workingCopy.Affects().routing = m_affectsRouting->GetValue();
416 m_workingCopy.Affects().placement = m_affectsPlacement->GetValue();
417
418 BOARD_COMMIT commit( m_parent );
419 commit.Modify( m_item );
420
421 bool pushCommit = ( m_item->GetEditFlags() == 0 );
422
423 // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
424 // SaveCopyInUndoList for the same item if it is moved, and then rotated, edited, etc....
425 if( !pushCommit )
426 m_item->SetFlags( IN_EDIT );
427
429 m_item->SetLocked( m_locked->GetValue() );
430
431 if( pushCommit )
432 commit.Push( _( "Edit Grid Item Properties" ) );
433
434 return true;
435}
436
437
439{
440 wxCHECK_RET( aGridItem, wxT( "ShowGridItemPropertiesDialog() error: NULL item" ) );
441
442 DIALOG_GRID_ITEM_PROPERTIES dlg( this, aGridItem );
443 dlg.ShowModal();
444}
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
DIALOG_GRID_ITEM_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("%s Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
std::vector< BOUND_CONTROL > m_boundFields
void changeAngle(size_t aIndex, const EDA_ANGLE &aValue)
void bindFields(size_t aFrom, size_t aTo, std::function< void()> aCb)
EDA_ANGLE getAngle(size_t aIndex) const
DIALOG_GRID_ITEM_PROPERTIES(PCB_BASE_EDIT_FRAME *aParent, PCB_GRID_ITEM *aItem)
VECTOR2I toLocal(VECTOR2I aWorld) const
VECTOR2I toWorld(VECTOR2I aLocal) const
void saveSpacingToItem(PCB_GRID_TYPE aType)
void onGridType(wxCommandEvent &aEvent)
void changeValue(size_t aIndex, int aValue)
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
Common, abstract interface for edit frames.
void ShowGridItemPropertiesDialog(PCB_GRID_ITEM *aGridItem)
The main frame for Pcbnew.
#define _(s)
#define IN_EDIT
Item currently edited.
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)
PCB_GRID_TYPE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
VECTOR2I center
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683