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