KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_fp_edit_pad_table.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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <wx/button.h>
27#include <wx/sizer.h>
28#include <wx/dcclient.h>
29#include <wx/stattext.h>
30#include <pcb_shape.h>
31#include <widgets/wx_grid.h>
34#include <base_units.h>
35#include <units_provider.h>
36#include <board.h>
37#include <footprint.h>
39#include <grid_tricks.h>
40#include <pin_numbers.h>
41#include <board_commit.h>
42
43
44// Helper to map shape string to PAD_SHAPE
45static PAD_SHAPE ShapeFromString( const wxString& shape )
46{
47 if( shape == _( "Oval" ) ) return PAD_SHAPE::OVAL;
48 if( shape == _( "Rectangle" ) ) return PAD_SHAPE::RECTANGLE;
49 if( shape == _( "Trapezoid" ) ) return PAD_SHAPE::TRAPEZOID;
50 if( shape == _( "Rounded rectangle" ) ) return PAD_SHAPE::ROUNDRECT;
51 if( shape == _( "Chamfered rectangle" ) ) return PAD_SHAPE::CHAMFERED_RECT;
52 if( shape == _( "Custom shape" ) ) return PAD_SHAPE::CUSTOM;
53
54 return PAD_SHAPE::CIRCLE;
55}
56
57
59 DIALOG_SHIM( (wxWindow*)aParent, wxID_ANY, _( "Pad Table" ), wxDefaultPosition, wxDefaultSize,
60 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
61 m_frame( aParent ),
62 m_grid( nullptr ),
63 m_footprint( aFootprint ),
65 m_summaryDirty( true )
66{
67 wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
68
69 wxBoxSizer* bSummarySizer;
70 bSummarySizer = new wxBoxSizer( wxHORIZONTAL );
71
72 m_staticTextPinNumbers = new wxStaticText( this, wxID_ANY, _( "Pad numbers:" ) );
73 m_staticTextPinNumbers->Wrap( -1 );
74 bSummarySizer->Add( m_staticTextPinNumbers, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
75
76 m_pin_numbers_summary = new wxStaticText( this, wxID_ANY, _( "0" ) );
77 m_pin_numbers_summary->Wrap( -1 );
78 bSummarySizer->Add( m_pin_numbers_summary, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
79
80 bSummarySizer->Add( 0, 0, 1, wxEXPAND, 5 );
81
82 m_staticTextPinCount = new wxStaticText( this, wxID_ANY, _( "Pad count:" ) );
83 m_staticTextPinCount->Wrap( -1 );
84 bSummarySizer->Add( m_staticTextPinCount, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 10 );
85
86 m_pin_count = new wxStaticText( this, wxID_ANY, _( "0" ) );
87 m_pin_count->Wrap( -1 );
88 bSummarySizer->Add( m_pin_count, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
89
90 bSummarySizer->Add( 0, 0, 1, wxEXPAND, 5 );
91
92 m_staticTextDuplicatePins = new wxStaticText( this, wxID_ANY, _("Duplicate pads:" ) );
93 m_staticTextDuplicatePins->Wrap( -1 );
94 bSummarySizer->Add( m_staticTextDuplicatePins, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 10 );
95
96 m_duplicate_pins = new wxStaticText( this, wxID_ANY, _( "0" ) );
97 m_duplicate_pins->Wrap( -1 );
98 bSummarySizer->Add( m_duplicate_pins, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
99
100 topSizer->Add( bSummarySizer, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
101
102 m_grid = new WX_GRID( this, wxID_ANY );
103 m_grid->CreateGrid( m_footprint->GetPadCount(), 11 );
104 m_grid->SetColLabelValue( COL_NUMBER, _( "Number" ) );
105 m_grid->SetColLabelValue( COL_TYPE, _( "Type" ) );
106 m_grid->SetColLabelValue( COL_SHAPE, _( "Shape" ) );
107 m_grid->SetColLabelValue( COL_POS_X, _( "X Position" ) );
108 m_grid->SetColLabelValue( COL_POS_Y, _( "Y Position" ) );
109 m_grid->SetColLabelValue( COL_SIZE_X, _( "Size X" ) );
110 m_grid->SetColLabelValue( COL_SIZE_Y, _( "Size Y" ) );
111 m_grid->SetColLabelValue( COL_DRILL_X, _( "Drill X" ) );
112 m_grid->SetColLabelValue( COL_DRILL_Y, _( "Drill Y" ) );
113 m_grid->SetColLabelValue( COL_P2D_LENGTH, _( "Pad->Die Length" ) );
114 m_grid->SetColLabelValue( COL_P2D_DELAY, _( "Pad->Die Delay" ) );
115 m_grid->SetColLabelSize( 24 );
116 m_grid->HideRowLabels();
117 m_grid->EnableEditing( true );
118
119 wxGridCellAttr* attr;
120
121 // Type column editor (attribute)
122 attr = new wxGridCellAttr;
123 wxArrayString typeNames;
124 typeNames.push_back( _( "Through-hole" ) ); // PTH
125 typeNames.push_back( _( "SMD" ) ); // SMD
126 typeNames.push_back( _( "Connector" ) ); // CONN SMD? (use CONN?)
127 typeNames.push_back( _( "NPTH" ) ); // NPTH
128 typeNames.push_back( _( "Aperture" ) ); // inferred copper-less
129 attr->SetEditor( new GRID_CELL_COMBOBOX( typeNames ) );
130 m_grid->SetColAttr( COL_TYPE, attr );
131
132 attr = new wxGridCellAttr;
133 wxArrayString shapeNames;
141 attr->SetEditor( new GRID_CELL_COMBOBOX( shapeNames ) );
142 m_grid->SetColAttr( COL_SHAPE, attr );
143
144 attr = new wxGridCellAttr;
145 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
146 m_grid->SetColAttr( COL_POS_X, attr );
147
148 attr = new wxGridCellAttr;
149 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
150 m_grid->SetColAttr( COL_POS_Y, attr );
151
152 attr = new wxGridCellAttr;
153 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
154 m_grid->SetColAttr( COL_SIZE_X, attr );
155
156 attr = new wxGridCellAttr;
157 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
158 m_grid->SetColAttr( COL_SIZE_Y, attr );
159
160 // Drill X
161 attr = new wxGridCellAttr;
162 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
163 m_grid->SetColAttr( COL_DRILL_X, attr );
164
165 // Drill Y
166 attr = new wxGridCellAttr;
167 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
168 m_grid->SetColAttr( COL_DRILL_Y, attr );
169
170 // Pad->Die Length
171 attr = new wxGridCellAttr;
172 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
173 m_grid->SetColAttr( COL_P2D_LENGTH, attr );
174
175 // Pad->Die Delay
176 attr = new wxGridCellAttr;
177 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
178 m_grid->SetColAttr( COL_P2D_DELAY, attr );
179
180 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_X );
181 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_Y );
182 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_X );
183 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_Y );
184 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_X );
185 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_Y );
187
188 // add Cut, Copy, and Paste to wxGrid
189 m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
190
191 topSizer->Add( m_grid, 1, wxEXPAND | wxALL, 5 );
192
193 wxStdDialogButtonSizer* buttons = new wxStdDialogButtonSizer();
194 buttons->AddButton( new wxButton( this, wxID_OK ) );
195 buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
196 buttons->Realize();
197 topSizer->Add( buttons, 0, wxALIGN_RIGHT | wxALL, 5 );
198
199 SetSizer( topSizer );
201
203
204 // Bind cell change handlers for real-time updates
205 m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged, this );
206 m_grid->Bind( wxEVT_GRID_SELECT_CELL, &DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell, this );
207 Bind( wxEVT_UPDATE_UI, &DIALOG_FP_EDIT_PAD_TABLE::OnUpdateUI, this );
208
209 // Listen for cancel
210 Bind( wxEVT_BUTTON,
211 [this]( wxCommandEvent& aEvt )
212 {
213 m_cancelled = true;
214 aEvt.Skip();
215 },
216 wxID_CANCEL );
217
218 Layout();
219 topSizer->Fit( this );
221}
222
223
225{
226 if( m_cancelled )
228
229 // destroy GRID_TRICKS before m_grid.
230 m_grid->PopEventHandler( true );
231
232 m_grid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged, this );
233 m_grid->Unbind( wxEVT_GRID_SELECT_CELL, &DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell, this );
234 Unbind( wxEVT_UPDATE_UI, &DIALOG_FP_EDIT_PAD_TABLE::OnUpdateUI, this );
235}
236
237
239{
240 if( !m_footprint )
241 return false;
242
243 int row = 0;
244
245 for( PAD* pad : m_footprint->Pads() )
246 {
247 if( row >= m_grid->GetNumberRows() )
248 continue;
249
250 m_grid->SetCellValue( row, COL_NUMBER, pad->GetNumber() );
251
252 // Pad attribute to string
253 wxString attrStr;
254
255 switch( pad->GetAttribute() )
256 {
257 case PAD_ATTRIB::PTH: attrStr = _( "Through-hole" ); break;
258 case PAD_ATTRIB::SMD: attrStr = _( "SMD" ); break;
259 case PAD_ATTRIB::CONN: attrStr = _( "Connector" ); break;
260 case PAD_ATTRIB::NPTH: attrStr = _( "NPTH" ); break;
261 default: attrStr = _( "Through-hole" ); break;
262 }
263
264 int size_x = pad->GetSize( PADSTACK::ALL_LAYERS ).x;
265 int size_y = pad->GetSize( PADSTACK::ALL_LAYERS ).y;
266 wxString padShape = pad->ShowPadShape( PADSTACK::ALL_LAYERS );
267
268 pad->Padstack().ForEachUniqueLayer(
269 [&]( PCB_LAYER_ID aLayer )
270 {
271 if( pad->GetSize( aLayer ).x != size_x )
272 size_x = -1;
273
274 if( pad->GetSize( aLayer ).y != size_y )
275 size_y = -1;
276
277 if( pad->ShowPadShape( aLayer ) != padShape )
278 padShape = INDETERMINATE_STATE;
279 } );
280
281 if( pad->IsAperturePad() )
282 attrStr = _( "Aperture" );
283
284 m_grid->SetCellValue( row, COL_TYPE, attrStr );
285 m_grid->SetCellValue( row, COL_SHAPE, padShape );
286 m_grid->SetCellValue( row, COL_POS_X, m_unitsProvider->StringFromValue( pad->GetPosition().x, true ) );
287 m_grid->SetCellValue( row, COL_POS_Y, m_unitsProvider->StringFromValue( pad->GetPosition().y, true ) );
288 m_grid->SetCellValue( row, COL_SIZE_X, size_x >= 0 ? m_unitsProvider->StringFromValue( size_x, true )
290 m_grid->SetCellValue( row, COL_SIZE_Y, size_y >= 0 ? m_unitsProvider->StringFromValue( size_y, true )
292
293 // Drill values (only meaningful for PTH or NPTH). Leave empty otherwise.
294 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
295 {
296 VECTOR2I drill = pad->GetDrillSize();
297
298 if( drill.x > 0 )
299 m_grid->SetCellValue( row, COL_DRILL_X, m_unitsProvider->StringFromValue( drill.x, true ) );
300
301 if( drill.y > 0 )
302 m_grid->SetCellValue( row, COL_DRILL_Y, m_unitsProvider->StringFromValue( drill.y, true ) );
303 }
304 else
305 {
306 // For non-PTH pads, drill columns are not applicable.
307 m_grid->SetReadOnly( row, COL_DRILL_X, true );
308 m_grid->SetReadOnly( row, COL_DRILL_Y, true );
309 }
310
311 // Pad to die metrics
312 if( pad->GetPadToDieLength() )
313 m_grid->SetCellValue( row, COL_P2D_LENGTH, m_unitsProvider->StringFromValue( pad->GetPadToDieLength(),
314 true ) );
315
316 if( pad->GetPadToDieDelay() )
317 m_grid->SetCellValue( row, COL_P2D_DELAY, wxString::Format( "%d", pad->GetPadToDieDelay() ) );
318
319 row++;
320 }
321
322 // Auto size the data columns first to get reasonable initial widths
323 m_grid->AutoSizeColumns();
324
325 // Ensure the Shape column (index 1) is wide enough for the longest translated
326 // shape text plus the dropdown arrow / padding. We compute a max text width
327 // using a device context and add a platform neutral padding.
328 {
329 wxClientDC dc( m_grid );
330 dc.SetFont( m_grid->GetFont() );
331
332 wxArrayString shapeNames;
340
341 int maxWidth = 0;
342
343 for( const wxString& str : shapeNames )
344 {
345 int w, h;
346 dc.GetTextExtent( str, &w, &h );
347 maxWidth = std::max( maxWidth, w );
348 }
349
350 // Add padding for internal cell margins + dropdown control.
351 int padding = FromDIP( 30 ); // heuristic: 2*margin + arrow button
352 m_grid->SetColSize( COL_SHAPE, maxWidth + padding );
353 }
354
355 // Record initial proportions for proportional resizing later.
357 Bind( wxEVT_SIZE, &DIALOG_FP_EDIT_PAD_TABLE::OnSize, this );
358
359 // Run an initial proportional resize using current client size so columns
360 // respect proportions immediately.
361 wxSizeEvent sizeEvt( GetSize(), GetId() );
362 CallAfter(
363 [this, sizeEvt]
364 {
365 wxSizeEvent evt( sizeEvt );
366 this->OnSize( evt );
367 } );
368
369 // If pads exist, select the first row to show initial highlight
370 if( m_grid->GetNumberRows() > 0 )
371 {
372 m_grid->SetGridCursor( 0, 0 );
373
374 // Construct event with required parameters (id, type, obj, row, col,...)
375 wxGridEvent ev( m_grid->GetId(), wxEVT_GRID_SELECT_CELL, m_grid, 0, 0, -1, -1, true );
376 OnSelectCell( ev );
377 }
378
379 return true;
380}
381
382
384{
385 m_originalPads.clear();
386
387 if( !m_footprint )
388 return;
389
390 for( PAD* pad : m_footprint->Pads() )
391 {
392 PAD_SNAPSHOT snap( pad );
393 snap.number = pad->GetNumber();
394 snap.position = pad->GetPosition();
395 snap.padstack = pad->Padstack();
396 snap.attribute = pad->GetAttribute();
397 snap.padToDieLength= pad->GetPadToDieLength();
398 snap.padToDieDelay = pad->GetPadToDieDelay();
399 m_originalPads.push_back( snap );
400 }
401}
402
403
405{
406 if( !m_footprint )
407 return;
408
409 size_t idx = 0;
410 PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
411 PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
412
413 for( PAD* pad : m_footprint->Pads() )
414 {
415 if( idx >= m_originalPads.size() )
416 break;
417
418 const PAD_SNAPSHOT& snap = m_originalPads[idx++];
419 pad->SetNumber( snap.number );
420 pad->SetPosition( snap.position );
421 pad->SetPadstack( snap.padstack );
422 pad->SetAttribute( snap.attribute );
423 pad->SetPadToDieLength( snap.padToDieLength );
424 pad->SetPadToDieDelay( snap.padToDieDelay );
425 pad->ClearBrightened();
426
427 if( canvas )
428 canvas->GetView()->Update( pad, KIGFX::REPAINT );
429 }
430
431 if( canvas )
432 {
434 canvas->ForceRefresh();
435 }
436
437 m_summaryDirty = true;
438}
439
440
442{
443 if( !m_grid->CommitPendingChanges() )
444 return false;
445
446 if( !m_footprint )
447 return true;
448
450 BOARD_COMMIT commit( m_frame );
451
452 int row = 0;
453
454 for( PAD* pad : m_footprint->Pads() )
455 {
456 commit.Modify( pad );
457 pad->SetNumber( m_grid->GetCellValue( row, COL_NUMBER ) );
458
459 wxString typeStr = m_grid->GetCellValue( row, COL_TYPE );
460
461 if( typeStr == _( "Through-hole" ) )
462 pad->SetAttribute( PAD_ATTRIB::PTH );
463 else if( typeStr == _( "SMD" ) )
464 pad->SetAttribute( PAD_ATTRIB::SMD );
465 else if( typeStr == _( "Connector" ) )
466 pad->SetAttribute( PAD_ATTRIB::CONN );
467 else if( typeStr == _( "NPTH" ) )
468 pad->SetAttribute( PAD_ATTRIB::NPTH );
469 // Aperture derived by copper-less layers; do not overwrite attribute here.
470
471 wxString shape = m_grid->GetCellValue( row, COL_SHAPE );
472
473 if( shape != INDETERMINATE_STATE )
474 {
475 PAD_SHAPE newShape = ShapeFromString( shape );
476
477 pad->Padstack().ForEachUniqueLayer(
478 [&]( PCB_LAYER_ID aLayer )
479 {
480 pad->SetShape( aLayer, newShape );
481 } );
482 }
483
484 VECTOR2I pos;
485 pos.x = m_grid->GetUnitValue( row, COL_POS_X );
486 pos.y = m_grid->GetUnitValue( row, COL_POS_Y );
487 pad->SetPosition( pos );
488
489 wxString size_x_value = m_grid->GetCellValue( row, COL_SIZE_X );
490
491 if( size_x_value != INDETERMINATE_STATE )
492 {
493 int size_x = m_grid->GetUnitValue( row, COL_SIZE_X );
494
495 pad->Padstack().ForEachUniqueLayer(
496 [&]( PCB_LAYER_ID aLayer )
497 {
498 VECTOR2I size( size_x, pad->GetSize( aLayer ).y );
499 pad->SetSize( aLayer, size );
500 } );
501 }
502
503 wxString size_y_value = m_grid->GetCellValue( row, COL_SIZE_Y );
504
505 if( size_y_value != INDETERMINATE_STATE )
506 {
507 int size_y = m_grid->GetUnitValue( row, COL_SIZE_Y );
508
509 pad->Padstack().ForEachUniqueLayer(
510 [&]( PCB_LAYER_ID aLayer )
511 {
512 VECTOR2I size( pad->GetSize( aLayer ).x, size_y );
513 pad->SetSize( aLayer, size );
514 } );
515 }
516
517 // Drill sizes (only if attribute allows)
518 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
519 {
520 int drill_x = m_grid->GetUnitValue( row, COL_DRILL_X );
521 int drill_y = m_grid->GetUnitValue( row, COL_DRILL_Y );
522
523 if( drill_x > 0 || drill_y > 0 )
524 {
525 if( drill_x <= 0 )
526 drill_x = drill_y;
527
528 if( drill_y <= 0 )
529 drill_y = drill_x;
530
531 pad->SetDrillSize( { drill_x, drill_y } );
532 }
533 }
534
535 // Pad->Die
536 wxString delayStr = m_grid->GetCellValue( row, COL_P2D_DELAY );
537 wxString lenStr = m_grid->GetCellValue( row, COL_P2D_LENGTH );
538
539 if( !lenStr.IsEmpty() )
540 pad->SetPadToDieLength( m_grid->GetUnitValue( row, COL_P2D_LENGTH ) );
541 else
542 pad->SetPadToDieLength( 0 );
543
544 if( !delayStr.IsEmpty() )
545 {
546 long delayVal;
547
548 if( delayStr.ToLong( &delayVal ) )
549 pad->SetPadToDieDelay( (int) delayVal );
550 else
551 pad->SetPadToDieDelay( 0 );
552 }
553 else
554 pad->SetPadToDieDelay( 0 );
555
556 row++;
557 }
558
559 commit.Push( _( "Edit Pads" ) );
560 m_frame->Refresh();
561
562 return true;
563}
564
565
567{
568 m_colProportions.clear();
569 m_minColWidths.clear();
570
571 if( !m_grid )
572 return;
573
574 // Only consider the actual data columns (all of them since row labels are hidden)
575 int cols = m_grid->GetNumberCols();
576 int total = 0;
577 std::vector<int> widths;
578 widths.reserve( cols );
579
580 for( int c = 0; c < cols; ++c )
581 {
582 int w = m_grid->GetColSize( c );
583 widths.push_back( w );
584 total += w;
585 }
586
587 if( total <= 0 )
588 return;
589
590 for( int w : widths )
591 {
592 m_colProportions.push_back( (double) w / (double) total );
593 m_minColWidths.push_back( w );
594 }
595}
596
597
598void DIALOG_FP_EDIT_PAD_TABLE::OnSize( wxSizeEvent& aEvent )
599{
600 if( m_colProportions.empty() )
601 {
602 aEvent.Skip();
603 return;
604 }
605
606 // Compute available total width for columns and resize keeping proportions.
607 int cols = m_grid->GetNumberCols();
608 int available = 0;
609
610 for( int c = 0; c < cols; ++c )
611 available += m_grid->GetColSize( c );
612
613 // Use client size of grid minus scrollbar estimate to better distribute.
614 int clientW = m_grid->GetClientSize().x;
615
616 if( clientW > 0 )
617 available = clientW; // prefer actual client width
618
619 int used = 0;
620
621 for( int c = 0; c < cols; ++c )
622 {
623 int target = (int) std::round( m_colProportions[c] * available );
624 target = std::max( target, m_minColWidths[c] );
625
626 // Defer last column to absorb rounding diff.
627 if( c == cols - 1 )
628 target = std::max( available - used, m_minColWidths[c] );
629
630 m_grid->SetColSize( c, target );
631 used += target;
632 }
633
634 aEvent.Skip();
635}
636
637
638void DIALOG_FP_EDIT_PAD_TABLE::OnCharHook( wxKeyEvent& aEvent )
639{
640 if( m_grid->IsCellEditControlShown() && m_grid->GetGridCursorCol() == COL_NUMBER )
641 m_summaryDirty = true;
642
643 DIALOG_SHIM::OnCharHook( aEvent );
644}
645
646
648{
649 int row = aEvent.GetRow();
650 int col = aEvent.GetCol();
651
652 if( !m_footprint )
653 return;
654
655 // row -> pad mapping is current order of Pads() iteration; rebuild each time (pads count small)
656 int idx = 0;
657 PAD* target = nullptr;
658
659 for( PAD* pad : m_footprint->Pads() )
660 {
661 if( idx == row )
662 {
663 target = pad;
664 break;
665 }
666
667 ++idx;
668 }
669
670 if( !target )
671 return;
672
673 bool needCanvasRefresh = false;
674
675 switch( col )
676 {
677 case COL_NUMBER:
678 target->SetNumber( m_grid->GetCellValue( row, col ) );
679 needCanvasRefresh = true;
680 m_summaryDirty = true;
681 break;
682
683 case COL_TYPE:
684 {
685 wxString typeStr = m_grid->GetCellValue( row, col );
686 PAD_ATTRIB newAttr = target->GetAttribute();
687
688 if( typeStr == _( "Through-hole" ) )
689 newAttr = PAD_ATTRIB::PTH;
690 else if( typeStr == _( "SMD" ) )
691 newAttr = PAD_ATTRIB::SMD;
692 else if( typeStr == _( "Connector" ) )
693 newAttr = PAD_ATTRIB::CONN;
694 else if( typeStr == _( "NPTH" ) )
695 newAttr = PAD_ATTRIB::NPTH;
696
697 if( newAttr != target->GetAttribute() )
698 {
699 target->SetAttribute( newAttr );
700
701 // Toggle drill columns read-only state dynamically.
702 bool drillsEditable = ( newAttr == PAD_ATTRIB::PTH || newAttr == PAD_ATTRIB::NPTH );
703 m_grid->SetReadOnly( row, COL_DRILL_X, !drillsEditable );
704 m_grid->SetReadOnly( row, COL_DRILL_Y, !drillsEditable );
705 needCanvasRefresh = true;
706 }
707
708 break;
709 }
710
711 case COL_SHAPE:
712 target->SetShape( PADSTACK::ALL_LAYERS, ShapeFromString( m_grid->GetCellValue( row, col ) ) );
713 needCanvasRefresh = true;
714 break;
715
716 case COL_POS_X:
717 case COL_POS_Y:
718 {
719 VECTOR2I pos = target->GetPosition();
720
721 if( col == COL_POS_X )
722 pos.x = m_grid->GetUnitValue( row, col );
723 else
724 pos.y = m_grid->GetUnitValue( row, col );
725
726 target->SetPosition( pos );
727 needCanvasRefresh = true;
728 break;
729 }
730
731 case COL_SIZE_X:
732 case COL_SIZE_Y:
733 {
734 VECTOR2I size = target->GetSize( PADSTACK::ALL_LAYERS );
735
736 if( col == COL_SIZE_X )
737 size.x = m_grid->GetUnitValue( row, col );
738 else
739 size.y = m_grid->GetUnitValue( row, col );
740
741 target->SetSize( PADSTACK::ALL_LAYERS, size );
742 needCanvasRefresh = true;
743 break;
744 }
745
746 case COL_DRILL_X:
747 case COL_DRILL_Y:
748 {
749 if( target->GetAttribute() == PAD_ATTRIB::PTH || target->GetAttribute() == PAD_ATTRIB::NPTH )
750 {
751 int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
752 int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
753
754 if( dx > 0 || dy > 0 )
755 {
756 if( dx <= 0 )
757 dx = dy;
758
759 if( dy <= 0 )
760 dy = dx;
761
762 target->SetDrillSize( { dx, dy } );
763 needCanvasRefresh = true;
764 }
765 }
766
767 break;
768 }
769
770 case COL_P2D_LENGTH:
771 if( !m_grid->GetCellValue( row, col ).IsEmpty() )
772 target->SetPadToDieLength( m_grid->GetUnitValue( row, col ) );
773
774 break;
775
776 case COL_P2D_DELAY:
777 {
778 wxString d = m_grid->GetCellValue( row, col );
779 long val;
780
781 if( d.ToLong( &val ) )
782 target->SetPadToDieDelay( (int) val );
783
784 break;
785 }
786
787 default:
788 break;
789 }
790
791 // Request redraw (simple approach)
792 target->SetDirty();
793
794 if( needCanvasRefresh )
795 {
796 if( PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() ) )
797 base->GetCanvas()->ForceRefresh();
798 }
799}
800
801
802void DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell( wxGridEvent& aEvent )
803{
804 int row = aEvent.GetRow();
805
806 if( !m_footprint )
807 return;
808
809 PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
810 PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
811
812 // Clear existing pad selections
813 for( PAD* pad : m_footprint->Pads() )
814 {
815 if( pad->IsBrightened() )
816 {
817 pad->ClearBrightened();
818
819 if( canvas )
820 canvas->GetView()->Update( pad, KIGFX::REPAINT );
821 }
822 }
823
824 int idx = 0;
825
826 for( PAD* pad : m_footprint->Pads() )
827 {
828 if( idx == row )
829 {
830 pad->SetBrightened();
831
832 if( canvas )
833 {
834 canvas->GetView()->Update( pad, KIGFX::REPAINT );
835 canvas->ForceRefresh();
836 }
837
838 break;
839 }
840
841 ++idx;
842 }
843}
844
845
846void DIALOG_FP_EDIT_PAD_TABLE::OnUpdateUI( wxUpdateUIEvent& aEvent )
847{
848 if( m_summaryDirty )
849 {
850 if( m_grid->IsCellEditControlShown() && m_grid->GetGridCursorCol() == COL_NUMBER )
851 {
852 int row = m_grid->GetGridCursorRow();
853 int col = m_grid->GetGridCursorCol();
854 int idx = 0;
855 PAD* target = nullptr;
856
857 for( PAD* pad : m_footprint->Pads() )
858 {
859 if( idx == row )
860 {
861 target = pad;
862 break;
863 }
864
865 ++idx;
866 }
867
868 if( target )
869 target->SetNumber( m_grid->GetCellEditor( row, col )->GetValue() );
870 }
871
873 m_summaryDirty = false;
874 }
875}
876
877
879{
880 PIN_NUMBERS pinNumbers;
881
882 for( PAD* pad : m_footprint->Pads() )
883 {
884 if( pad->GetNumber().Length() )
885 pinNumbers.insert( pad->GetNumber() );
886 }
887
888 m_pin_numbers_summary->SetLabel( pinNumbers.GetSummary() );
889 m_pin_count->SetLabel( wxString::Format( wxT( "%u" ), (unsigned) m_footprint->Pads().size() ) );
890 m_duplicate_pins->SetLabel( pinNumbers.GetDuplicates() );
891
892 Layout();
893}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
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:106
DIALOG_FP_EDIT_PAD_TABLE(PCB_BASE_FRAME *aParent, FOOTPRINT *aFootprint)
void OnCharHook(wxKeyEvent &aEvent) override
void OnUpdateUI(wxUpdateUIEvent &aEvent)
std::vector< PAD_SNAPSHOT > m_originalPads
void OnSize(wxSizeEvent &aEvent)
void OnCellChanged(wxGridEvent &aEvent)
std::vector< double > m_colProportions
std::unique_ptr< UNITS_PROVIDER > m_unitsProvider
void OnSelectCell(wxGridEvent &aEvent)
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
DIALOG_SHIM(wxWindow *aParent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER, const wxString &name=wxDialogNameStr)
virtual void OnCharHook(wxKeyEvent &aEvt)
EDA_UNITS GetUserUnits() const
void ForceRefresh()
Force a redraw.
This class works around a bug in wxGrid where the first keystroke doesn't get sent through the valida...
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:91
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:639
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
void SetAttribute(PAD_ATTRIB aAttribute)
Definition pad.cpp:1316
static wxString ShowPadShape(PAD_SHAPE aShape)
Definition pad.cpp:2046
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition pad.h:187
VECTOR2I GetPosition() const override
Definition pad.h:209
void SetDirty()
Definition pad.h:552
void SetPadToDieDelay(int aDelay)
Definition pad.h:578
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
void SetPosition(const VECTOR2I &aPos) override
Definition pad.h:203
void SetDrillSize(const VECTOR2I &aSize)
Definition pad.h:316
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:259
void SetPadToDieLength(int aLength)
Definition pad.h:575
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
wxString GetDuplicates() const
Gets a formatted string of all the pins that have duplicate numbers.
void insert(value_type const &v)
Definition pin_numbers.h:62
wxString GetSummary() const
static PAD_SHAPE ShapeFromString(const wxString &shape)
#define _(s)
static std::map< int, wxString > shapeNames
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:58
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:39
STL namespace.
PAD_ATTRIB
The set of pad shapes, used with PAD::{Set,Get}Attribute().
Definition padstack.h:97
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:100
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:52
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
#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