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