KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_lib_edit_pin_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/filedlg.h>
23#include <wx/wfstream.h>
24#include <wx/msgdlg.h>
25#include <wx/tokenzr.h>
26
27#include <column_formatter.h>
28#include <grid_tricks.h>
29#include <richio.h>
30#include <sch_pin.h>
31#include <pin_numbers.h>
32#include <reporter.h>
33#include "pgm_base.h"
34#include <base_units.h>
35#include <bitmaps.h>
36#include <clipboard.h>
37#include <confirm.h>
38#include <symbol_edit_frame.h>
40#include <io/csv.h>
41#include <kiplatform/ui.h>
44#include <widgets/wx_grid.h>
49#include <tool/action_menu.h>
50#include <tool/tool_manager.h>
51#include <table_io.h>
53#include <string_utils.h>
54
55
61static wxString GetPinTableColLabel( int aCol )
62{
63 switch( aCol )
64 {
65 case COL_PIN_COUNT: return _HKI( "Count" );
66 case COL_NUMBER: return _HKI( "Number" );
67 case COL_NAME: return _HKI( "Name" );
68 case COL_TYPE: return _HKI( "Electrical Type" );
69 case COL_SHAPE: return _HKI( "Graphic Style" );
70 case COL_ORIENTATION: return _HKI( "Orientation" );
71 case COL_NUMBER_SIZE: return _HKI( "Number Text Size" );
72 case COL_NAME_SIZE: return _HKI( "Name Text Size" );
73 case COL_LENGTH: return _HKI( "Length" );
74 case COL_POSX: return _HKI( "X Position" );
75 case COL_POSY: return _HKI( "Y Position" );
76 case COL_VISIBLE: return _HKI( "Visible" );
77 case COL_UNIT: return _HKI( "Unit" );
78 case COL_BODY_STYLE: return _HKI( "Body Style" );
79 default: wxFAIL; return wxEmptyString;
80 }
81}
82
83
84static COL_ORDER GetColTypeForString( const wxString& aStr )
85{
86 for( int i = 0; i < COL_COUNT; i++ )
87 {
88 if( MatchTranslationOrNative( aStr, GetPinTableColLabel( i ), false ) )
89 return (COL_ORDER) i;
90 }
91 return COL_COUNT;
92}
93
99{
100public:
101 PIN_INFO_FORMATTER( UNITS_PROVIDER& aUnitsProvider, bool aIncludeUnits, BOOL_FORMAT aBoolFormat,
102 REPORTER& aReporter ) :
103 COLUMN_FORMATTER( aUnitsProvider, aIncludeUnits, aBoolFormat, aReporter )
104 {
105 }
106
107 wxString Format( const SCH_PIN& aPin, int aFieldId ) const
108 {
109 switch( aFieldId )
110 {
111 case COL_NAME:
112 return aPin.GetName();
113
114 case COL_NUMBER:
115 return aPin.GetNumber();
116
117 case COL_TYPE:
118 return PinTypeNames()[static_cast<int>( aPin.GetType() )];
119
120 case COL_SHAPE:
121 return PinShapeNames()[static_cast<int>( aPin.GetShape() )];
122
123 case COL_ORIENTATION:
124 {
125 const int index = PinOrientationIndex( aPin.GetOrientation() );
126
127 if( index >= 0)
128 return PinOrientationNames()[ index ];
129
130 return wxEmptyString;
131 }
132
133 case COL_NUMBER_SIZE:
134 return m_unitsProvider.StringFromValue( aPin.GetNumberTextSize(), m_includeUnits );
135
136 case COL_NAME_SIZE:
137 return m_unitsProvider.StringFromValue( aPin.GetNameTextSize(), m_includeUnits );
138
139 case COL_LENGTH:
140 return m_unitsProvider.StringFromValue( aPin.GetLength(), m_includeUnits );
141
142 case COL_POSX:
143 return m_unitsProvider.StringFromValue( aPin.GetPosition().x, m_includeUnits );
144
145 case COL_POSY:
146 return m_unitsProvider.StringFromValue( aPin.GetPosition().y, m_includeUnits );
147
148 case COL_VISIBLE:
149 return stringFromBool( aPin.IsVisible() );
150
151 case COL_UNIT:
152 if( const SYMBOL* parent = aPin.GetParentSymbol() )
153 {
154 if( !parent->IsMultiUnit() )
155 return wxGetTranslation( UNITS_ALL );
156 }
157
158 if( aPin.GetUnit() == ALL_UNITS )
159 return wxGetTranslation( UNITS_ALL );
160 else
161 return aPin.GetUnitDisplayName( aPin.GetUnit(), true );
162
163 case COL_BODY_STYLE:
164 if( const SYMBOL* parent = aPin.GetParentSymbol() )
165 {
166 if( !parent->IsMultiBodyStyle() )
167 return wxGetTranslation( UNITS_ALL );
168 }
169
170 if( aPin.GetBodyStyle() == ALL_BODY_STYLES )
171 return wxGetTranslation( DEMORGAN_ALL );
172 else
173 return aPin.GetBodyStyleDescription( aPin.GetBodyStyle(), true );
174
175 default:
176 wxFAIL_MSG( wxString::Format( "Invalid field id %d", aFieldId ) );
177 return wxEmptyString;
178 }
179 }
180
187 void UpdatePin( SCH_PIN& aPin, const wxString& aValue, int aFieldId, const LIB_SYMBOL& aSymbol ) const
188 {
189 switch( aFieldId )
190 {
191 case COL_NUMBER:
192 aPin.SetNumber( aValue );
193 break;
194
195 case COL_NAME:
196 aPin.SetName( aValue );
197 break;
198
199 case COL_TYPE:
200 if( PinTypeNames().Index( aValue, false ) != wxNOT_FOUND )
201 aPin.SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( aValue, false ) );
202
203 break;
204
205 case COL_SHAPE:
206 if( PinShapeNames().Index( aValue, false ) != wxNOT_FOUND )
207 aPin.SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( aValue, false ) );
208
209 break;
210
211 case COL_ORIENTATION:
212 if( PinOrientationNames().Index( aValue, false ) != wxNOT_FOUND )
213 aPin.SetOrientation( (PIN_ORIENTATION) PinOrientationNames().Index( aValue, false ) );
214
215 break;
216
217 case COL_NUMBER_SIZE:
218 aPin.SetNumberTextSize( m_unitsProvider.ValueFromString( aValue ) );
219 break;
220
221 case COL_NAME_SIZE:
222 aPin.SetNameTextSize( m_unitsProvider.ValueFromString( aValue ) );
223 break;
224
225 case COL_LENGTH:
226 aPin.ChangeLength( m_unitsProvider.ValueFromString( aValue ) );
227 break;
228
229 case COL_POSX:
230 aPin.SetPosition( VECTOR2I( m_unitsProvider.ValueFromString( aValue ), aPin.GetPosition().y ) );
231 break;
232
233 case COL_POSY:
234 aPin.SetPosition( VECTOR2I( aPin.GetPosition().x, m_unitsProvider.ValueFromString( aValue ) ) );
235 break;
236
237 case COL_VISIBLE:
238 aPin.SetVisible(boolFromString( aValue, m_reporter ) );
239 break;
240
241 case COL_UNIT:
242 if( const SYMBOL* parent = aPin.GetParentSymbol() )
243 {
244 if( !parent->IsMultiUnit() )
245 break;
246 }
247
248 if( MatchTranslationOrNative( aValue, UNITS_ALL, false ) )
249 {
250 aPin.SetUnit( 0 );
251 break;
252 }
253
254 for( int i = 1; i <= aSymbol.GetUnitCount(); i++ )
255 {
256 if( aValue == aPin.GetUnitDisplayName( i, true )
257 || aValue == aPin.GetUnitDisplayName( i, false ) )
258 {
259 aPin.SetUnit( i );
260 break;
261 }
262 }
263
264 break;
265
266 case COL_BODY_STYLE:
267 if( const SYMBOL* parent = aPin.GetParentSymbol() )
268 {
269 if( !parent->IsMultiBodyStyle() )
270 break;
271 }
272
273 if( MatchTranslationOrNative( aValue, DEMORGAN_ALL, false ) )
274 {
275 aPin.SetBodyStyle( 0 );
276 break;
277 }
278
279 for( int i = 1; i <= aSymbol.GetBodyStyleCount(); i++ )
280 {
281 if( aValue == aPin.GetBodyStyleDescription( i, true )
282 || aValue == aPin.GetBodyStyleDescription( i, false ) )
283 {
284 aPin.SetBodyStyle( i );
285 break;
286 }
287 }
288
289 break;
290
291 default:
292 wxFAIL_MSG( wxString::Format( "Invalid field id %d", aFieldId ) );
293 break;
294 }
295 }
296};
297
298
299void getSelectedArea( WX_GRID* aGrid, int* aRowStart, int* aRowCount )
300{
301 wxGridCellCoordsArray topLeft = aGrid->GetSelectionBlockTopLeft();
302 wxGridCellCoordsArray botRight = aGrid->GetSelectionBlockBottomRight();
303
304 wxArrayInt cols = aGrid->GetSelectedCols();
305 wxArrayInt rows = aGrid->GetSelectedRows();
306
307 if( topLeft.Count() && botRight.Count() )
308 {
309 *aRowStart = topLeft[0].GetRow();
310 *aRowCount = botRight[0].GetRow() - *aRowStart + 1;
311 }
312 else if( cols.Count() )
313 {
314 *aRowStart = 0;
315 *aRowCount = aGrid->GetNumberRows();
316 }
317 else if( rows.Count() )
318 {
319 *aRowStart = rows[0];
320 *aRowCount = rows.Count();
321 }
322 else
323 {
324 *aRowStart = aGrid->GetGridCursorRow();
325 *aRowCount = *aRowStart >= 0 ? 1 : 0;
326 }
327}
328
329
331{
332public:
334 const std::vector<SCH_PIN*>& aOrigSelectedPins ) :
335 m_frame( aFrame ),
338 m_filterBySelection( false ),
339 m_edited( false ),
340 m_pinTable( aPinTable ),
341 m_symbol( aSymbol ),
342 m_origSelectedPins( aOrigSelectedPins )
343 {
344 m_eval = std::make_unique<NUMERIC_EVALUATOR>( m_frame->GetUserUnits() );
345
346 m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
347 }
348
350 {
351 m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
352 }
353
354 void onUnitsChanged( wxCommandEvent& aEvent )
355 {
356 if( GetView() )
357 GetView()->ForceRefresh();
358
359 aEvent.Skip();
360 }
361
362 void SetUnitFilter( int aFilter ) { m_unitFilter = aFilter; }
363 void SetBodyStyleFilter( int aFilter ) { m_bodyStyleFilter = aFilter; }
364 void SetFilterBySelection( bool aFilter ) { m_filterBySelection = aFilter; }
365
366 int GetNumberRows() override { return (int) m_rows.size(); }
367 int GetNumberCols() override { return COL_COUNT; }
368
369 wxString GetColLabelValue( int aCol ) override
370 {
371 return wxGetTranslation( GetPinTableColLabel( aCol ) );
372 }
373
374 bool IsEmptyCell( int row, int col ) override
375 {
376 return false; // don't allow adjacent cell overflow, even if we are actually empty
377 }
378
379 wxString GetValue( int aRow, int aCol ) override
380 {
381 wxGrid* grid = GetView();
382
383 if( grid->GetGridCursorRow() == aRow && grid->GetGridCursorCol() == aCol && grid->IsCellEditControlShown() )
384 {
385 auto it = m_evalOriginal.find( { m_rows[ aRow ], aCol } );
386
387 if( it != m_evalOriginal.end() )
388 return it->second;
389 }
390
391 return GetValue( m_rows[ aRow ], aCol, m_frame );
392 }
393
394 static wxString GetValue( const std::vector<SCH_PIN*>& pins, int aCol, EDA_DRAW_FRAME* aParentFrame )
395 {
396 wxString fieldValue;
397
398 if( pins.empty() )
399 return fieldValue;
400
403
404 for( const SCH_PIN* pin : pins )
405 {
406 wxString val;
407 switch( aCol )
408 {
409 case COL_PIN_COUNT:
410 val << pins.size();
411 break;
412 default:
413 val << formatter.Format( *pin, aCol );
414 break;
415 }
416
417 if( aCol == COL_NUMBER )
418 {
419 if( fieldValue.length() )
420 fieldValue += wxT( ", " );
421
422 fieldValue += val;
423 }
424 else
425 {
426 if( !fieldValue.Length() )
427 fieldValue = val;
428 else if( val != fieldValue )
429 fieldValue = INDETERMINATE_STATE;
430 }
431 }
432
433 return fieldValue;
434 }
435
436 void SetValue( int aRow, int aCol, const wxString &aValue ) override
437 {
438 if( aValue == INDETERMINATE_STATE )
439 return;
440
441 wxString value = aValue;
442
443 switch( aCol )
444 {
445 case COL_NUMBER_SIZE:
446 case COL_NAME_SIZE:
447 case COL_LENGTH:
448 case COL_POSX:
449 case COL_POSY:
450 m_eval->SetDefaultUnits( m_frame->GetUserUnits() );
451
452 if( m_eval->Process( value ) )
453 {
454 m_evalOriginal[ { m_rows[ aRow ], aCol } ] = value;
455 value = m_eval->Result();
456 }
457
458 break;
459
460 default:
461 break;
462 }
463
464 std::vector<SCH_PIN*> pins = m_rows[ aRow ];
465
466 // If the NUMBER column is edited and the pins are grouped, renumber, and add or
467 // remove pins based on the comma separated list of pins.
468 if( aCol == COL_NUMBER && m_pinTable->IsDisplayGrouped() )
469 {
470 wxStringTokenizer tokenizer( value, "," );
471 size_t i = 0;
472
473 while( tokenizer.HasMoreTokens() )
474 {
475 wxString pinName = tokenizer.GetNextToken();
476
477 // Trim whitespace from both ends of the string
478 pinName.Trim( true ).Trim( false );
479
480 if( i < pins.size() )
481 {
482 // Renumber the existing pins
483 pins.at( i )->SetNumber( pinName );
484 }
485 else
486 {
487 // Create new pins
488 SCH_PIN* newPin = new SCH_PIN( this->m_symbol );
489 SCH_PIN* last = pins.back();
490
491 newPin->SetNumber( pinName );
492 newPin->SetName( last->GetName() );
493 newPin->SetOrientation( last->GetOrientation() );
494 newPin->SetType( last->GetType() );
495 newPin->SetShape( last->GetShape() );
496 newPin->SetUnit( last->GetUnit() );
497
498 VECTOR2I pos = last->GetPosition();
499
501 {
504 {
505 pos.y -= schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
506 }
507 else
508 {
509 pos.x += schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
510 }
511 }
512
513 newPin->SetPosition( pos );
514
515 pins.push_back( newPin );
516 m_pinTable->AddPin( newPin );
517 }
518
519 i++;
520 }
521
522 while( pins.size() > i )
523 {
524 m_pinTable->RemovePin( pins.back() );
525 pins.pop_back();
526 }
527
528 m_rows[aRow] = pins;
529 m_edited = true;
530
531 return;
532 }
533
536
537 for( SCH_PIN* pin : pins )
538 formatter.UpdatePin( *pin, value, aCol, *m_symbol );
539
540 m_edited = true;
541 }
542
543 static int findRow( const std::vector<std::vector<SCH_PIN*>>& aRowSet, const wxString& aName )
544 {
545 for( size_t i = 0; i < aRowSet.size(); ++i )
546 {
547 if( aRowSet[ i ][ 0 ] && aRowSet[ i ][ 0 ]->GetName() == aName )
548 return i;
549 }
550
551 return -1;
552 }
553
554 static bool compare( const std::vector<SCH_PIN*>& lhs, const std::vector<SCH_PIN*>& rhs,
555 int sortCol, bool ascending, EDA_DRAW_FRAME* parentFrame )
556 {
557 wxString lhStr = GetValue( lhs, sortCol, parentFrame );
558 wxString rhStr = GetValue( rhs, sortCol, parentFrame );
559
560 if( lhStr == rhStr )
561 {
562 // Secondary sort key is always COL_NUMBER
563 sortCol = COL_NUMBER;
564 lhStr = GetValue( lhs, sortCol, parentFrame );
565 rhStr = GetValue( rhs, sortCol, parentFrame );
566 }
567
568 bool res;
569
570 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
571 // to get the opposite sort. i.e. ~(a<b) != (a>b)
572 auto cmp =
573 [ ascending ]( const auto a, const auto b )
574 {
575 if( ascending )
576 return a < b;
577 else
578 return b < a;
579 };
580
581 switch( sortCol )
582 {
583 case COL_NUMBER:
584 case COL_NAME:
585 res = cmp( PIN_NUMBERS::Compare( lhStr, rhStr ), 0 );
586 break;
587
588 case COL_NUMBER_SIZE:
589 case COL_NAME_SIZE:
590 res = cmp( parentFrame->ValueFromString( lhStr ), parentFrame->ValueFromString( rhStr ) );
591 break;
592
593 case COL_LENGTH:
594 case COL_POSX:
595 case COL_POSY:
596 res = cmp( parentFrame->ValueFromString( lhStr ), parentFrame->ValueFromString( rhStr ) );
597 break;
598
599 case COL_VISIBLE:
600 case COL_UNIT:
601 case COL_BODY_STYLE:
602 default:
603 res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
604 break;
605 }
606
607 return res;
608 }
609
610 void RebuildRows( const std::vector<SCH_PIN*>& aPins, bool groupByName, bool groupBySelection )
611 {
612 WX_GRID* grid = dynamic_cast<WX_GRID*>( GetView() );
613 std::vector<SCH_PIN*> clear_flags;
614
615 clear_flags.reserve( aPins.size() );
616
617 if( grid )
618 {
619 if( groupBySelection )
620 {
621 for( SCH_PIN* pin : aPins )
622 pin->ClearTempFlags();
623
624 int firstSelectedRow;
625 int selectedRowCount;
626
627 getSelectedArea( grid, &firstSelectedRow, &selectedRowCount );
628
629 for( int ii = 0; ii < selectedRowCount; ++ii )
630 {
631 for( SCH_PIN* pin : m_rows[ firstSelectedRow + ii ] )
632 {
633 pin->SetFlags( CANDIDATE );
634 clear_flags.push_back( pin );
635 }
636 }
637 }
638
639 // Commit any pending in-place edits before the row gets moved out from under
640 // the editor.
641 grid->CommitPendingChanges( true );
642
643 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
644 GetView()->ProcessTableMessage( msg );
645 }
646
647 m_rows.clear();
648
649 if( groupBySelection )
650 m_rows.emplace_back( std::vector<SCH_PIN*>() );
651
652 std::set<wxString> selectedNumbers;
653
655 selectedNumbers.insert( pin->GetNumber() );
656
657 const auto pinIsInEditorSelection =
658 [&]( SCH_PIN* pin )
659 {
660 // Quick check before we iterate the whole thing in N^2 time.
661 // (3000^2 = FPGAs causing issues down the road).
662 if( selectedNumbers.count( pin->GetNumber() ) == 0 )
663 return false;
664
665 for( SCH_PIN* selectedPin : m_origSelectedPins )
666 {
667 // The selected pin is in the editor, but the pins in the table
668 // are copies. We will mark the pin as selected if it's a match
669 // on the critical items.
670 if( selectedPin->GetNumber() == pin->GetNumber()
671 && selectedPin->GetName() == pin->GetName()
672 && selectedPin->GetUnit() == pin->GetUnit()
673 && selectedPin->GetBodyStyle() == pin->GetBodyStyle() )
674 {
675 return true;
676 }
677 }
678
679 return false;
680 };
681
682 for( SCH_PIN* pin : aPins )
683 {
684 const bool includedByUnit = m_unitFilter == ALL_UNITS
685 || pin->GetUnit() == ALL_UNITS
686 || pin->GetUnit() == m_unitFilter;
687 const bool includedByBodyStyle = m_bodyStyleFilter == ALL_BODY_STYLES
688 || pin->GetBodyStyle() == ALL_BODY_STYLES
689 || pin->GetBodyStyle() == m_bodyStyleFilter;
690 const bool includedBySelection = !m_filterBySelection
691 || pinIsInEditorSelection( pin );
692
693 if( includedByUnit && includedByBodyStyle && includedBySelection )
694 {
695 int rowIndex = -1;
696
697 if( groupByName )
698 rowIndex = findRow( m_rows, pin->GetName() );
699 else if( groupBySelection && ( pin->GetFlags() & CANDIDATE ) )
700 rowIndex = 0;
701
702 if( rowIndex < 0 )
703 {
704 m_rows.emplace_back( std::vector<SCH_PIN*>() );
705 rowIndex = m_rows.size() - 1;
706 }
707
708 m_rows[ rowIndex ].push_back( pin );
709 }
710 }
711
712 int sortCol = 0;
713 bool ascending = true;
714
715 if( GetView() && GetView()->GetSortingColumn() != wxNOT_FOUND )
716 {
717 sortCol = GetView()->GetSortingColumn();
718 ascending = GetView()->IsSortOrderAscending();
719 }
720
721 for( std::vector<SCH_PIN*>& row : m_rows )
722 SortPins( row );
723
724 if( !groupBySelection )
725 SortRows( sortCol, ascending );
726
727 if ( GetView() )
728 {
729 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, (int) m_rows.size() );
730 GetView()->ProcessTableMessage( msg );
731
732 if( groupBySelection )
733 GetView()->SelectRow( 0 );
734 }
735
736 for( SCH_PIN* pin : clear_flags )
737 pin->ClearFlags( CANDIDATE );
738 }
739
740 void SortRows( int aSortCol, bool ascending )
741 {
742 std::sort( m_rows.begin(), m_rows.end(),
743 [ aSortCol, ascending, this ]( const std::vector<SCH_PIN*>& lhs,
744 const std::vector<SCH_PIN*>& rhs ) -> bool
745 {
746 return compare( lhs, rhs, aSortCol, ascending, m_frame );
747 } );
748 }
749
750 void SortPins( std::vector<SCH_PIN*>& aRow )
751 {
752 std::sort( aRow.begin(), aRow.end(),
753 []( SCH_PIN* lhs, SCH_PIN* rhs ) -> bool
754 {
755 return PIN_NUMBERS::Compare( lhs->GetNumber(), rhs->GetNumber() ) < 0;
756 } );
757 }
758
759 void AppendRow( SCH_PIN* aPin )
760 {
761 std::vector<SCH_PIN*> row;
762 row.push_back( aPin );
763 m_rows.push_back( row );
764
765 if ( GetView() )
766 {
767 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
768 GetView()->ProcessTableMessage( msg );
769 }
770
771 m_edited = true;
772 }
773
774 std::vector<SCH_PIN*> RemoveRow( int aRow )
775 {
776 std::vector<SCH_PIN*> removedRow = m_rows[ aRow ];
777
778 m_rows.erase( m_rows.begin() + aRow );
779
780 if( GetView() )
781 {
782 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
783 GetView()->ProcessTableMessage( msg );
784 }
785
786 m_edited = true;
787 return removedRow;
788 }
789
790 std::vector<SCH_PIN*> GetRowPins( int aRow )
791 {
792 return m_rows[ aRow ];
793 }
794
795 bool IsEdited()
796 {
797 return m_edited;
798 }
799
800private:
802
803 // Because the rows of the grid can either be a single pin or a group of pins, the
804 // data model is a 2D vector. If we're in the single pin case, each row's SCH_PINs
805 // contains only a single pin.
806 std::vector<std::vector<SCH_PIN*>> m_rows;
807 int m_unitFilter; // 0 to show pins for all units
808 int m_bodyStyleFilter; // 0 to show all body styles
810
812
814 LIB_SYMBOL* m_symbol; // Parent symbol that the pins belong to.
815
817 const std::vector<SCH_PIN*>& m_origSelectedPins;
818
819 std::unique_ptr<NUMERIC_EVALUATOR> m_eval;
820 std::map< std::pair<std::vector<SCH_PIN*>, int>, wxString > m_evalOriginal;
821};
822
823
825 const std::vector<SCH_PIN*>& aSelectedPins ) :
827 m_editFrame( parent ),
828 m_symbol( aSymbol )
829{
830 m_dataModel = new PIN_TABLE_DATA_MODEL( m_editFrame, this, this->m_symbol, aSelectedPins );
831
832 // Save original columns widths so we can do proportional sizing.
833 for( int i = 0; i < COL_COUNT; ++i )
834 m_originalColWidths[ i ] = m_grid->GetColSize( i );
835
836 m_grid->SetTable( m_dataModel );
837 m_grid->PushEventHandler( new GRID_TRICKS( m_grid, [this]( wxCommandEvent& aEvent )
838 {
839 OnAddRow( aEvent );
840 } ) );
841 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
842 m_grid->ShowHideColumns( "0 1 2 3 4 5 9 10" );
843 m_columnsShown = m_grid->GetShownColumns();
844
845 // Set special attributes
846 wxGridCellAttr* attr;
847
848 attr = new wxGridCellAttr;
849 attr->SetReadOnly( true );
850 m_grid->SetColAttr( COL_PIN_COUNT, attr );
851
852 attr = new wxGridCellAttr;
853 wxArrayString typeNames = PinTypeNames();
854 typeNames.push_back( INDETERMINATE_STATE );
855 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinTypeIcons(), typeNames ) );
856 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinTypeIcons(), typeNames ) );
857 m_grid->SetColAttr( COL_TYPE, attr );
858
859 attr = new wxGridCellAttr;
860 wxArrayString shapeNames = PinShapeNames();
861 shapeNames.push_back( INDETERMINATE_STATE );
862 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinShapeIcons(), shapeNames ) );
863 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinShapeIcons(), shapeNames ) );
864 m_grid->SetColAttr( COL_SHAPE, attr );
865
866 attr = new wxGridCellAttr;
867 wxArrayString orientationNames = PinOrientationNames();
868 orientationNames.push_back( INDETERMINATE_STATE );
869 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinOrientationIcons(), orientationNames ) );
870 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinOrientationIcons(), orientationNames ) );
871 m_grid->SetColAttr( COL_ORIENTATION, attr );
872
873 attr = new wxGridCellAttr;
874 wxArrayString unitNames;
875 unitNames.push_back( wxGetTranslation( UNITS_ALL ) );
876
877 for( int i = 1; i <= aSymbol->GetUnitCount(); i++ )
878 unitNames.push_back( m_symbol->GetUnitDisplayName( i, true ) );
879
880 attr->SetEditor( new GRID_CELL_COMBOBOX( unitNames ) );
881 m_grid->SetColAttr( COL_UNIT, attr );
882
883 attr = new wxGridCellAttr;
884 wxArrayString bodyStyleNames;
885 bodyStyleNames.push_back( wxGetTranslation( DEMORGAN_ALL ) );
886
887 if( aSymbol->HasDeMorganBodyStyles() )
888 {
889 bodyStyleNames.push_back( wxGetTranslation( DEMORGAN_STD ) );
890 bodyStyleNames.push_back( wxGetTranslation( DEMORGAN_ALT ) );
891 }
892 else
893 {
894 for( const wxString& body_style_name : aSymbol->GetBodyStyleNames() )
895 bodyStyleNames.push_back( body_style_name );
896 }
897
898 attr->SetEditor( new GRID_CELL_COMBOBOX( bodyStyleNames ) );
899 m_grid->SetColAttr( COL_BODY_STYLE, attr );
900
901 attr = new wxGridCellAttr;
902 attr->SetRenderer( new wxGridCellBoolRenderer() );
903 attr->SetEditor( new wxGridCellBoolEditor() );
904 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
905 m_grid->SetColAttr( COL_VISIBLE, attr );
906
907 /* Right-aligned position values look much better, but only MSW and GTK2+
908 * currently support right-aligned textEditCtrls, so the text jumps on all
909 * the other platforms when you edit it.
910 attr = new wxGridCellAttr;
911 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
912 m_grid->SetColAttr( COL_POSX, attr );
913
914 attr = new wxGridCellAttr;
915 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
916 m_grid->SetColAttr( COL_POSY, attr );
917 */
918
922 m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
923
924 const int summaryW = m_pin_numbers_summary->GetCharWidth() * 30;
925
926 m_pin_numbers_summary->SetWindowStyleFlag( m_pin_numbers_summary->GetWindowStyleFlag() | wxST_ELLIPSIZE_END );
927 m_pin_numbers_summary->SetMaxSize( wxSize( summaryW, -1 ) );
928
929 m_duplicate_pins->SetWindowStyleFlag( m_duplicate_pins->GetWindowStyleFlag() | wxST_ELLIPSIZE_END );
930 m_duplicate_pins->SetMaxSize( wxSize( summaryW, -1 ) );
931
932 GetSizer()->SetSizeHints(this);
933 Centre();
934
935 if( aSymbol->IsMultiUnit() )
936 {
937 m_unitFilter->Append( wxGetTranslation( UNITS_ALL ) );
938
939 for( int ii = 0; ii < aSymbol->GetUnitCount(); ++ii )
940 m_unitFilter->Append( LIB_SYMBOL::LetterSubReference( ii + 1, 'A' ) );
941
942 m_unitFilter->SetSelection( -1 );
943 }
944 else
945 {
946 m_cbFilterByUnit->Enable( false );
947 m_unitFilter->Enable( false );
948 }
949
950 if( aSymbol->HasDeMorganBodyStyles() )
951 {
952 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALL ) );
953 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_STD ) );
954 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALT ) );
955
956 m_bodyStyleFilter->SetSelection( -1 );
957 }
958 else if( aSymbol->IsMultiBodyStyle() )
959 {
960 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALL ) );
961
962 for( const wxString& bodyStyle : aSymbol->GetBodyStyleNames() )
963 m_bodyStyleFilter->Append( bodyStyle );
964
965 m_bodyStyleFilter->SetSelection( -1 );
966 }
967 else
968 {
969 m_cbFilterByBodyStyle->Enable( false );
970 m_bodyStyleFilter->Enable( false );
971 }
972
974
976
977 if( !parent->IsSymbolGraphicallyEditable() )
978 {
979 // Export is OK, but Import is just going to produce frustration
980 m_btnImportFromClipboard->Enable( false );
981 m_btnImportFromFile->Enable( false );
982
983 m_ButtonsCancel->SetDefault();
984 m_ButtonsOK->SetLabel( _( "Read Only" ) );
985 m_ButtonsOK->Enable( false );
986 }
987
988 m_initialized = true;
989 m_modified = false;
990
991 // Connect Events
992 m_grid->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr,
993 this );
994}
995
996
998{
999 // Disconnect Events
1000 m_grid->Disconnect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr,
1001 this );
1002
1003 // Prevents crash bug in wxGrid's d'tor
1004 m_grid->DestroyTable( m_dataModel );
1005
1006 // Delete the GRID_TRICKS.
1007 m_grid->PopEventHandler( true );
1008
1009 // This is our copy of the pins. If they were transferred to the part on an OK, then
1010 // m_pins will already be empty.
1011 for( SCH_PIN* pin : m_pins )
1012 delete pin;
1013
1014 WINDOW_THAWER thawer( m_editFrame );
1015
1016 m_editFrame->ClearFocus();
1017 m_editFrame->GetCanvas()->Refresh();
1018}
1019
1020
1022{
1023 // Make a copy of the pins for editing
1024 std::vector<SCH_PIN*> pins = m_symbol->GetGraphicalPins( 0, 0 );
1025
1026 for( SCH_PIN* pin : pins )
1027 m_pins.push_back( new SCH_PIN( *pin ) );
1028
1029 if( m_cbFilterByUnit->GetValue() )
1030 {
1031 if( m_unitFilter->GetSelection() == -1 )
1032 m_unitFilter->SetSelection( 0 );
1033
1034 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1035 }
1036
1037 if( m_cbFilterByBodyStyle->GetValue() )
1038 {
1039 if( m_bodyStyleFilter->GetSelection() == -1 )
1040 m_bodyStyleFilter->SetSelection( 0 );
1041
1042 m_dataModel->SetBodyStyleFilter( m_bodyStyleFilter->GetSelection() );
1043 }
1044
1045 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
1046
1047 updateSummary();
1048
1049 return true;
1050}
1051
1052
1054{
1055 if( !m_grid->CommitPendingChanges() )
1056 return false;
1057
1058 // Delete the part's pins
1059 std::vector<SCH_PIN*> pins = m_symbol->GetGraphicalPins( 0, 0 );
1060
1061 for( SCH_PIN* pin : pins )
1062 m_symbol->RemoveDrawItem( pin );
1063
1064 // Transfer our pins to the part
1065 for( SCH_PIN* pin : m_pins )
1066 m_symbol->AddDrawItem( pin );
1067
1068 m_pins.clear();
1069
1070 return true;
1071}
1072
1073
1074void DIALOG_LIB_EDIT_PIN_TABLE::OnColSort( wxGridEvent& aEvent )
1075{
1076 int sortCol = aEvent.GetCol();
1077 bool ascending;
1078
1079 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
1080 // event, and if we ask it will give us pre-event info.
1081 if( m_grid->IsSortingBy( sortCol ) )
1082 // same column; invert ascending
1083 ascending = !m_grid->IsSortOrderAscending();
1084 else
1085 // different column; start with ascending
1086 ascending = true;
1087
1088 m_dataModel->SortRows( sortCol, ascending );
1089}
1090
1091
1092void DIALOG_LIB_EDIT_PIN_TABLE::OnAddRow( wxCommandEvent& event )
1093{
1094 m_grid->OnAddRow(
1095 [&]() -> std::pair<int, int>
1096 {
1097 SCH_PIN* newPin = new SCH_PIN( this->m_symbol );
1098
1099 // Copy the settings of the last pin onto the new pin.
1100 if( m_pins.size() > 0 )
1101 {
1102 SCH_PIN* last = m_pins.back();
1103
1104 newPin->SetOrientation( last->GetOrientation() );
1105 newPin->SetType( last->GetType() );
1106 newPin->SetShape( last->GetShape() );
1107 newPin->SetUnit( last->GetUnit() );
1108 newPin->SetBodyStyle( last->GetBodyStyle() );
1109
1110 VECTOR2I pos = last->GetPosition();
1111
1112 SYMBOL_EDITOR_SETTINGS* cfg = m_editFrame->GetSettings();
1113
1116 {
1117 pos.y -= schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
1118 }
1119 else
1120 {
1121 pos.x += schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
1122 }
1123
1124 newPin->SetPosition( pos );
1125 }
1126
1127 m_pins.push_back( newPin );
1128
1129 m_dataModel->AppendRow( m_pins[ m_pins.size() - 1 ] );
1130 updateSummary();
1131
1132 return { m_dataModel->GetNumberRows() - 1, COL_NUMBER };
1133 } );
1134}
1135
1136
1138{
1139 m_pins.push_back( pin );
1140 updateSummary();
1141}
1142
1143
1144void DIALOG_LIB_EDIT_PIN_TABLE::OnDeleteRow( wxCommandEvent& event )
1145{
1146 m_grid->OnDeleteRows(
1147 [&]( int row )
1148 {
1149 std::vector<SCH_PIN*> removedRow = m_dataModel->RemoveRow( row );
1150
1151 for( SCH_PIN* pin : removedRow )
1152 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
1153 } );
1154
1155 updateSummary();
1156}
1157
1158
1160{
1161 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
1162 updateSummary();
1163}
1164
1165
1167{
1168 updateSummary();
1169}
1170
1171
1173{
1174 SCH_PIN* pin = nullptr;
1175
1176 if( event.GetRow() >= 0 && event.GetRow() < m_dataModel->GetNumberRows() )
1177 {
1178 const std::vector<SCH_PIN*>& pins = m_dataModel->GetRowPins( event.GetRow() );
1179
1180 if( pins.size() == 1 && m_editFrame->GetCurSymbol() )
1181 {
1182 for( SCH_PIN* candidate : m_editFrame->GetCurSymbol()->GetGraphicalPins( 0, 0 ) )
1183 {
1184 if( candidate->GetNumber() == pins.at( 0 )->GetNumber() )
1185 {
1186 pin = candidate;
1187 break;
1188 }
1189 }
1190 }
1191 }
1192
1193 SYMBOL_EDITOR_SETTINGS* cfg = static_cast<SYMBOL_EDITOR_SETTINGS*>( m_editFrame->config() );
1194
1196 {
1197 WINDOW_THAWER thawer( m_editFrame );
1198
1199 m_editFrame->FocusOnItem( pin );
1200 m_editFrame->GetCanvas()->Refresh();
1201 }
1202
1203 // Let WX_GRID run its editor-positioning handler
1204 if( event.GetEventType() == wxEVT_GRID_EDITOR_SHOWN )
1205 event.Skip();
1206}
1207
1208
1210{
1211 return m_cbGroup->GetValue();
1212}
1213
1214
1216{
1217 m_cbGroup->SetValue( false );
1218
1219 m_dataModel->RebuildRows( m_pins, false, true );
1220
1221 m_grid->ShowCol( COL_PIN_COUNT );
1222 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1223
1225}
1226
1227
1229{
1230 if( !m_grid->CommitPendingChanges() )
1231 return;
1232
1233 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
1234
1235 if( m_cbGroup->GetValue() )
1236 {
1237 m_grid->ShowCol( COL_PIN_COUNT );
1238 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1239 }
1240
1242}
1243
1244
1245void DIALOG_LIB_EDIT_PIN_TABLE::OnMenu( wxCommandEvent& event )
1246{
1247 SYMBOL_EDITOR_SETTINGS* cfg = static_cast<SYMBOL_EDITOR_SETTINGS*>( m_editFrame->config() );
1248
1249 // Build a pop menu:
1250 wxMenu menu;
1251
1252 menu.Append( 4206, _( "Highlight on Cross-probe" ),
1253 _( "Highlight corresponding pin on canvas when it is selected in the table" ),
1254 wxITEM_CHECK );
1255 menu.Check( 4206, cfg->m_PinTable.crossprobe_on_selection );
1256
1257 // menu_id is the selected submenu id from the popup menu or wxID_NONE
1258 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
1259
1260 if( menu_id == 0 || menu_id == 4206 )
1262}
1263
1264
1266{
1267 if( event.GetEventObject() == m_cbFilterByUnit )
1268 {
1269 if( event.IsChecked() )
1270 {
1271 if( m_unitFilter->GetSelection() == -1 )
1272 m_unitFilter->SetSelection( 0 );
1273
1274 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1275 }
1276 else
1277 {
1278 m_dataModel->SetUnitFilter( -1 );
1279 m_unitFilter->SetSelection( -1 );
1280 }
1281 }
1282 else if( event.GetEventObject() == m_cbFilterByBodyStyle )
1283 {
1284 if( event.IsChecked() )
1285 {
1286 if( m_bodyStyleFilter->GetSelection() == -1 )
1287 m_bodyStyleFilter->SetSelection( 0 );
1288
1289 m_dataModel->SetBodyStyleFilter( m_bodyStyleFilter->GetSelection() );
1290 }
1291 else
1292 {
1293 m_dataModel->SetBodyStyleFilter( ALL_BODY_STYLES );
1294 m_bodyStyleFilter->SetSelection( -1 );
1295 }
1296 }
1297 else if( event.GetEventObject() == m_cbFilterSelected )
1298 {
1299 m_dataModel->SetFilterBySelection( event.IsChecked() );
1300 }
1301
1302 OnRebuildRows( event );
1303}
1304
1305
1307{
1308 if( event.GetEventObject() == m_unitFilter )
1309 {
1310 m_cbFilterByUnit->SetValue( true );
1311 m_dataModel->SetUnitFilter( std::max( m_unitFilter->GetSelection(), ALL_UNITS ) );
1312 }
1313 else if( event.GetEventObject() == m_bodyStyleFilter )
1314 {
1315 m_cbFilterByBodyStyle->SetValue( true );
1316 m_dataModel->SetBodyStyleFilter( std::max( m_bodyStyleFilter->GetSelection(), ALL_BODY_STYLES ) );
1317 }
1318
1319 OnRebuildRows( event );
1320}
1321
1322
1324{
1325 if( !m_grid->CommitPendingChanges() )
1326 return;
1327
1328 bool fromFile = event.GetEventObject() == m_btnImportFromFile;
1329 bool replaceAll = m_rbReplaceAll->GetValue();
1330
1332
1334
1335 auto updateFn =
1336 [&]( SCH_PIN& aPin, const wxString& aVal, COL_ORDER aCol )
1337 {
1338 fmt.UpdatePin( aPin, aVal, aCol, *m_symbol );
1339 };
1340
1341 auto createFn =
1342 []( LIB_SYMBOL& aSym )
1343 {
1344 return std::make_unique<SCH_PIN>( &aSym );
1345 };
1346
1347 auto colLabelToEnumFn =
1348 []( const wxString& aStr ) -> COL_ORDER
1349 {
1350 return GetColTypeForString( aStr );
1351 };
1352
1353 std::optional<std::vector<std::vector<wxString>>> csvData = ReadTableFromFileOrClipboard( *m_editFrame, fromFile );
1354
1355 std::vector<std::unique_ptr<SCH_PIN>> newPins;
1356
1357 if( csvData && csvData->size() >= 2 )
1358 {
1359 std::vector<COL_ORDER> headerCols;
1360 wxArrayString unknownHeaders;
1361
1362 for( const wxString& label : ( *csvData )[0] )
1363 {
1364 COL_ORDER col = colLabelToEnumFn( label );
1365
1366 if( col >= COL_COUNT )
1367 unknownHeaders.push_back( label );
1368
1369 headerCols.push_back( col );
1370 }
1371
1372 if( !unknownHeaders.IsEmpty() )
1373 {
1374 wxString msg = wxString::Format( _( "Unknown columns in data: %s. These columns will be ignored." ),
1375 AccumulateDescriptions( unknownHeaders ) );
1376 reporter.Report( msg, RPT_SEVERITY_ERROR );
1377 }
1378
1379 for( size_t i = 1; i < csvData->size(); ++i )
1380 {
1381 const std::vector<wxString>& cols = ( *csvData )[i];
1382 std::unique_ptr<SCH_PIN> item = createFn( *m_symbol );
1383
1384 size_t maxCol = std::min( headerCols.size(), cols.size() );
1385
1386 for( size_t j = 0; j < maxCol; ++j )
1387 {
1388 if( headerCols[j] == COL_COUNT )
1389 continue;
1390
1391 updateFn( *item, cols[j], headerCols[j] );
1392 }
1393
1394 newPins.emplace_back( std::move( item ) );
1395 }
1396 }
1397
1398 if( reporter.HasMessage() )
1399 {
1400 int ret = wxMessageBox( reporter.GetMessages(), _( "Errors" ), wxOK | wxCANCEL | wxICON_ERROR, this );
1401
1402 // Give the user the option to cancel the import on errors.
1403 if( ret == wxCANCEL )
1404 return;
1405 }
1406
1407 if( !newPins.size() )
1408 return;
1409
1410 if( replaceAll )
1411 {
1412 // This is quite a dance with a segfault without smart pointers
1413 for( SCH_PIN* pin : m_pins )
1414 delete pin;
1415
1416 m_pins.clear();
1417 }
1418
1419 for( std::unique_ptr<SCH_PIN>& newPin : newPins )
1420 m_pins.push_back( newPin.release() );
1421
1422 m_cbGroup->SetValue( false );
1423 m_dataModel->RebuildRows( m_pins, false, false );
1424 m_modified = true;
1425
1426 updateSummary();
1427}
1428
1429
1431{
1432 if( !m_grid->CommitPendingChanges() )
1433 return;
1434
1435 bool toFile = event.GetEventObject() == m_btnExportToFile;
1436 bool onlyShown = m_rbExportOnlyShownPins->GetValue();
1437
1438 wxString filePath = wxEmptyString;
1439
1440 if( toFile )
1441 {
1442 wxFileName fn( m_symbol->GetName() );
1443 fn.SetExt( FILEEXT::CsvFileExtension );
1444 wxFileDialog dlg( this, _( "Select pin data file" ), "", fn.GetFullName(), FILEEXT::CsvFileWildcard(),
1445 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1446
1448
1449 if( dlg.ShowModal() == wxID_CANCEL )
1450 return;
1451
1452 filePath = dlg.GetPath();
1453 }
1454
1455 std::vector<SCH_PIN*> pinsToExport;
1456
1457 if( onlyShown )
1458 {
1459 for( int i = 0; i < m_dataModel->GetNumberRows(); ++i )
1460 {
1461 for( SCH_PIN* pin : m_dataModel->GetRowPins( i ) )
1462 pinsToExport.push_back( pin );
1463 }
1464 }
1465 else
1466 {
1467 pinsToExport = m_pins;
1468 }
1469
1470 static const std::vector<COL_ORDER> exportCols {
1471 COL_NUMBER,
1472 COL_NAME,
1473 COL_TYPE,
1474 COL_SHAPE,
1478 COL_LENGTH,
1479 COL_POSX,
1480 COL_POSY,
1482 COL_UNIT,
1484 };
1485
1488
1489 std::vector<std::vector<wxString>> table;
1490 table.reserve( pinsToExport.size() + 1 );
1491
1492 std::vector<wxString> header;
1493 header.reserve( exportCols.size() );
1494
1495 for( COL_ORDER col : exportCols )
1496 header.emplace_back( wxGetTranslation( GetPinTableColLabel( static_cast<int>( col ) ) ) );
1497
1498 table.emplace_back( std::move( header ) );
1499
1500 for( const SCH_PIN* pin : pinsToExport )
1501 {
1502 std::vector<wxString>& row = table.emplace_back();
1503 row.reserve( exportCols.size() );
1504
1505 for( COL_ORDER col : exportCols )
1506 row.emplace_back( fmt.Format( *pin, static_cast<int>( col ) ) );
1507 }
1508
1510}
1511
1512
1514{
1515 // Account for scroll bars
1517
1518 wxGridUpdateLocker deferRepaintsTillLeavingScope;
1519
1520 // The Number and Name columns must be at least wide enough to hold their contents, but
1521 // no less wide than their original widths.
1522 m_grid->AutoSizeColumn( COL_NUMBER );
1523
1524 const int colNumberMax = std::max( m_originalColWidths[COL_NUMBER] * 4, 400 );
1525
1526 if( m_grid->GetColSize( COL_NUMBER ) > colNumberMax )
1527 m_grid->SetColSize( COL_NUMBER, colNumberMax );
1528
1529 if( m_grid->GetColSize( COL_NUMBER ) < m_originalColWidths[ COL_NUMBER ] )
1531
1532 m_grid->AutoSizeColumn( COL_NAME );
1533
1534 if( m_grid->GetColSize( COL_NAME ) < m_originalColWidths[ COL_NAME ] )
1535 m_grid->SetColSize( COL_NAME, m_originalColWidths[ COL_NAME ] );
1536
1537 // If the grid is still wider than the columns, then stretch the Number and Name columns
1538 // to fit.
1539 for( int i = 0; i < COL_COUNT; ++i )
1540 width -= m_grid->GetColSize( i );
1541
1542 if( width > 0 )
1543 {
1544 m_grid->SetColSize( COL_NUMBER, m_grid->GetColSize( COL_NUMBER ) + width / 2 );
1545 m_grid->SetColSize( COL_NAME, m_grid->GetColSize( COL_NAME ) + width / 2 );
1546 }
1547}
1548
1549
1550void DIALOG_LIB_EDIT_PIN_TABLE::OnSize( wxSizeEvent& event )
1551{
1552 wxSize new_size = event.GetSize();
1553
1554 if( m_initialized && m_size != new_size )
1555 {
1556 m_size = new_size;
1557
1559 }
1560
1561 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1562 event.Skip();
1563}
1564
1565
1566void DIALOG_LIB_EDIT_PIN_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
1567{
1568 std::bitset<64> columnsShown = m_grid->GetShownColumns();
1569
1570 if( columnsShown != m_columnsShown )
1571 {
1572 m_columnsShown = columnsShown;
1573
1574 if( !m_grid->IsCellEditControlShown() )
1576 }
1577
1578 int firstSelectedRow;
1579 int selectedRowCount;
1580
1581 getSelectedArea( m_grid, &firstSelectedRow, &selectedRowCount );
1582
1583 if( ( selectedRowCount > 1 ) != m_groupSelected->IsEnabled() )
1584 m_groupSelected->Enable( selectedRowCount > 1 );
1585}
1586
1587
1588void DIALOG_LIB_EDIT_PIN_TABLE::OnCancel( wxCommandEvent& event )
1589{
1590 Close();
1591}
1592
1593
1594void DIALOG_LIB_EDIT_PIN_TABLE::OnClose( wxCloseEvent& event )
1595{
1596 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1597 m_grid->CommitPendingChanges( true );
1598
1599 int retval = wxID_CANCEL;
1600
1601 if( ( m_modified || m_dataModel->IsEdited() ) && m_ButtonsOK->IsEnabled() )
1602 {
1603 if( HandleUnsavedChanges( this, _( "Save changes?" ),
1604 [&]() -> bool
1605 {
1607 {
1608 retval = wxID_OK;
1609 return true;
1610 }
1611
1612 return false;
1613 } ) )
1614 {
1615 EndDialogShim( retval );
1616 return;
1617 }
1618 else
1619 {
1620 event.Veto();
1621 return;
1622 }
1623 }
1624
1625 // No change in dialog: we can close it
1626 EndDialogShim( retval );
1627}
1628
1629
1631{
1632 PIN_NUMBERS pinNumbers;
1633
1634 for( SCH_PIN* pin : m_pins )
1635 {
1636 if( pin->GetNumber().Length() )
1637 pinNumbers.insert( pin->GetNumber() );
1638 }
1639
1640 const wxString summary = pinNumbers.GetSummary();
1641 const wxString duplicates = pinNumbers.GetDuplicates();
1642
1643 m_pin_numbers_summary->SetLabel( summary );
1644 m_pin_numbers_summary->SetToolTip( summary );
1645 m_pin_count->SetLabel( wxString::Format( wxT( "%u" ), (unsigned) m_pins.size() ) );
1646 m_duplicate_pins->SetLabel( duplicates );
1647 m_duplicate_pins->SetToolTip( duplicates );
1648
1649 Layout();
1650}
int index
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
COLUMN_FORMATTER(UNITS_PROVIDER &aUnitsProvider, bool aIncludeUnits, BOOL_FORMAT aBoolFormat, REPORTER &aReporter)
wxString stringFromBool(bool aValue) const
bool boolFromString(const wxString &aValue, REPORTER &aReporter) const
UNITS_PROVIDER & m_unitsProvider
DIALOG_LIB_EDIT_PIN_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Pin Table"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
bool m_modified
true when there are unsaved changes
void OnExportButtonClick(wxCommandEvent &event) override
void OnRebuildRows(wxCommandEvent &event) override
void OnCancel(wxCommandEvent &event) override
void OnUpdateUI(wxUpdateUIEvent &event) override
void OnImportButtonClick(wxCommandEvent &event) override
void OnGroupSelected(wxCommandEvent &event) override
void OnClose(wxCloseEvent &event) override
void OnSize(wxSizeEvent &event) override
DIALOG_LIB_EDIT_PIN_TABLE(SYMBOL_EDIT_FRAME *parent, LIB_SYMBOL *aSymbol, const std::vector< SCH_PIN * > &aSelectedPins)
void OnMenu(wxCommandEvent &event) override
void OnCellSelected(wxGridEvent &event) override
void OnAddRow(wxCommandEvent &event) override
void OnDeleteRow(wxCommandEvent &event) override
void OnCellEdited(wxGridEvent &event) override
std::vector< SCH_PIN * > m_pins
void OnFilterCheckBox(wxCommandEvent &event) override
void OnFilterChoice(wxCommandEvent &event) override
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void EndDialogShim(int aReturnCode)
A mode agnostic way to close a dialog.
The base class for create windows for drawing purpose.
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Define a library symbol object.
Definition lib_symbol.h:114
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:902
static wxString LetterSubReference(int aUnit, wxChar aInitialLetter)
const std::vector< wxString > & GetBodyStyleNames() const
bool HasDeMorganBodyStyles() const override
bool IsMultiUnit() const override
Definition lib_symbol.h:898
int GetBodyStyleCount() const override
The body styles are a property of the drawings, which a derived symbol inherits from its root symbol ...
int GetUnitCount() const override
A singleton reporter that reports to nowhere.
Definition reporter.h:267
Class that handles conversion of various pin data fields into strings for display in the UI or serial...
wxString Format(const SCH_PIN &aPin, int aFieldId) const
void UpdatePin(SCH_PIN &aPin, const wxString &aValue, int aFieldId, const LIB_SYMBOL &aSymbol) const
Update the pin from the given col/string.
PIN_INFO_FORMATTER(UNITS_PROVIDER &aUnitsProvider, bool aIncludeUnits, BOOL_FORMAT aBoolFormat, REPORTER &aReporter)
wxString GetDuplicates() const
Gets a formatted string of all the pins that have duplicate numbers.
static int Compare(const wxString &lhs, const wxString &rhs)
void insert(value_type const &v)
Definition pin_numbers.h:58
wxString GetSummary() const
static bool compare(const std::vector< SCH_PIN * > &lhs, const std::vector< SCH_PIN * > &rhs, int sortCol, bool ascending, EDA_DRAW_FRAME *parentFrame)
void RebuildRows(const std::vector< SCH_PIN * > &aPins, bool groupByName, bool groupBySelection)
static int findRow(const std::vector< std::vector< SCH_PIN * > > &aRowSet, const wxString &aName)
std::map< std::pair< std::vector< SCH_PIN * >, int >, wxString > m_evalOriginal
std::vector< std::vector< SCH_PIN * > > m_rows
const std::vector< SCH_PIN * > & m_origSelectedPins
The pins in the symbol that are selected at dialog start.
bool IsEmptyCell(int row, int col) override
DIALOG_LIB_EDIT_PIN_TABLE * m_pinTable
std::vector< SCH_PIN * > GetRowPins(int aRow)
static wxString GetValue(const std::vector< SCH_PIN * > &pins, int aCol, EDA_DRAW_FRAME *aParentFrame)
PIN_TABLE_DATA_MODEL(SYMBOL_EDIT_FRAME *aFrame, DIALOG_LIB_EDIT_PIN_TABLE *aPinTable, LIB_SYMBOL *aSymbol, const std::vector< SCH_PIN * > &aOrigSelectedPins)
std::unique_ptr< NUMERIC_EVALUATOR > m_eval
void SetValue(int aRow, int aCol, const wxString &aValue) override
wxString GetValue(int aRow, int aCol) override
void SetFilterBySelection(bool aFilter)
void SortRows(int aSortCol, bool ascending)
void onUnitsChanged(wxCommandEvent &aEvent)
wxString GetColLabelValue(int aCol) override
std::vector< SCH_PIN * > RemoveRow(int aRow)
void SortPins(std::vector< SCH_PIN * > &aRow)
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:252
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:350
virtual wxString GetBodyStyleDescription(int aBodyStyle, bool aLabel) const
Definition sch_item.cpp:236
int GetBodyStyle() const
Definition sch_item.h:253
virtual wxString GetUnitDisplayName(int aUnit, bool aLabel) const
Definition sch_item.cpp:225
int GetUnit() const
Definition sch_item.h:243
virtual void SetUnit(int aUnit)
Definition sch_item.h:242
int GetNumberTextSize() const
Definition sch_pin.cpp:882
int GetLength() const
Definition sch_pin.cpp:421
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:842
void SetVisible(bool aVisible)
Definition sch_pin.h:134
void ChangeLength(int aLength)
Change the length of a pin and adjust its position based on orientation.
Definition sch_pin.cpp:1492
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:113
void SetName(const wxString &aName)
Definition sch_pin.cpp:545
bool IsVisible() const
Definition sch_pin.cpp:513
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:318
const wxString & GetName() const
Definition sch_pin.cpp:527
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:386
void SetNumberTextSize(int aSize)
Definition sch_pin.cpp:896
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:116
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:378
int GetNameTextSize() const
Definition sch_pin.cpp:856
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:455
const wxString & GetNumber() const
Definition sch_pin.h:144
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:400
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:435
void SetNameTextSize(int aSize)
Definition sch_pin.cpp:870
The symbol library editor main window.
bool IsSymbolGraphicallyEditable() const
Test if a symbol is loaded and can be edited graphically.
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:60
int ValueFromString(const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aTextValue in aUnits to internal units used by the frame.
A wrapper for reporting to a wxString object.
Definition reporter.h:242
bool MatchTranslationOrNative(const wxString &aStr, const wxString &aNativeLabel, bool aCaseSensitive)
Return true if the given string matches either the translated or native version of the given label.
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:146
This file is part of the common library.
static wxString GetPinTableColLabel(int aCol)
Get the label for a given column in the pin table.
void getSelectedArea(WX_GRID *aGrid, int *aRowStart, int *aRowCount)
static COL_ORDER GetColTypeForString(const wxString &aStr)
#define _(s)
#define CANDIDATE
flag indicating that the structure is connected
static std::map< int, wxString > shapeNames
static const std::string CsvFileExtension
static wxString CsvFileWildcard()
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition wxgtk/ui.cpp:367
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
#define _HKI(x)
Definition page_info.cpp:40
see class PGM_BASE
const std::vector< BITMAPS > & PinTypeIcons()
Definition pin_type.cpp:158
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
const wxArrayString & PinTypeNames()
Definition pin_type.cpp:149
int PinOrientationIndex(PIN_ORIENTATION code)
Definition pin_type.cpp:66
const wxArrayString & PinShapeNames()
Definition pin_type.cpp:167
const std::vector< BITMAPS > & PinShapeIcons()
Definition pin_type.cpp:176
const wxArrayString & PinOrientationNames()
Definition pin_type.cpp:185
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
const std::vector< BITMAPS > & PinOrientationIcons()
Definition pin_type.cpp:194
GRAPHIC_PINSHAPE
Definition pin_type.h:80
@ RPT_SEVERITY_ERROR
T * GetAppSettings(const char *aFilename)
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
#define UNITS_ALL
#define DEMORGAN_ALL
#define DEMORGAN_ALT
#define DEMORGAN_STD
void WriteTableToFileOrClipboard(const wxString &aToFile, const std::vector< std::vector< wxString > > &aTable)
Write aTable as CSV to aToFile (if non-empty) or to the system clipboard.
Definition table_io.cpp:66
std::optional< std::vector< std::vector< wxString > > > ReadTableFromFileOrClipboard(EDA_BASE_FRAME &aFrame, bool aFromFile)
Read a CSV/TSV table from a file selected via a dialog (aFromFile) or from the system clipboard.
Definition table_io.cpp:34
IbisParser parser & reporter
KIBIS_PIN * pin
VECTOR3I res
#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:708
Definition of file extensions used in Kicad.