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