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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <wx/display.h>
23#include <wx/dcclient.h>
24#include <pcb_shape.h>
25#include <widgets/wx_grid.h>
28#include <base_units.h>
29#include <units_provider.h>
30#include <board.h>
31#include <footprint.h>
33#include <grid_tricks.h>
34#include <pin_numbers.h>
35#include <board_commit.h>
36
37
38// Helper to map shape string to PAD_SHAPE
39static PAD_SHAPE ShapeFromString( const wxString& shape )
40{
41 if( shape == _( "Oval" ) ) return PAD_SHAPE::OVAL;
42 if( shape == _( "Rectangle" ) ) return PAD_SHAPE::RECTANGLE;
43 if( shape == _( "Trapezoid" ) ) return PAD_SHAPE::TRAPEZOID;
44 if( shape == _( "Rounded rectangle" ) ) return PAD_SHAPE::ROUNDRECT;
45 if( shape == _( "Chamfered rectangle" ) ) return PAD_SHAPE::CHAMFERED_RECT;
46 if( shape == _( "Custom shape" ) ) return PAD_SHAPE::CUSTOM;
47
48 return PAD_SHAPE::CIRCLE;
49}
50
51
53 DIALOG_FP_EDIT_PAD_TABLE_BASE( (wxWindow*) aParent ),
54 m_frame( aParent ),
55 m_footprint( aFootprint ),
57 m_summaryDirty( true )
58{
60
61 // The base class created a single placeholder row; resize the grid to fit the pads.
62 if( m_grid->GetNumberRows() > 0 )
63 m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
64
65 if( !m_originalPads.empty() )
66 m_grid->AppendRows( m_originalPads.size() );
67
68 // Constrain summary label widths so they ellipsize rather than push the layout around
69 // when long pin-number summaries (or duplicate lists) are produced.
70 const int summaryW = m_pin_numbers_summary->GetCharWidth() * 30;
71
72 m_pin_numbers_summary->SetMinSize( wxSize( summaryW, -1 ) );
73 m_pin_numbers_summary->SetMaxSize( wxSize( summaryW, -1 ) );
74
75 m_duplicate_pins->SetMinSize( wxSize( summaryW, -1 ) );
76 m_duplicate_pins->SetMaxSize( wxSize( summaryW, -1 ) );
77
78 wxGridCellAttr* attr;
79
80 // Type column editor (attribute)
81 attr = new wxGridCellAttr;
82 wxArrayString typeNames;
83 typeNames.push_back( _( "Through-hole" ) ); // PTH
84 typeNames.push_back( _( "SMD" ) ); // SMD
85 typeNames.push_back( _( "Connector" ) ); // CONN SMD? (use CONN?)
86 typeNames.push_back( _( "NPTH" ) ); // NPTH
87 typeNames.push_back( _( "Aperture" ) ); // inferred copper-less
88 attr->SetEditor( new GRID_CELL_COMBOBOX( typeNames ) );
89 m_grid->SetColAttr( COL_TYPE, attr );
90
91 attr = new wxGridCellAttr;
92 wxArrayString shapeNames;
100 attr->SetEditor( new GRID_CELL_COMBOBOX( shapeNames ) );
101 m_grid->SetColAttr( COL_SHAPE, attr );
102
103 attr = new wxGridCellAttr;
104 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
105 m_grid->SetColAttr( COL_POS_X, attr );
106
107 attr = new wxGridCellAttr;
108 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
109 m_grid->SetColAttr( COL_POS_Y, attr );
110
111 attr = new wxGridCellAttr;
112 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
113 m_grid->SetColAttr( COL_SIZE_X, attr );
114
115 attr = new wxGridCellAttr;
116 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
117 m_grid->SetColAttr( COL_SIZE_Y, attr );
118
119 // Drill X
120 attr = new wxGridCellAttr;
121 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
122 m_grid->SetColAttr( COL_DRILL_X, attr );
123
124 // Drill Y
125 attr = new wxGridCellAttr;
126 attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
127 m_grid->SetColAttr( COL_DRILL_Y, attr );
128
129 // Pad->Die Length
130 m_grid->SetAutoEvalColUnits( COL_P2D_LENGTH, m_unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::DISTANCE ) );
131
132 // Pad->Die Delay
133 m_grid->SetAutoEvalColUnits( COL_P2D_DELAY, m_unitsProvider->GetUnitsFromType( EDA_DATA_TYPE::TIME ) );
134
135 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_X );
136 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_Y );
137 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_X );
138 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_Y );
139 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_X );
140 m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_Y );
141 m_grid->SetAutoEvalCols( { COL_POS_X, COL_POS_Y,
145 COL_P2D_DELAY } );
146
147 // add Cut, Copy, and Paste to wxGrid
148 m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
149
151
152 Layout();
154
155 // Cap the initial height so the dialog does not grow off-screen for footprints
156 // with many pads. The grid grows to fill the available space via wxEXPAND.
157 // Use the parent window to find the display since this dialog isn't shown yet.
158 int displayIdx = wxDisplay::GetFromWindow( aParent );
159
160 if( displayIdx == wxNOT_FOUND )
161 displayIdx = 0;
162
163 wxRect displayArea = wxDisplay( (unsigned int) displayIdx ).GetClientArea();
164 wxSize dlgSize = GetSize();
165 int maxH = ( displayArea.height * 4 ) / 5;
166
167 if( dlgSize.y > maxH )
168 {
169 dlgSize.y = maxH;
170 SetSize( dlgSize );
171
172 // Reset minimum height so the user can resize the capped dialog freely.
173 // The minimum width from finishDialogSettings() is still honoured.
174 wxSize minSz = GetMinSize();
175 minSz.y = -1;
176 SetMinSize( minSz );
177
178 Centre();
179 }
180}
181
182
184{
185 if( m_cancelled )
187
188 // destroy GRID_TRICKS before m_grid.
189 m_grid->PopEventHandler( true );
190}
191
192
194{
195 if( !m_footprint )
196 return false;
197
198 int row = 0;
199
200 for( const auto pad : m_originalPads | std::views::keys )
201 {
202 if( row >= m_grid->GetNumberRows() )
203 continue;
204
205 m_grid->SetCellValue( row, COL_NUMBER, pad->GetNumber() );
206
207 // Pad attribute to string
208 wxString attrStr;
209
210 switch( pad->GetAttribute() )
211 {
212 case PAD_ATTRIB::PTH: attrStr = _( "Through-hole" ); break;
213 case PAD_ATTRIB::SMD: attrStr = _( "SMD" ); break;
214 case PAD_ATTRIB::CONN: attrStr = _( "Connector" ); break;
215 case PAD_ATTRIB::NPTH: attrStr = _( "NPTH" ); break;
216 default: attrStr = _( "Through-hole" ); break;
217 }
218
219 int size_x = pad->GetSize( PADSTACK::ALL_LAYERS ).x;
220 int size_y = pad->GetSize( PADSTACK::ALL_LAYERS ).y;
221 wxString padShape = pad->ShowPadShape( PADSTACK::ALL_LAYERS );
222
223 pad->Padstack().ForEachUniqueLayer(
224 [&]( PCB_LAYER_ID aLayer )
225 {
226 if( pad->GetSize( aLayer ).x != size_x )
227 size_x = -1;
228
229 if( pad->GetSize( aLayer ).y != size_y )
230 size_y = -1;
231
232 if( pad->ShowPadShape( aLayer ) != padShape )
233 padShape = INDETERMINATE_STATE;
234 } );
235
236 if( pad->IsAperturePad() )
237 attrStr = _( "Aperture" );
238
239 m_grid->SetCellValue( row, COL_TYPE, attrStr );
240 m_grid->SetCellValue( row, COL_SHAPE, padShape );
241 m_grid->SetCellValue( row, COL_POS_X, m_unitsProvider->StringFromValue( pad->GetPosition().x, true ) );
242 m_grid->SetCellValue( row, COL_POS_Y, m_unitsProvider->StringFromValue( pad->GetPosition().y, true ) );
243 m_grid->SetCellValue( row, COL_SIZE_X, size_x >= 0 ? m_unitsProvider->StringFromValue( size_x, true )
245 m_grid->SetCellValue( row, COL_SIZE_Y, size_y >= 0 ? m_unitsProvider->StringFromValue( size_y, true )
247
248 // Drill values (only meaningful for PTH or NPTH). Leave empty otherwise.
249 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
250 {
251 VECTOR2I drill = pad->GetDrillSize();
252
253 if( drill.x > 0 )
254 m_grid->SetCellValue( row, COL_DRILL_X, m_unitsProvider->StringFromValue( drill.x, true ) );
255
256 if( drill.y > 0 )
257 m_grid->SetCellValue( row, COL_DRILL_Y, m_unitsProvider->StringFromValue( drill.y, true ) );
258 }
259 else
260 {
261 // For non-PTH pads, drill columns are not applicable.
262 m_grid->SetReadOnly( row, COL_DRILL_X, true );
263 m_grid->SetReadOnly( row, COL_DRILL_Y, true );
264 }
265
266 // Pad to die metrics
267 if( pad->GetPadToDieLength() )
268 m_grid->SetUnitValue( row, COL_P2D_LENGTH, pad->GetPadToDieLength() );
269
270 if( pad->GetPadToDieDelay() )
271 m_grid->SetUnitValue( row, COL_P2D_DELAY, pad->GetPadToDieDelay() );
272
274
275 row++;
276 }
277
278 // Auto size the data columns first to get reasonable initial widths
279 m_grid->AutoSizeColumns();
280
281 // Ensure the Shape column (index 1) is wide enough for the longest translated
282 // shape text plus the dropdown arrow / padding. We compute a max text width
283 // using a device context and add a platform neutral padding.
284 {
285 wxClientDC dc( m_grid );
286 dc.SetFont( m_grid->GetFont() );
287
288 wxArrayString shapeNames;
296
297 int maxWidth = 0;
298
299 for( const wxString& str : shapeNames )
300 {
301 int w, h;
302 dc.GetTextExtent( str, &w, &h );
303 maxWidth = std::max( maxWidth, w );
304 }
305
306 // Add padding for internal cell margins + dropdown control.
307 int padding = FromDIP( 30 ); // heuristic: 2*margin + arrow button
308 m_grid->SetColSize( COL_SHAPE, maxWidth + padding );
309 }
310
311 // Record initial proportions for proportional resizing later.
313
314 // Run an initial proportional resize using current client size so columns
315 // respect proportions immediately.
316 wxSizeEvent sizeEvt( GetSize(), GetId() );
317 CallAfter(
318 [this, sizeEvt]
319 {
320 wxSizeEvent evt( sizeEvt );
321 this->OnSize( evt );
322 } );
323
324 // If pads exist, select the first row to show initial highlight
325 if( m_grid->GetNumberRows() > 0 )
326 {
327 m_grid->SetGridCursor( 0, 0 );
328
329 // Construct event with required parameters (id, type, obj, row, col,...)
330 wxGridEvent ev( m_grid->GetId(), wxEVT_GRID_SELECT_CELL, m_grid, 0, 0, -1, -1, true );
331 OnSelectCell( ev );
332 }
333
334 return true;
335}
336
337
339{
340 // Set nullable editors
341 auto setCellEditor =
342 [this, aRowId]( int aCol )
343 {
345 wxGridCellAttr* attr = m_grid->GetOrCreateCellAttr( aRowId, aCol );
346 attr->SetEditor( cellEditor );
347 attr->DecRef();
348 };
349
350 setCellEditor( COL_P2D_LENGTH );
351 setCellEditor( COL_P2D_DELAY );
352}
353
354
356{
357 m_originalPads.clear();
358
359 if( !m_footprint )
360 return;
361
362 for( PAD* pad : m_footprint->Pads() )
363 {
364 PAD_SNAPSHOT snap( pad );
365 snap.number = pad->GetNumber();
366 snap.position = pad->GetPosition();
367 snap.padstack = pad->Padstack();
368 snap.attribute = pad->GetAttribute();
369 snap.padToDieLength= pad->GetPadToDieLength();
370 snap.padToDieDelay = pad->GetPadToDieDelay();
371
372 m_originalPads.try_emplace( pad, std::move( snap ) );
373 }
374}
375
376
378{
379 if( !m_footprint )
380 return;
381
382 const PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
383 PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
384
385 for( PAD* pad : m_footprint->Pads() )
386 {
387 if( !m_originalPads.contains( pad ) )
388 continue;
389
390 const PAD_SNAPSHOT& snap = m_originalPads.at( pad );
391 pad->SetNumber( snap.number );
392 pad->SetPosition( snap.position );
393 pad->SetPadstack( snap.padstack );
394 pad->SetAttribute( snap.attribute );
395 pad->SetPadToDieLength( snap.padToDieLength );
396 pad->SetPadToDieDelay( snap.padToDieDelay );
397 pad->ClearBrightened();
398
399 if( canvas )
400 canvas->GetView()->Update( pad, KIGFX::REPAINT );
401 }
402
403 if( canvas )
404 {
406 canvas->ForceRefresh();
407 }
408
409 m_summaryDirty = true;
410}
411
412
414{
415 if( !m_grid->CommitPendingChanges() )
416 return false;
417
418 if( !m_footprint )
419 return true;
420
422 BOARD_COMMIT commit( m_frame );
423
424 int row = 0;
425
426 for( PAD* pad : m_originalPads | std::views::keys )
427 {
428 commit.Modify( pad );
429 pad->SetNumber( m_grid->GetCellValue( row, COL_NUMBER ) );
430
431 wxString typeStr = m_grid->GetCellValue( row, COL_TYPE );
432
433 if( typeStr == _( "Through-hole" ) )
434 pad->SetAttribute( PAD_ATTRIB::PTH );
435 else if( typeStr == _( "SMD" ) )
436 pad->SetAttribute( PAD_ATTRIB::SMD );
437 else if( typeStr == _( "Connector" ) )
438 pad->SetAttribute( PAD_ATTRIB::CONN );
439 else if( typeStr == _( "NPTH" ) )
440 pad->SetAttribute( PAD_ATTRIB::NPTH );
441 // Aperture derived by copper-less layers; do not overwrite attribute here.
442
443 wxString shape = m_grid->GetCellValue( row, COL_SHAPE );
444
445 if( shape != INDETERMINATE_STATE )
446 {
447 PAD_SHAPE newShape = ShapeFromString( shape );
448
449 pad->Padstack().ForEachUniqueLayer(
450 [&]( PCB_LAYER_ID aLayer )
451 {
452 pad->SetShape( aLayer, newShape );
453 } );
454 }
455
456 VECTOR2I pos;
457 pos.x = m_grid->GetUnitValue( row, COL_POS_X );
458 pos.y = m_grid->GetUnitValue( row, COL_POS_Y );
459 pad->SetPosition( pos );
460
461 wxString size_x_value = m_grid->GetCellValue( row, COL_SIZE_X );
462
463 if( size_x_value != INDETERMINATE_STATE )
464 {
465 int size_x = m_grid->GetUnitValue( row, COL_SIZE_X );
466
467 pad->Padstack().ForEachUniqueLayer(
468 [&]( PCB_LAYER_ID aLayer )
469 {
470 VECTOR2I size( size_x, pad->GetSize( aLayer ).y );
471 pad->SetSize( aLayer, size );
472 } );
473 }
474
475 wxString size_y_value = m_grid->GetCellValue( row, COL_SIZE_Y );
476
477 if( size_y_value != INDETERMINATE_STATE )
478 {
479 int size_y = m_grid->GetUnitValue( row, COL_SIZE_Y );
480
481 pad->Padstack().ForEachUniqueLayer(
482 [&]( PCB_LAYER_ID aLayer )
483 {
484 VECTOR2I size( pad->GetSize( aLayer ).x, size_y );
485 pad->SetSize( aLayer, size );
486 } );
487 }
488
489 // Drill sizes (only if attribute allows)
490 if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
491 {
492 int drill_x = m_grid->GetUnitValue( row, COL_DRILL_X );
493 int drill_y = m_grid->GetUnitValue( row, COL_DRILL_Y );
494
495 if( drill_x > 0 || drill_y > 0 )
496 {
497 if( drill_x <= 0 )
498 drill_x = drill_y;
499
500 if( drill_y <= 0 )
501 drill_y = drill_x;
502
503 pad->SetDrillSize( { drill_x, drill_y } );
504 }
505 }
506
507 // Pad->Die
508 const wxString delayStr = m_grid->GetCellValue( row, COL_P2D_DELAY );
509 const wxString lenStr = m_grid->GetCellValue( row, COL_P2D_LENGTH );
510
511 if( !lenStr.IsEmpty() )
512 pad->SetPadToDieLength( m_grid->GetUnitValue( row, COL_P2D_LENGTH ) );
513 else
514 pad->SetPadToDieLength( 0 );
515
516 if( !delayStr.IsEmpty() )
517 pad->SetPadToDieDelay( m_grid->GetUnitValue( row, COL_P2D_DELAY ) );
518 else
519 pad->SetPadToDieDelay( 0 );
520
521 row++;
522 }
523
524 commit.Push( _( "Edit Pads" ) );
525 m_frame->Refresh();
526
527 return true;
528}
529
530
532{
533 m_colProportions.clear();
534 m_minColWidths.clear();
535
536 if( !m_grid )
537 return;
538
539 // Only consider the actual data columns (all of them since row labels are hidden)
540 int cols = m_grid->GetNumberCols();
541 int total = 0;
542 std::vector<int> widths;
543 widths.reserve( cols );
544
545 for( int c = 0; c < cols; ++c )
546 {
547 int w = m_grid->GetColSize( c );
548 widths.push_back( w );
549 total += w;
550 }
551
552 if( total <= 0 )
553 return;
554
555 for( int w : widths )
556 {
557 m_colProportions.push_back( (double) w / (double) total );
558 m_minColWidths.push_back( w );
559 }
560}
561
562
563void DIALOG_FP_EDIT_PAD_TABLE::OnSize( wxSizeEvent& aEvent )
564{
565 if( m_colProportions.empty() )
566 {
567 aEvent.Skip();
568 return;
569 }
570
571 // Compute available total width for columns and resize keeping proportions.
572 int cols = m_grid->GetNumberCols();
573 int available = 0;
574
575 for( int c = 0; c < cols; ++c )
576 available += m_grid->GetColSize( c );
577
578 // Use client size of grid minus scrollbar estimate to better distribute.
579 int clientW = m_grid->GetClientSize().x;
580
581 if( clientW > 0 )
582 available = clientW; // prefer actual client width
583
584 int used = 0;
585
586 for( int c = 0; c < cols; ++c )
587 {
588 int target = (int) std::round( m_colProportions[c] * available );
589 target = std::max( target, m_minColWidths[c] );
590
591 // Defer last column to absorb rounding diff.
592 if( c == cols - 1 )
593 target = std::max( available - used, m_minColWidths[c] );
594
595 m_grid->SetColSize( c, target );
596 used += target;
597 }
598
599 aEvent.Skip();
600}
601
602
603void DIALOG_FP_EDIT_PAD_TABLE::OnCharHook( wxKeyEvent& aEvent )
604{
605 if( m_grid->IsCellEditControlShown() && m_grid->GetGridCursorCol() == COL_NUMBER )
606 m_summaryDirty = true;
607
608 DIALOG_SHIM::OnCharHook( aEvent );
609}
610
611
613{
614 int row = aEvent.GetRow();
615 int col = aEvent.GetCol();
616
617 if( !m_footprint )
618 return;
619
620 PAD* target = getPadForRow( row );
621
622 if( !target )
623 return;
624
625 bool needCanvasRefresh = false;
626
627 switch( col )
628 {
629 case COL_NUMBER:
630 target->SetNumber( m_grid->GetCellValue( row, col ) );
631 needCanvasRefresh = true;
632 m_summaryDirty = true;
633 break;
634
635 case COL_TYPE:
636 {
637 wxString typeStr = m_grid->GetCellValue( row, col );
638 PAD_ATTRIB newAttr = target->GetAttribute();
639
640 if( typeStr == _( "Through-hole" ) )
641 newAttr = PAD_ATTRIB::PTH;
642 else if( typeStr == _( "SMD" ) )
643 newAttr = PAD_ATTRIB::SMD;
644 else if( typeStr == _( "Connector" ) )
645 newAttr = PAD_ATTRIB::CONN;
646 else if( typeStr == _( "NPTH" ) )
647 newAttr = PAD_ATTRIB::NPTH;
648
649 if( newAttr != target->GetAttribute() )
650 {
651 target->SetAttribute( newAttr );
652
653 // Toggle drill columns read-only state dynamically.
654 bool drillsEditable = ( newAttr == PAD_ATTRIB::PTH || newAttr == PAD_ATTRIB::NPTH );
655 m_grid->SetReadOnly( row, COL_DRILL_X, !drillsEditable );
656 m_grid->SetReadOnly( row, COL_DRILL_Y, !drillsEditable );
657 needCanvasRefresh = true;
658 }
659
660 break;
661 }
662
663 case COL_SHAPE:
664 target->SetShape( PADSTACK::ALL_LAYERS, ShapeFromString( m_grid->GetCellValue( row, col ) ) );
665 needCanvasRefresh = true;
666 break;
667
668 case COL_POS_X:
669 case COL_POS_Y:
670 {
671 VECTOR2I pos = target->GetPosition();
672
673 if( col == COL_POS_X )
674 pos.x = m_grid->GetUnitValue( row, col );
675 else
676 pos.y = m_grid->GetUnitValue( row, col );
677
678 target->SetPosition( pos );
679 needCanvasRefresh = true;
680 break;
681 }
682
683 case COL_SIZE_X:
684 case COL_SIZE_Y:
685 {
686 VECTOR2I size = target->GetSize( PADSTACK::ALL_LAYERS );
687
688 if( col == COL_SIZE_X )
689 size.x = m_grid->GetUnitValue( row, col );
690 else
691 size.y = m_grid->GetUnitValue( row, col );
692
693 target->SetSize( PADSTACK::ALL_LAYERS, size );
694 needCanvasRefresh = true;
695 break;
696 }
697
698 case COL_DRILL_X:
699 case COL_DRILL_Y:
700 {
701 if( target->GetAttribute() == PAD_ATTRIB::PTH || target->GetAttribute() == PAD_ATTRIB::NPTH )
702 {
703 int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
704 int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
705
706 if( dx > 0 || dy > 0 )
707 {
708 if( dx <= 0 )
709 dx = dy;
710
711 if( dy <= 0 )
712 dy = dx;
713
714 target->SetDrillSize( { dx, dy } );
715 needCanvasRefresh = true;
716 }
717 }
718
719 break;
720 }
721
722 case COL_P2D_LENGTH:
723 if( !m_grid->GetCellValue( row, col ).IsEmpty() )
724 target->SetPadToDieLength( m_grid->GetUnitValue( row, col ) );
725
726 break;
727
728 case COL_P2D_DELAY:
729 if( !m_grid->GetCellValue( row, col ).IsEmpty() )
730 target->SetPadToDieDelay( m_grid->GetUnitValue( row, col ) );
731
732 break;
733
734 default:
735 break;
736 }
737
738 // Request redraw (simple approach)
739 target->SetDirty();
740
741 if( needCanvasRefresh )
742 {
743 if( PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() ) )
744 base->GetCanvas()->ForceRefresh();
745 }
746}
747
748
749void DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell( wxGridEvent& aEvent )
750{
751 int row = aEvent.GetRow();
752
753 if( !m_footprint )
754 return;
755
756 PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
757 PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
758
759 // Clear existing pad selections
760 for( PAD* pad : m_footprint->Pads() )
761 {
762 if( pad->IsBrightened() )
763 {
764 pad->ClearBrightened();
765
766 if( canvas )
767 canvas->GetView()->Update( pad, KIGFX::REPAINT );
768 }
769 }
770
771 PAD* pad = getPadForRow( row );
772
773 if( !pad )
774 return;
775
776 pad->SetBrightened();
777
778 if( canvas )
779 {
780 canvas->GetView()->Update( pad, KIGFX::REPAINT );
781 canvas->ForceRefresh();
782 }
783}
784
785
786void DIALOG_FP_EDIT_PAD_TABLE::OnUpdateUI( wxUpdateUIEvent& aEvent )
787{
788 if( m_summaryDirty )
789 {
790 if( m_grid->IsCellEditControlShown() && m_grid->GetGridCursorCol() == COL_NUMBER )
791 {
792 int row = m_grid->GetGridCursorRow();
793 int col = m_grid->GetGridCursorCol();
794
795 PAD* target = getPadForRow( row );
796
797 if( !target )
798 return;
799
800 wxGridCellEditor* editor = m_grid->GetCellEditor( row, col );
801
802 if( editor )
803 {
804 target->SetNumber( editor->GetValue() );
805 editor->DecRef();
806 }
807 }
808
810 m_summaryDirty = false;
811 }
812}
813
814
815void DIALOG_FP_EDIT_PAD_TABLE::OnCancel( wxCommandEvent& aEvent )
816{
817 m_cancelled = true;
818 aEvent.Skip();
819}
820
821
823{
824 PIN_NUMBERS pinNumbers;
825
826 for( PAD* pad : m_footprint->Pads() )
827 {
828 if( pad->GetNumber().Length() )
829 pinNumbers.insert( pad->GetNumber() );
830 }
831
832 const wxString summary = pinNumbers.GetSummary();
833 const wxString duplicates = pinNumbers.GetDuplicates();
834
835 m_pin_numbers_summary->SetLabel( summary );
836 m_pin_numbers_summary->SetToolTip( summary );
837 m_pin_count->SetLabel( wxString::Format( wxT( "%u" ), (unsigned) m_footprint->Pads().size() ) );
838 m_duplicate_pins->SetLabel( duplicates );
839 m_duplicate_pins->SetToolTip( duplicates );
840
841 Layout();
842}
843
844
846{
847 if( static_cast<size_t>( aRowId ) >= m_originalPads.size() )
848 return nullptr;
849
850 const auto targetItr = std::next( m_originalPads.begin(), aRowId );
851 return targetItr->first;
852}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
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_FP_EDIT_PAD_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Pad Table"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
PAD * getPadForRow(int aRowId) const
void OnCellChanged(wxGridEvent &aEvent) override
DIALOG_FP_EDIT_PAD_TABLE(PCB_BASE_FRAME *aParent, FOOTPRINT *aFootprint)
void OnCharHook(wxKeyEvent &aEvent) override
void OnSelectCell(wxGridEvent &aEvent) override
std::map< PAD *, PAD_SNAPSHOT, PAD_SNAPSHOT_COMPARE > m_originalPads
void setRowNullableEditors(int aRowId) const
void OnSize(wxSizeEvent &aEvent) override
void OnCancel(wxCommandEvent &aEvent) override
std::vector< double > m_colProportions
std::unique_ptr< UNITS_PROVIDER > m_unitsProvider
void OnUpdateUI(wxUpdateUIEvent &aEvent) override
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...
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:57
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:87
void MarkTargetDirty(int aTarget)
Set or clear target 'dirty' flag.
Definition view.h:657
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:61
void SetAttribute(PAD_ATTRIB aAttribute)
Definition pad.cpp:1615
static wxString ShowPadShape(PAD_SHAPE aShape)
Definition pad.cpp:2500
PAD_ATTRIB GetAttribute() const
Definition pad.h:555
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition pad.h:193
VECTOR2I GetPosition() const override
Definition pad.cpp:245
void SetDirty()
Definition pad.h:544
void SetPadToDieDelay(int aDelay)
Definition pad.h:575
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:142
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:287
void SetPosition(const VECTOR2I &aPos) override
Definition pad.cpp:234
void SetDrillSize(const VECTOR2I &aSize)
Definition pad.h:314
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.cpp:254
void SetPadToDieLength(int aLength)
Definition pad.h:572
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:58
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:56
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:54
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
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:683