KiCad PCB EDA Suite
Loading...
Searching...
No Matches
vertex_editor_pane.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
20#include "vertex_editor_pane.h"
21
22#include <limits>
23
24#include <wx/aui/aui.h>
25#include <wx/grid.h>
26#include <wx/settings.h>
27#include <wx/sizer.h>
28#include <wx/translation.h>
29
30#include <board_item.h>
32#include <eda_units.h>
33#include <pcb_base_edit_frame.h>
34#include <pcb_shape.h>
35#include <tool/edit_points.h>
37#include <tool/tool_manager.h>
38#include <tool/actions.h>
39#include <zone.h>
40
41#include <view/view.h>
42
44 wxPanel( aFrame ),
45 m_frame( aFrame ),
46 m_item( nullptr ),
47 m_zone( nullptr ),
48 m_shape( nullptr ),
49 m_grid( nullptr ),
50 m_updatingGrid( false )
51{
52 wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
53 m_grid = new wxGrid( this, wxID_ANY );
54 m_grid->CreateGrid( 0, 2 );
55 m_grid->SetRowLabelSize( 0 );
56 m_grid->SetColLabelValue( 0, _( "X coord" ) );
57 m_grid->SetColLabelValue( 1, _( "Y coord" ) );
58 m_grid->EnableDragGridSize( false );
59 m_grid->EnableDragRowSize( false );
60 m_grid->EnableDragColSize( false );
61 m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
62 m_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
63 m_grid->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS );
64 m_grid->EnableScrolling( false, true );
65 m_grid->SetColMinimalWidth( 0, FromDIP( 120 ) );
66 m_grid->SetColMinimalWidth( 1, FromDIP( 120 ) );
67
68 topSizer->Add( m_grid, 1, wxEXPAND | wxALL, FromDIP( 5 ) );
69
70 SetSizer( topSizer );
71
72 Bind( wxEVT_SIZE, &PCB_VERTEX_EDITOR_PANE::OnSize, this );
73 m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &PCB_VERTEX_EDITOR_PANE::OnGridCellChange, this );
74 m_grid->Bind( wxEVT_GRID_SELECT_CELL, &PCB_VERTEX_EDITOR_PANE::OnGridSelectCell, this );
75
76 // Initial column sizing
78}
79
81{
82 if( m_frame )
83 m_frame->OnVertexEditorPaneClosed( this );
84}
85
86
88{
89 m_item = aItem;
90 m_zone = dynamic_cast<ZONE*>( aItem );
91 m_shape = dynamic_cast<PCB_SHAPE*>( aItem );
92
94
95 if( m_grid->GetNumberRows() > 0 )
96 {
97 m_grid->SetGridCursor( 0, 0 );
98 m_grid->SelectRow( 0 );
99 updateHighlight( 0 );
100 }
101
103}
104
105
107{
108 m_item = nullptr;
109 m_zone = nullptr;
110 m_shape = nullptr;
111
112 m_updatingGrid = true;
113 if( m_grid->GetNumberRows() > 0 )
114 m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
115 m_rows.clear();
116 m_updatingGrid = false;
117}
118
119
121{
122 // Check if the new item is a polygon or zone
123 if( aNewItem )
124 {
125 ZONE* zone = dynamic_cast<ZONE*>( aNewItem );
126 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aNewItem );
127
128 bool isPolygon = ( zone != nullptr ) || ( shape && shape->GetShape() == SHAPE_T::POLY );
129
130 if( isPolygon )
131 {
132 // Update to the new item
133 SetItem( aNewItem );
134 }
135 else
136 {
137 // Not a polygon/zone, clear the editor
138 ClearItem();
139 }
140 }
141 else
142 {
143 // Nothing selected, clear the editor
144 ClearItem();
145 }
146}
147
149{
150 m_updatingGrid = true;
151
152 if( m_grid->GetNumberRows() > 0 )
153 m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
154
155 m_rows.clear();
156
157 SHAPE_POLY_SET* poly = getPoly();
158
159 if( poly )
160 {
161 for( auto it = poly->CIterateWithHoles(); it; ++it )
162 {
163 m_rows.push_back( it.GetIndex() );
164 m_grid->AppendRows( 1 );
165 updateRow( m_grid->GetNumberRows() - 1 );
166 }
167 }
168
169 m_updatingGrid = false;
170}
171
173{
174 if( aRow < 0 || aRow >= m_grid->GetNumberRows() )
175 return;
176
177 SHAPE_POLY_SET* poly = getPoly();
178
179 if( !poly || aRow >= (int) m_rows.size() )
180 return;
181
182 const SHAPE_POLY_SET::VERTEX_INDEX& idx = m_rows[aRow];
183 VECTOR2I vertex = poly->CVertex( idx );
184
185 m_updatingGrid = true;
186 m_grid->SetCellValue( aRow, 0, formatCoord( vertex.x, ORIGIN_TRANSFORMS::ABS_X_COORD ) );
187 m_grid->SetCellValue( aRow, 1, formatCoord( vertex.y, ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
188 m_updatingGrid = false;
189}
190
192{
193 // Highlighting is now handled by the point editor tool
194 // We just refresh the view
195 if( m_frame && m_frame->GetCanvas() )
196 {
197 m_frame->GetCanvas()->GetView()->Update( m_item );
198 m_frame->GetCanvas()->Refresh();
199 }
200}
201
202
204{
205 if( m_zone )
206 {
207 m_zone->UnFill();
208 m_zone->SetNeedRefill( true );
209 m_zone->HatchBorder();
210 }
211
212 if( m_frame && m_frame->GetCanvas() )
213 {
214 m_frame->GetCanvas()->GetView()->Update( m_item );
215 m_frame->GetCanvas()->Refresh();
216 }
217}
218
219bool PCB_VERTEX_EDITOR_PANE::parseCellValue( const wxString& aText,
221 int& aResult ) const
222{
223 // ValueFromString() returns 0 both for a literal "0" and for unparseable input, so it cannot
224 // by itself tell a valid zero from garbage. Require the leading numeric token to hold at least
225 // one digit before trusting the parse; otherwise blank or non-numeric entries would silently
226 // collapse the vertex onto the display origin instead of being rejected.
227 bool hasDigit = false;
228
229 for( wxUniChar ch : aText.Strip( wxString::both ) )
230 {
231 if( wxIsdigit( ch ) )
232 {
233 hasDigit = true;
234 break;
235 }
236
237 if( ch != '+' && ch != '-' && ch != '.' && ch != ',' )
238 break;
239 }
240
241 if( !hasDigit )
242 return false;
243
244 long long displayValue = EDA_UNIT_UTILS::UI::ValueFromString( pcbIUScale, m_frame->GetUserUnits(), aText );
245
246 long long internal = m_frame->GetOriginTransforms().FromDisplay( displayValue, aCoordType );
247
248 if( internal < std::numeric_limits<int>::min() || internal > std::numeric_limits<int>::max() )
249 return false;
250
251 aResult = static_cast<int>( internal );
252 return true;
253}
254
256 ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType ) const
257{
258 int displayValue = m_frame->GetOriginTransforms().ToDisplay( aValue, aCoordType );
259 return m_frame->MessageTextFromValue( displayValue );
260}
261
263{
264 if( m_zone )
265 return m_zone->Outline();
266
267 if( m_shape && m_shape->GetShape() == SHAPE_T::POLY )
268 return &m_shape->GetPolyShape();
269
270 return nullptr;
271}
272
274{
275 if( m_zone )
276 return m_zone->Outline();
277
278 if( m_shape && m_shape->GetShape() == SHAPE_T::POLY )
279 return &m_shape->GetPolyShape();
280
281 return nullptr;
282}
283
284
286{
287 if( m_updatingGrid )
288 return;
289
290 int row = aEvent.GetRow();
291 int col = aEvent.GetCol();
292
293 if( row < 0 || row >= (int) m_rows.size() )
294 {
295 updateHighlight( -1 );
296 return;
297 }
298
299 SHAPE_POLY_SET* poly = getPoly();
300
301 if( !poly || !m_item )
302 return;
303
304 const SHAPE_POLY_SET::VERTEX_INDEX& idx = m_rows[row];
305 VECTOR2I vertex = poly->CVertex( idx );
306 int newValue;
307
310
311 if( !parseCellValue( m_grid->GetCellValue( row, col ), coordType, newValue ) )
312 {
313 updateRow( row );
314 return;
315 }
316
317 int& target = ( col == 0 ) ? vertex.x : vertex.y;
318
319 if( target == newValue )
320 {
321 updateRow( row );
322 return;
323 }
324
325 // Create a new commit for each edit
326 BOARD_COMMIT commit( m_frame );
327 commit.Modify( m_item );
328
329 target = newValue;
330 poly->SetVertex( idx, vertex );
331
332 if( m_zone )
333 {
334 m_zone->UnFill();
335 m_zone->SetNeedRefill( true );
336 m_zone->HatchBorder();
337 }
338
339 commit.Push( _( "Edit Vertex" ) );
340
341 // Update the view after the commit
342 if( m_frame && m_frame->GetCanvas() )
343 {
344 m_frame->GetCanvas()->GetView()->Update( m_item );
345 m_frame->GetCanvas()->Refresh();
346 }
347
348 updateRow( row );
349 updateHighlight( row );
350}
351
353{
354 updateHighlight( aEvent.GetRow() );
355 aEvent.Skip();
356}
357
358
360{
361 if( !m_grid || m_grid->GetNumberCols() != 2 )
362 return;
363
364 // Get the available width (excluding row labels and scrollbar)
365 int width = m_grid->GetClientSize().GetWidth() - m_grid->GetRowLabelSize();
366
367 // Reserve space for vertical scrollbar if it's shown
368 if( m_grid->GetScrollRange( wxVERTICAL ) > 0 )
369 width -= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X );
370
371 // Split the width equally between the two columns
372 int colWidth = width / 2;
373
374 if( colWidth > m_grid->GetColMinimalAcceptableWidth() )
375 {
376 m_grid->SetColSize( 0, colWidth );
377 m_grid->SetColSize( 1, colWidth );
378 }
379}
380
381
382void PCB_VERTEX_EDITOR_PANE::OnSize( wxSizeEvent& aEvent )
383{
385 aEvent.Skip();
386}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
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
SHAPE_T GetShape() const
Definition eda_shape.h:185
COORD_TYPES_T
The supported Display Origin Transform types.
Common, abstract interface for edit frames.
void SetItem(BOARD_ITEM *aItem)
PCB_BASE_EDIT_FRAME * m_frame
void OnGridSelectCell(wxGridEvent &aEvent)
void OnSize(wxSizeEvent &aEvent)
std::vector< SHAPE_POLY_SET::VERTEX_INDEX > m_rows
wxString formatCoord(int aValue, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType) const
void OnSelectionChanged(BOARD_ITEM *aNewItem)
Update the pane in response to external selection changes.
PCB_VERTEX_EDITOR_PANE(PCB_BASE_EDIT_FRAME *aFrame)
bool parseCellValue(const wxString &aText, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType, int &aResult) const
void OnGridCellChange(wxGridEvent &aEvent)
Represent a set of closed polygons.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
CONST_ITERATOR CIterateWithHoles(int aOutline) const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
#define _(s)
KICOMMON_API long long int ValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue in aUnits to internal units used by the application.
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683