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( aPin.GetUnit() == 0 )
173 return wxGetTranslation( UNITS_ALL );
174
175 return aPin.GetUnitDisplayName( aPin.GetUnit(), true );
176
177 case COL_BODY_STYLE:
178 if( aPin.GetBodyStyle() == 0 )
179 return wxGetTranslation( DEMORGAN_ALL );
180
181 return aPin.GetBodyStyleDescription( aPin.GetBodyStyle(), true );
182
183 default:
184 wxFAIL_MSG( wxString::Format( "Invalid field id %d", aFieldId ) );
185 return wxEmptyString;
186 }
187 }
188
195 void UpdatePin( SCH_PIN& aPin, const wxString& aValue, int aFieldId, const LIB_SYMBOL& aSymbol ) const
196 {
197 switch( aFieldId )
198 {
199 case COL_NUMBER:
200 aPin.SetNumber( aValue );
201 break;
202
203 case COL_NAME:
204 aPin.SetName( aValue );
205 break;
206
207 case COL_TYPE:
208 if( PinTypeNames().Index( aValue, false ) != wxNOT_FOUND )
209 aPin.SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( aValue ) );
210
211 break;
212
213 case COL_SHAPE:
214 if( PinShapeNames().Index( aValue, false ) != wxNOT_FOUND )
215 aPin.SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( aValue ) );
216
217 break;
218
219 case COL_ORIENTATION:
220 if( PinOrientationNames().Index( aValue, false ) != wxNOT_FOUND )
221 aPin.SetOrientation( (PIN_ORIENTATION) PinOrientationNames().Index( aValue ) );
222
223 break;
224
225 case COL_NUMBER_SIZE:
226 aPin.SetNumberTextSize( m_unitsProvider.ValueFromString( aValue ) );
227 break;
228
229 case COL_NAME_SIZE:
230 aPin.SetNameTextSize( m_unitsProvider.ValueFromString( aValue ) );
231 break;
232
233 case COL_LENGTH:
234 aPin.ChangeLength( m_unitsProvider.ValueFromString( aValue ) );
235 break;
236
237 case COL_POSX:
238 aPin.SetPosition( VECTOR2I( m_unitsProvider.ValueFromString( aValue ), aPin.GetPosition().y ) );
239 break;
240
241 case COL_POSY:
242 aPin.SetPosition( VECTOR2I( aPin.GetPosition().x, m_unitsProvider.ValueFromString( aValue ) ) );
243 break;
244
245 case COL_VISIBLE:
246 aPin.SetVisible(boolFromString( aValue, m_reporter ) );
247 break;
248
249 case COL_UNIT:
250 if( MatchTranslationOrNative( aValue, UNITS_ALL, false ) )
251 {
252 aPin.SetUnit( 0 );
253 break;
254 }
255
256 for( int i = 1; i <= aSymbol.GetUnitCount(); i++ )
257 {
258 if( aValue == aPin.GetBodyStyleDescription( i, true )
259 || aValue == aPin.GetBodyStyleDescription( i, false ) )
260 {
261 aPin.SetUnit( i );
262 break;
263 }
264 }
265
266 break;
267
268 case COL_BODY_STYLE:
269 if( MatchTranslationOrNative( aValue, DEMORGAN_ALL, false ) )
270 {
271 aPin.SetBodyStyle( 0 );
272 break;
273 }
274
275 for( int i = 1; i <= aSymbol.GetBodyStyleCount(); i++ )
276 {
277 if( aValue == aPin.GetBodyStyleDescription( i, true )
278 || aValue == aPin.GetBodyStyleDescription( i, false ) )
279 {
280 aPin.SetBodyStyle( i );
281 break;
282 }
283 }
284
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, EDA_DRAW_FRAME* aParentFrame )
438 {
439 wxString fieldValue;
440
441 if( pins.empty() )
442 return fieldValue;
443
444 NULL_REPORTER reporter;
445 PIN_INFO_FORMATTER formatter( *aParentFrame, true, PIN_INFO_FORMATTER::BOOL_FORMAT::ZERO_ONE, reporter );
446
447 for( const SCH_PIN* pin : pins )
448 {
449 wxString val;
450 switch( aCol )
451 {
452 case COL_PIN_COUNT:
453 val << pins.size();
454 break;
455 default:
456 val << formatter.Format( *pin, aCol );
457 break;
458 }
459
460 if( aCol == COL_NUMBER )
461 {
462 if( fieldValue.length() )
463 fieldValue += wxT( ", " );
464
465 fieldValue += val;
466 }
467 else
468 {
469 if( !fieldValue.Length() )
470 fieldValue = val;
471 else if( val != fieldValue )
472 fieldValue = INDETERMINATE_STATE;
473 }
474 }
475
476 return fieldValue;
477 }
478
479 void SetValue( int aRow, int aCol, const wxString &aValue ) override
480 {
481 if( aValue == INDETERMINATE_STATE )
482 return;
483
484 wxString value = aValue;
485
486 switch( aCol )
487 {
488 case COL_NUMBER_SIZE:
489 case COL_NAME_SIZE:
490 case COL_LENGTH:
491 case COL_POSX:
492 case COL_POSY:
493 m_eval->SetDefaultUnits( m_frame->GetUserUnits() );
494
495 if( m_eval->Process( value ) )
496 {
497 m_evalOriginal[ { m_rows[ aRow ], aCol } ] = value;
498 value = m_eval->Result();
499 }
500
501 break;
502
503 default:
504 break;
505 }
506
507 std::vector<SCH_PIN*> pins = m_rows[ aRow ];
508
509 // If the NUMBER column is edited and the pins are grouped, renumber, and add or
510 // remove pins based on the comma separated list of pins.
511 if( aCol == COL_NUMBER && m_pinTable->IsDisplayGrouped() )
512 {
513 wxStringTokenizer tokenizer( value, "," );
514 size_t i = 0;
515
516 while( tokenizer.HasMoreTokens() )
517 {
518 wxString pinName = tokenizer.GetNextToken();
519
520 // Trim whitespace from both ends of the string
521 pinName.Trim( true ).Trim( false );
522
523 if( i < pins.size() )
524 {
525 // Renumber the existing pins
526 pins.at( i )->SetNumber( pinName );
527 }
528 else
529 {
530 // Create new pins
531 SCH_PIN* newPin = new SCH_PIN( this->m_symbol );
532 SCH_PIN* last = pins.back();
533
534 newPin->SetNumber( pinName );
535 newPin->SetName( last->GetName() );
536 newPin->SetOrientation( last->GetOrientation() );
537 newPin->SetType( last->GetType() );
538 newPin->SetShape( last->GetShape() );
539 newPin->SetUnit( last->GetUnit() );
540
541 VECTOR2I pos = last->GetPosition();
542
544 {
547 {
548 pos.y -= schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
549 }
550 else
551 {
552 pos.x += schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
553 }
554 }
555
556 newPin->SetPosition( pos );
557
558 pins.push_back( newPin );
559 m_pinTable->AddPin( newPin );
560 }
561
562 i++;
563 }
564
565 while( pins.size() > i )
566 {
567 m_pinTable->RemovePin( pins.back() );
568 pins.pop_back();
569 }
570
571 m_rows[aRow] = pins;
572 m_edited = true;
573
574 return;
575 }
576
577 NULL_REPORTER reporter;
579
580 for( SCH_PIN* pin : pins )
581 {
582 formatter.UpdatePin( *pin, value, aCol, *m_symbol );
583 }
584
585 m_edited = true;
586 }
587
588 static int findRow( const std::vector<std::vector<SCH_PIN*>>& aRowSet, const wxString& aName )
589 {
590 for( size_t i = 0; i < aRowSet.size(); ++i )
591 {
592 if( aRowSet[ i ][ 0 ] && aRowSet[ i ][ 0 ]->GetName() == aName )
593 return i;
594 }
595
596 return -1;
597 }
598
599 static bool compare( const std::vector<SCH_PIN*>& lhs, const std::vector<SCH_PIN*>& rhs,
600 int sortCol, bool ascending, EDA_DRAW_FRAME* parentFrame )
601 {
602 wxString lhStr = GetValue( lhs, sortCol, parentFrame );
603 wxString rhStr = GetValue( rhs, sortCol, parentFrame );
604
605 if( lhStr == rhStr )
606 {
607 // Secondary sort key is always COL_NUMBER
608 sortCol = COL_NUMBER;
609 lhStr = GetValue( lhs, sortCol, parentFrame );
610 rhStr = GetValue( rhs, sortCol, parentFrame );
611 }
612
613 bool res;
614
615 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
616 // to get the opposite sort. i.e. ~(a<b) != (a>b)
617 auto cmp =
618 [ 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
633 case COL_NUMBER_SIZE:
634 case COL_NAME_SIZE:
635 res = cmp( parentFrame->ValueFromString( lhStr ), parentFrame->ValueFromString( rhStr ) );
636 break;
637
638 case COL_LENGTH:
639 case COL_POSX:
640 case COL_POSY:
641 res = cmp( parentFrame->ValueFromString( lhStr ), parentFrame->ValueFromString( rhStr ) );
642 break;
643
644 case COL_VISIBLE:
645 case COL_UNIT:
646 case COL_BODY_STYLE:
647 default:
648 res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
649 break;
650 }
651
652 return res;
653 }
654
655 void RebuildRows( const std::vector<SCH_PIN*>& aPins, bool groupByName, bool groupBySelection )
656 {
657 WX_GRID* grid = dynamic_cast<WX_GRID*>( GetView() );
658 std::vector<SCH_PIN*> clear_flags;
659
660 clear_flags.reserve( aPins.size() );
661
662 if( grid )
663 {
664 if( groupBySelection )
665 {
666 for( SCH_PIN* pin : aPins )
667 pin->ClearTempFlags();
668
669 int firstSelectedRow;
670 int selectedRowCount;
671
672 getSelectedArea( grid, &firstSelectedRow, &selectedRowCount );
673
674 for( int ii = 0; ii < selectedRowCount; ++ii )
675 {
676 for( SCH_PIN* pin : m_rows[ firstSelectedRow + ii ] )
677 {
678 pin->SetFlags( CANDIDATE );
679 clear_flags.push_back( pin );
680 }
681 }
682 }
683
684 // Commit any pending in-place edits before the row gets moved out from under
685 // the editor.
686 grid->CommitPendingChanges( true );
687
688 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
689 GetView()->ProcessTableMessage( msg );
690 }
691
692 m_rows.clear();
693
694 if( groupBySelection )
695 m_rows.emplace_back( std::vector<SCH_PIN*>() );
696
697 std::set<wxString> selectedNumbers;
699 {
700 selectedNumbers.insert( pin->GetNumber() );
701 }
702
703 const auto pinIsInEditorSelection = [&]( SCH_PIN* pin )
704 {
705 // Quick check before we iterate the whole thing in N^2 time.
706 // (3000^2 = FPGAs causing issues down the road).
707 if( selectedNumbers.count( pin->GetNumber() ) == 0 )
708 {
709 return false;
710 }
711
712 for( SCH_PIN* selectedPin : m_origSelectedPins )
713 {
714 // The selected pin is in the editor, but the pins in the table
715 // are copies. We will mark the pin as selected if it's a match
716 // on the critical items.
717 if( selectedPin->GetNumber() == pin->GetNumber()
718 && selectedPin->GetName() == pin->GetName()
719 && selectedPin->GetUnit() == pin->GetUnit()
720 && selectedPin->GetBodyStyle() == pin->GetBodyStyle()
721 )
722 {
723 return true;
724 }
725 }
726
727 return false;
728 };
729
730
731 for( SCH_PIN* pin : aPins )
732 {
733 const bool includedByUnit = m_unitFilter == -1 || pin->GetUnit() == 0 || pin->GetUnit() == m_unitFilter;
734 const bool includedByBodyStyle = 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 ) );
1030 m_reporter.Report( msg, RPT_SEVERITY_ERROR );
1031 }
1032
1033 return colOrder;
1034 }
1035
1038};
1039
1040
1042 const std::vector<SCH_PIN*>& aSelectedPins ) :
1044 m_editFrame( parent ),
1045 m_symbol( aSymbol )
1046{
1047 m_dataModel = new PIN_TABLE_DATA_MODEL( m_editFrame, this, this->m_symbol, aSelectedPins );
1048
1049 // Save original columns widths so we can do proportional sizing.
1050 for( int i = 0; i < COL_COUNT; ++i )
1051 m_originalColWidths[ i ] = m_grid->GetColSize( i );
1052
1053 m_grid->SetTable( m_dataModel );
1054 m_grid->PushEventHandler( new GRID_TRICKS( m_grid, [this]( wxCommandEvent& aEvent )
1055 {
1056 OnAddRow( aEvent );
1057 } ) );
1058 m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
1059
1060 // Show/hide columns according to the user's preference
1061 if( SYMBOL_EDITOR_SETTINGS* cfg = parent->GetSettings() )
1062 {
1063 m_grid->ShowHideColumns( cfg->m_PinTableVisibleColumns );
1064 m_columnsShown = m_grid->GetShownColumns();
1065 }
1066
1067 // Set special attributes
1068 wxGridCellAttr* attr;
1069
1070 attr = new wxGridCellAttr;
1071 attr->SetReadOnly( true );
1072 m_grid->SetColAttr( COL_PIN_COUNT, attr );
1073
1074 attr = new wxGridCellAttr;
1075 wxArrayString typeNames = PinTypeNames();
1076 typeNames.push_back( INDETERMINATE_STATE );
1077 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinTypeIcons(), typeNames ) );
1078 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinTypeIcons(), typeNames ) );
1079 m_grid->SetColAttr( COL_TYPE, attr );
1080
1081 attr = new wxGridCellAttr;
1082 wxArrayString shapeNames = PinShapeNames();
1083 shapeNames.push_back( INDETERMINATE_STATE );
1084 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinShapeIcons(), shapeNames ) );
1085 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinShapeIcons(), shapeNames ) );
1086 m_grid->SetColAttr( COL_SHAPE, attr );
1087
1088 attr = new wxGridCellAttr;
1089 wxArrayString orientationNames = PinOrientationNames();
1090 orientationNames.push_back( INDETERMINATE_STATE );
1091 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinOrientationIcons(), orientationNames ) );
1092 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinOrientationIcons(), orientationNames ) );
1093 m_grid->SetColAttr( COL_ORIENTATION, attr );
1094
1095 attr = new wxGridCellAttr;
1096 wxArrayString unitNames;
1097 unitNames.push_back( wxGetTranslation( UNITS_ALL ) );
1098
1099 for( int i = 1; i <= aSymbol->GetUnitCount(); i++ )
1100 unitNames.push_back( LIB_SYMBOL::LetterSubReference( i, 'A' ) );
1101
1102 attr->SetEditor( new GRID_CELL_COMBOBOX( unitNames ) );
1103 m_grid->SetColAttr( COL_UNIT, attr );
1104
1105 attr = new wxGridCellAttr;
1106 wxArrayString bodyStyleNames;
1107
1108 bodyStyleNames.push_back( wxGetTranslation( DEMORGAN_ALL ) );
1109
1110 for( const wxString& body_style_name : aSymbol->GetBodyStyleNames() )
1111 bodyStyleNames.push_back( body_style_name );
1112
1113 attr->SetEditor( new GRID_CELL_COMBOBOX( bodyStyleNames ) );
1114 m_grid->SetColAttr( COL_BODY_STYLE, attr );
1115
1116 attr = new wxGridCellAttr;
1117 attr->SetRenderer( new wxGridCellBoolRenderer() );
1118 attr->SetEditor( new wxGridCellBoolEditor() );
1119 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1120 m_grid->SetColAttr( COL_VISIBLE, attr );
1121
1122 /* Right-aligned position values look much better, but only MSW and GTK2+
1123 * currently support right-aligned textEditCtrls, so the text jumps on all
1124 * the other platforms when you edit it.
1125 attr = new wxGridCellAttr;
1126 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
1127 m_grid->SetColAttr( COL_POSX, attr );
1128
1129 attr = new wxGridCellAttr;
1130 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
1131 m_grid->SetColAttr( COL_POSY, attr );
1132 */
1133
1137
1138 m_divider1->SetIsSeparator();
1139
1140 GetSizer()->SetSizeHints(this);
1141 Centre();
1142
1143 if( aSymbol->IsMultiUnit() )
1144 {
1145 m_unitFilter->Append( wxGetTranslation( UNITS_ALL ) );
1146
1147 for( int ii = 0; ii < aSymbol->GetUnitCount(); ++ii )
1148 m_unitFilter->Append( LIB_SYMBOL::LetterSubReference( ii + 1, 'A' ) );
1149
1150 m_unitFilter->SetSelection( -1 );
1151 }
1152 else
1153 {
1154 m_cbFilterByUnit->Enable( false );
1155 m_unitFilter->Enable( false );
1156 }
1157
1158 if( aSymbol->HasDeMorganBodyStyles() )
1159 {
1160 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALL ) );
1161 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_STD ) );
1162 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALT ) );
1163
1164 m_bodyStyleFilter->SetSelection( -1 );
1165 }
1166 else if( aSymbol->IsMultiBodyStyle() )
1167 {
1168 m_bodyStyleFilter->Append( wxGetTranslation( DEMORGAN_ALL ) );
1169
1170 for( const wxString& bodyStyle : aSymbol->GetBodyStyleNames() )
1171 m_bodyStyleFilter->Append( bodyStyle );
1172
1173 m_bodyStyleFilter->SetSelection( -1 );
1174 }
1175 else
1176 {
1177 m_cbFilterByBodyStyle->Enable( false );
1178 m_bodyStyleFilter->Enable( false );
1179 }
1180
1182
1183 if( !parent->IsSymbolEditable() || parent->IsSymbolAlias() )
1184 {
1185 m_ButtonsCancel->SetDefault();
1186 m_ButtonsOK->SetLabel( _( "Read Only" ) );
1187 m_ButtonsOK->Enable( false );
1188 }
1189
1190 m_initialized = true;
1191 m_modified = false;
1192
1193 // Connect Events
1194 m_grid->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr,
1195 this );
1196}
1197
1198
1200{
1201 if( SYMBOL_EDITOR_SETTINGS* cfg = m_editFrame->GetSettings() )
1202 cfg->m_PinTableVisibleColumns = m_grid->GetShownColumnsAsString();
1203
1204 // Disconnect Events
1205 m_grid->Disconnect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr,
1206 this );
1207
1208 // Prevents crash bug in wxGrid's d'tor
1209 m_grid->DestroyTable( m_dataModel );
1210
1211 // Delete the GRID_TRICKS.
1212 m_grid->PopEventHandler( true );
1213
1214 // This is our copy of the pins. If they were transferred to the part on an OK, then
1215 // m_pins will already be empty.
1216 for( SCH_PIN* pin : m_pins )
1217 delete pin;
1218
1219 WINDOW_THAWER thawer( m_editFrame );
1220
1221 m_editFrame->ClearFocus();
1222 m_editFrame->GetCanvas()->Refresh();
1223}
1224
1225
1227{
1228 // Make a copy of the pins for editing
1229 std::vector<SCH_PIN*> pins = m_symbol->GetPins();
1230
1231 for( SCH_PIN* pin : pins )
1232 m_pins.push_back( new SCH_PIN( *pin ) );
1233
1234 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
1235
1236 if( m_symbol->IsMultiUnit() )
1237 m_grid->ShowCol( COL_UNIT );
1238 else
1239 m_grid->HideCol( COL_UNIT );
1240
1241 if( m_symbol->IsMultiBodyStyle() )
1242 m_grid->ShowCol( COL_BODY_STYLE );
1243 else
1244 m_grid->HideCol( COL_BODY_STYLE );
1245
1246 updateSummary();
1247
1248 return true;
1249}
1250
1251
1253{
1254 if( !m_grid->CommitPendingChanges() )
1255 return false;
1256
1257 // Delete the part's pins
1258 std::vector<SCH_PIN*> pins = m_symbol->GetPins();
1259
1260 for( SCH_PIN* pin : pins )
1261 m_symbol->RemoveDrawItem( pin );
1262
1263 // Transfer our pins to the part
1264 for( SCH_PIN* pin : m_pins )
1265 m_symbol->AddDrawItem( pin );
1266
1267 m_pins.clear();
1268
1269 return true;
1270}
1271
1272
1273void DIALOG_LIB_EDIT_PIN_TABLE::OnColSort( wxGridEvent& aEvent )
1274{
1275 int sortCol = aEvent.GetCol();
1276 bool ascending;
1277
1278 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
1279 // event, and if we ask it will give us pre-event info.
1280 if( m_grid->IsSortingBy( sortCol ) )
1281 // same column; invert ascending
1282 ascending = !m_grid->IsSortOrderAscending();
1283 else
1284 // different column; start with ascending
1285 ascending = true;
1286
1287 m_dataModel->SortRows( sortCol, ascending );
1288}
1289
1290
1291void DIALOG_LIB_EDIT_PIN_TABLE::OnAddRow( wxCommandEvent& event )
1292{
1293 m_grid->OnAddRow(
1294 [&]() -> std::pair<int, int>
1295 {
1296 SCH_PIN* newPin = new SCH_PIN( this->m_symbol );
1297
1298 // Copy the settings of the last pin onto the new pin.
1299 if( m_pins.size() > 0 )
1300 {
1301 SCH_PIN* last = m_pins.back();
1302
1303 newPin->SetOrientation( last->GetOrientation() );
1304 newPin->SetType( last->GetType() );
1305 newPin->SetShape( last->GetShape() );
1306 newPin->SetUnit( last->GetUnit() );
1307
1308 VECTOR2I pos = last->GetPosition();
1309
1310 SYMBOL_EDITOR_SETTINGS* cfg = m_editFrame->GetSettings();
1311
1314 {
1315 pos.y -= schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
1316 }
1317 else
1318 {
1319 pos.x += schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
1320 }
1321
1322 newPin->SetPosition( pos );
1323 }
1324
1325 m_pins.push_back( newPin );
1326
1327 m_dataModel->AppendRow( m_pins[ m_pins.size() - 1 ] );
1328 updateSummary();
1329
1330 return { m_dataModel->GetNumberRows() - 1, COL_NUMBER };
1331 } );
1332}
1333
1334
1336{
1337 m_pins.push_back( pin );
1338 updateSummary();
1339}
1340
1341
1342void DIALOG_LIB_EDIT_PIN_TABLE::OnDeleteRow( wxCommandEvent& event )
1343{
1344 m_grid->OnDeleteRows(
1345 [&]( int row )
1346 {
1347 std::vector<SCH_PIN*> removedRow = m_dataModel->RemoveRow( row );
1348
1349 for( SCH_PIN* pin : removedRow )
1350 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
1351 } );
1352
1353 updateSummary();
1354}
1355
1356
1358{
1359 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
1360 updateSummary();
1361}
1362
1363
1365{
1366 updateSummary();
1367}
1368
1369
1371{
1372 SCH_PIN* pin = nullptr;
1373
1374 if( event.GetRow() >= 0 && event.GetRow() < m_dataModel->GetNumberRows() )
1375 {
1376 const std::vector<SCH_PIN*>& pins = m_dataModel->GetRowPins( event.GetRow() );
1377
1378 if( pins.size() == 1 && m_editFrame->GetCurSymbol() )
1379 {
1380 for( SCH_PIN* candidate : m_editFrame->GetCurSymbol()->GetPins() )
1381 {
1382 if( candidate->GetNumber() == pins.at( 0 )->GetNumber() )
1383 {
1384 pin = candidate;
1385 break;
1386 }
1387 }
1388 }
1389 }
1390
1391 WINDOW_THAWER thawer( m_editFrame );
1392
1393 m_editFrame->FocusOnItem( pin );
1394 m_editFrame->GetCanvas()->Refresh();
1395}
1396
1397
1399{
1400 return m_cbGroup->GetValue();
1401}
1402
1403
1405{
1406 m_cbGroup->SetValue( false );
1407
1408 m_dataModel->RebuildRows( m_pins, false, true );
1409
1410 m_grid->ShowCol( COL_PIN_COUNT );
1411 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1412
1414}
1415
1416
1418{
1419 if( !m_grid->CommitPendingChanges() )
1420 return;
1421
1422 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
1423
1424 if( m_cbGroup->GetValue() )
1425 {
1426 m_grid->ShowCol( COL_PIN_COUNT );
1427 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1428 }
1429
1431}
1432
1433
1435{
1436 if( event.GetEventObject() == m_cbFilterByUnit )
1437 {
1438 if( event.IsChecked() )
1439 {
1440 if( m_unitFilter->GetSelection() == -1 )
1441 m_unitFilter->SetSelection( 0 );
1442
1443 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1444 }
1445 else
1446 {
1447 m_dataModel->SetUnitFilter( -1 );
1448 m_unitFilter->SetSelection( -1 );
1449 }
1450 }
1451 else if( event.GetEventObject() == m_cbFilterByBodyStyle )
1452 {
1453 if( event.IsChecked() )
1454 {
1455 if( m_bodyStyleFilter->GetSelection() == -1 )
1456 m_bodyStyleFilter->SetSelection( 0 );
1457
1458 m_dataModel->SetBodyStyleFilter( m_bodyStyleFilter->GetSelection() );
1459 }
1460 else
1461 {
1462 m_dataModel->SetBodyStyleFilter( -1 );
1463 m_bodyStyleFilter->SetSelection( -1 );
1464 }
1465 }
1466 else if( event.GetEventObject() == m_cbFilterSelected )
1467 {
1468 m_dataModel->SetFilterBySelection( event.IsChecked() );
1469 }
1470
1471 OnRebuildRows( event );
1472}
1473
1474
1476{
1477 if( event.GetEventObject() == m_unitFilter )
1478 {
1479 m_cbFilterByUnit->SetValue( true );
1480 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1481 }
1482 else if( event.GetEventObject() == m_bodyStyleFilter )
1483 {
1484 m_cbFilterByBodyStyle->SetValue( true );
1485 m_dataModel->SetBodyStyleFilter( m_bodyStyleFilter->GetSelection() );
1486 }
1487
1488 OnRebuildRows( event );
1489}
1490
1491
1493{
1494 bool fromFile = event.GetEventObject() == m_btnImportFromFile;
1495 bool replaceAll = m_rbReplaceAll->GetValue();
1496
1497 WX_STRING_REPORTER reporter;
1498 PIN_TABLE_IMPORT importer( *m_editFrame, reporter );
1499
1500 std::vector<std::unique_ptr<SCH_PIN>> newPins = importer.ImportData( fromFile, *m_symbol );
1501
1502 if( reporter.HasMessage() )
1503 {
1504 int ret = wxMessageBox( reporter.GetMessages(), _( "Errors" ), wxOK | wxCANCEL | wxICON_ERROR, this );
1505
1506 // Give the user the option to cancel the import on errors.
1507 if( ret == wxCANCEL )
1508 return;
1509 }
1510
1511 if( !newPins.size() )
1512 return;
1513
1514 if( replaceAll )
1515 {
1516 // This is quite a dance with a segfault without smart pointers
1517 for( SCH_PIN* pin : m_pins )
1518 delete pin;
1519
1520 m_pins.clear();
1521 }
1522
1523 for( auto& newPin : newPins )
1524 m_pins.push_back( newPin.release() );
1525
1526 m_cbGroup->SetValue( false );
1527 m_dataModel->RebuildRows( m_pins, false, false );
1528
1529 updateSummary();
1530}
1531
1532
1534{
1535 bool toFile = event.GetEventObject() == m_btnExportToFile;
1536 bool onlyShown = m_rbExportOnlyShownPins->GetValue();
1537
1538 wxString filePath = wxEmptyString;
1539
1540 if( toFile )
1541 {
1542 wxFileName fn( m_symbol->GetName() );
1543 fn.SetExt( FILEEXT::CsvFileExtension );
1544 wxFileDialog dlg( this, _( "Select pin data file" ), "", fn.GetFullName(), FILEEXT::CsvFileWildcard(),
1545 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1546
1547 if( dlg.ShowModal() == wxID_CANCEL )
1548 return;
1549
1550 filePath = dlg.GetPath();
1551 }
1552
1553 std::vector<SCH_PIN*> pinsToExport;
1554
1555 if( onlyShown )
1556 {
1557 for( int i = 0; i < m_dataModel->GetNumberRows(); ++i )
1558 {
1559 for( SCH_PIN* pin : m_dataModel->GetRowPins( i ) )
1560 pinsToExport.push_back( pin );
1561 }
1562 }
1563 else
1564 {
1565 pinsToExport = m_pins;
1566 }
1567
1568 PIN_TABLE_EXPORT exporter( *m_editFrame );
1569 exporter.ExportData( pinsToExport, filePath );
1570}
1571
1572
1574{
1575 // Account for scroll bars
1577
1578 wxGridUpdateLocker deferRepaintsTillLeavingScope;
1579
1580 // The Number and Name columns must be at least wide enough to hold their contents, but
1581 // no less wide than their original widths.
1582 m_grid->AutoSizeColumn( COL_NUMBER );
1583
1584 if( m_grid->GetColSize( COL_NUMBER ) < m_originalColWidths[ COL_NUMBER ] )
1586
1587 m_grid->AutoSizeColumn( COL_NAME );
1588
1589 if( m_grid->GetColSize( COL_NAME ) < m_originalColWidths[ COL_NAME ] )
1590 m_grid->SetColSize( COL_NAME, m_originalColWidths[ COL_NAME ] );
1591
1592 // If the grid is still wider than the columns, then stretch the Number and Name columns
1593 // to fit.
1594 for( int i = 0; i < COL_COUNT; ++i )
1595 width -= m_grid->GetColSize( i );
1596
1597 if( width > 0 )
1598 {
1599 m_grid->SetColSize( COL_NUMBER, m_grid->GetColSize( COL_NUMBER ) + width / 2 );
1600 m_grid->SetColSize( COL_NAME, m_grid->GetColSize( COL_NAME ) + width / 2 );
1601 }
1602}
1603
1604
1605void DIALOG_LIB_EDIT_PIN_TABLE::OnSize( wxSizeEvent& event )
1606{
1607 wxSize new_size = event.GetSize();
1608
1609 if( m_initialized && m_size != new_size )
1610 {
1611 m_size = new_size;
1612
1614 }
1615
1616 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1617 event.Skip();
1618}
1619
1620
1621void DIALOG_LIB_EDIT_PIN_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
1622{
1623 std::bitset<64> columnsShown = m_grid->GetShownColumns();
1624
1625 if( columnsShown != m_columnsShown )
1626 {
1627 m_columnsShown = columnsShown;
1628
1629 if( !m_grid->IsCellEditControlShown() )
1631 }
1632
1633 int firstSelectedRow;
1634 int selectedRowCount;
1635
1636 getSelectedArea( m_grid, &firstSelectedRow, &selectedRowCount );
1637
1638 if( ( selectedRowCount > 1 ) != m_groupSelected->IsEnabled() )
1639 m_groupSelected->Enable( selectedRowCount > 1 );
1640}
1641
1642
1643void DIALOG_LIB_EDIT_PIN_TABLE::OnCancel( wxCommandEvent& event )
1644{
1645 Close();
1646}
1647
1648
1649void DIALOG_LIB_EDIT_PIN_TABLE::OnClose( wxCloseEvent& event )
1650{
1651 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1652 m_grid->CommitPendingChanges( true );
1653
1654 int retval = wxID_CANCEL;
1655
1656 if( m_dataModel->IsEdited() )
1657 {
1658 if( HandleUnsavedChanges( this, _( "Save changes?" ),
1659 [&]() -> bool
1660 {
1662 {
1663 retval = wxID_OK;
1664 return true;
1665 }
1666
1667 return false;
1668 } ) )
1669 {
1670 if( IsQuasiModal() )
1671 EndQuasiModal( retval );
1672 else
1673 EndDialog( retval );
1674
1675 return;
1676 }
1677 else
1678 {
1679 event.Veto();
1680 return;
1681 }
1682 }
1683
1684 // No change in dialog: we can close it
1685 if( IsQuasiModal() )
1686 EndQuasiModal( retval );
1687 else
1688 EndDialog( retval );
1689}
1690
1691
1693{
1694 PIN_NUMBERS pinNumbers;
1695
1696 for( SCH_PIN* pin : m_pins )
1697 {
1698 if( pin->GetNumber().Length() )
1699 pinNumbers.insert( pin->GetNumber() );
1700 }
1701
1702 m_pin_numbers_summary->SetLabel( pinNumbers.GetSummary() );
1703 m_pin_count->SetLabel( wxString::Format( wxT( "%u" ), (unsigned) m_pins.size() ) );
1704 m_duplicate_pins->SetLabel( pinNumbers.GetDuplicates() );
1705
1706 Layout();
1707}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
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 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:85
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:594
static wxString LetterSubReference(int aUnit, wxChar aInitialLetter)
const std::vector< wxString > & GetBodyStyleNames() const
Definition lib_symbol.h:607
bool HasDeMorganBodyStyles() const override
Definition lib_symbol.h:604
bool IsMultiUnit() const override
Definition lib_symbol.h:590
int GetBodyStyleCount() const override
Definition lib_symbol.h:596
int GetUnitCount() const override
A singleton reporter that reports to nowhere.
Definition reporter.h:216
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
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:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
virtual bool HasMessage() const
Returns true if any messages were reported.
Definition reporter.h:134
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:243
virtual wxString GetBodyStyleDescription(int aBodyStyle, bool aLabel) const
Definition sch_item.cpp:179
int GetBodyStyle() const
Definition sch_item.h:244
virtual wxString GetUnitDisplayName(int aUnit, bool aLabel) const
Definition sch_item.cpp:168
int GetUnit() const
Definition sch_item.h:238
virtual void SetUnit(int aUnit)
Definition sch_item.h:237
int GetNumberTextSize() const
Definition sch_pin.cpp:670
int GetLength() const
Definition sch_pin.cpp:298
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:633
void SetVisible(bool aVisible)
Definition sch_pin.h:114
void ChangeLength(int aLength)
Change the length of a pin and adjust its position based on orientation.
Definition sch_pin.cpp:1193
void SetOrientation(PIN_ORIENTATION aOrientation)
Definition sch_pin.h:93
void SetName(const wxString &aName)
Definition sch_pin.cpp:418
bool IsVisible() const
Definition sch_pin.cpp:386
void SetPosition(const VECTOR2I &aPos) override
Definition sch_pin.h:238
const wxString & GetName() const
Definition sch_pin.cpp:400
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:263
void SetNumberTextSize(int aSize)
Definition sch_pin.cpp:684
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:96
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:255
int GetNameTextSize() const
Definition sch_pin.cpp:646
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:332
const wxString & GetNumber() const
Definition sch_pin.h:124
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:277
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:312
void SetNameTextSize(int aSize)
Definition sch_pin.cpp:660
The symbol library editor main window.
bool IsSymbolAlias() const
Return true if aLibId is an alias for the editor screen symbol.
SYMBOL_EDITOR_SETTINGS * GetSettings() const
bool IsSymbolEditable() const
Test if a symbol is loaded and can be edited.
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:191
const wxString & GetMessages() const
Definition reporter.cpp:77
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: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
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:264
#define _HKI(x)
Definition page_info.cpp:44
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
@ 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: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
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
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.