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