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