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 (C) 1992-2022 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#include "grid_tricks.h"
26#include "lib_pin.h"
27#include "pin_numbers.h"
28#include "pgm_base.h"
29#include <base_units.h>
30#include <bitmaps.h>
31#include <confirm.h>
32#include <symbol_edit_frame.h>
34#include <kiplatform/ui.h>
37#include <widgets/wx_grid.h>
41#include <wx/tokenzr.h>
42#include <string_utils.h>
43
44#define UNITS_ALL _( "ALL" )
45#define DEMORGAN_ALL _( "ALL" )
46#define DEMORGAN_STD _( "Standard" )
47#define DEMORGAN_ALT _( "Alternate" )
48
49
50void getSelectedArea( WX_GRID* aGrid, int* aRowStart, int* aRowCount )
51{
52 wxGridCellCoordsArray topLeft = aGrid->GetSelectionBlockTopLeft();
53 wxGridCellCoordsArray botRight = aGrid->GetSelectionBlockBottomRight();
54
55 wxArrayInt cols = aGrid->GetSelectedCols();
56 wxArrayInt rows = aGrid->GetSelectedRows();
57
58 if( topLeft.Count() && botRight.Count() )
59 {
60 *aRowStart = topLeft[0].GetRow();
61 *aRowCount = botRight[0].GetRow() - *aRowStart + 1;
62 }
63 else if( cols.Count() )
64 {
65 *aRowStart = 0;
66 *aRowCount = aGrid->GetNumberRows();
67 }
68 else if( rows.Count() )
69 {
70 *aRowStart = rows[0];
71 *aRowCount = rows.Count();
72 }
73 else
74 {
75 *aRowStart = aGrid->GetGridCursorRow();
76 *aRowCount = *aRowStart >= 0 ? 1 : 0;
77 }
78}
79
80
81class PIN_TABLE_DATA_MODEL : public wxGridTableBase
82{
83public:
85 LIB_SYMBOL* aSymbol ) :
86 m_frame( aFrame ),
87 m_unitFilter( -1 ),
88 m_edited( false ),
89 m_pinTable( aPinTable ),
90 m_symbol( aSymbol )
91 {
92 m_eval = std::make_unique<NUMERIC_EVALUATOR>( m_frame->GetUserUnits() );
93
94 m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
95 }
96
98 {
99 m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
100 }
101
102 void onUnitsChanged( wxCommandEvent& aEvent )
103 {
104 if( GetView() )
105 GetView()->ForceRefresh();
106
107 aEvent.Skip();
108 }
109
110 void SetUnitFilter( int aFilter ) { m_unitFilter = aFilter; }
111
112 int GetNumberRows() override { return (int) m_rows.size(); }
113 int GetNumberCols() override { return COL_COUNT; }
114
115 wxString GetColLabelValue( int aCol ) override
116 {
117 switch( aCol )
118 {
119 case COL_PIN_COUNT: return _( "Count" );
120 case COL_NUMBER: return _( "Number" );
121 case COL_NAME: return _( "Name" );
122 case COL_TYPE: return _( "Electrical Type" );
123 case COL_SHAPE: return _( "Graphic Style" );
124 case COL_ORIENTATION: return _( "Orientation" );
125 case COL_NUMBER_SIZE: return _( "Number Text Size" );
126 case COL_NAME_SIZE: return _( "Name Text Size" );
127 case COL_LENGTH: return _( "Length" );
128 case COL_POSX: return _( "X Position" );
129 case COL_POSY: return _( "Y Position" );
130 case COL_VISIBLE: return _( "Visible" );
131 case COL_UNIT: return _( "Unit" );
132 case COL_DEMORGAN: return _( "De Morgan" );
133 default: wxFAIL; return wxEmptyString;
134 }
135 }
136
137 bool IsEmptyCell( int row, int col ) override
138 {
139 return false; // don't allow adjacent cell overflow, even if we are actually empty
140 }
141
142 wxString GetValue( int aRow, int aCol ) override
143 {
144 wxGrid* grid = GetView();
145
146 if( grid->GetGridCursorRow() == aRow && grid->GetGridCursorCol() == aCol
147 && grid->IsCellEditControlShown() )
148 {
149 auto it = m_evalOriginal.find( { m_rows[ aRow ], aCol } );
150
151 if( it != m_evalOriginal.end() )
152 return it->second;
153 }
154
155 return GetValue( m_rows[ aRow ], aCol, m_frame );
156 }
157
158 static wxString GetValue( const LIB_PINS& pins, int aCol, EDA_DRAW_FRAME* aParentFrame )
159 {
160 wxString fieldValue;
161
162 if( pins.empty() )
163 return fieldValue;
164
165 for( LIB_PIN* pin : pins )
166 {
167 wxString val;
168
169 switch( aCol )
170 {
171 case COL_PIN_COUNT:
172 val << pins.size();
173 break;
174
175 case COL_NUMBER:
176 val = pin->GetNumber();
177 break;
178
179 case COL_NAME:
180 val = pin->GetName();
181 break;
182
183 case COL_TYPE:
184 val = PinTypeNames()[static_cast<int>( pin->GetType() )];
185 break;
186
187 case COL_SHAPE:
188 val = PinShapeNames()[static_cast<int>( pin->GetShape() )];
189 break;
190
191 case COL_ORIENTATION:
192 if( PinOrientationIndex( pin->GetOrientation() ) >= 0 )
193 val = PinOrientationNames()[ PinOrientationIndex( pin->GetOrientation() ) ];
194
195 break;
196
197 case COL_NUMBER_SIZE:
198 val = aParentFrame->StringFromValue( pin->GetNumberTextSize(), true );
199 break;
200
201 case COL_NAME_SIZE:
202 val = aParentFrame->StringFromValue( pin->GetNameTextSize(), true );
203 break;
204
205 case COL_LENGTH:
206 val = aParentFrame->StringFromValue( pin->GetLength(), true );
207 break;
208
209 case COL_POSX:
210 val = aParentFrame->StringFromValue( pin->GetPosition().x, true );
211 break;
212
213 case COL_POSY:
214 val = aParentFrame->StringFromValue( -pin->GetPosition().y, true );
215 break;
216
217 case COL_VISIBLE:
218 val = StringFromBool( pin->IsVisible() );
219 break;
220
221 case COL_UNIT:
222 if( pin->GetUnit() )
223 val = LIB_SYMBOL::SubReference( pin->GetUnit(), false );
224 else
225 val = UNITS_ALL;
226 break;
227
228 case COL_DEMORGAN:
229 switch( pin->GetConvert() )
230 {
232 val = DEMORGAN_STD;
233 break;
235 val = DEMORGAN_ALT;
236 break;
237 default:
238 val = DEMORGAN_ALL;
239 break;
240 }
241 break;
242
243 default:
244 wxFAIL;
245 break;
246 }
247
248 if( aCol == COL_NUMBER )
249 {
250 if( fieldValue.length() )
251 fieldValue += wxT( ", " );
252 fieldValue += val;
253 }
254 else
255 {
256 if( !fieldValue.Length() )
257 fieldValue = val;
258 else if( val != fieldValue )
259 fieldValue = INDETERMINATE_STATE;
260 }
261 }
262
263 return fieldValue;
264 }
265
266 void SetValue( int aRow, int aCol, const wxString &aValue ) override
267 {
268 if( aValue == INDETERMINATE_STATE )
269 return;
270
271 wxString value = aValue;
272
273 switch( aCol )
274 {
275 case COL_NUMBER_SIZE:
276 case COL_NAME_SIZE:
277 case COL_LENGTH:
278 case COL_POSX:
279 case COL_POSY:
280 m_eval->SetDefaultUnits( m_frame->GetUserUnits() );
281
282 if( m_eval->Process( value ) )
283 {
284 m_evalOriginal[ { m_rows[ aRow ], aCol } ] = value;
285 value = m_eval->Result();
286 }
287
288 break;
289
290 default:
291 break;
292 }
293
294 LIB_PINS pins = m_rows[ aRow ];
295
296 // If the NUMBER column is edited and the pins are grouped, renumber, and add or
297 // remove pins based on the comma separated list of pins.
298 if( aCol == COL_NUMBER && m_pinTable->IsDisplayGrouped() )
299 {
300 wxStringTokenizer tokenizer( value, "," );
301 size_t i = 0;
302
303 while( tokenizer.HasMoreTokens() )
304 {
305 wxString pinName = tokenizer.GetNextToken();
306
307 // Trim whitespace from both ends of the string
308 pinName.Trim( true ).Trim( false );
309
310 if( i < pins.size() )
311 {
312 // Renumber the existing pins
313 pins.at( i )->SetNumber( pinName );
314 }
315 else
316 {
317 // Create new pins
318 LIB_PIN* newPin = new LIB_PIN( this->m_symbol );
319 LIB_PIN* last = pins.back();
320
321 newPin->SetNumber( pinName );
322 newPin->SetName( last->GetName() );
323 newPin->SetOrientation( last->GetOrientation() );
324 newPin->SetType( last->GetType() );
325 newPin->SetShape( last->GetShape() );
326 newPin->SetUnit( last->GetUnit() );
327
328 VECTOR2I pos = last->GetPosition();
329
330 auto* cfg = Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
331
332 if( last->GetOrientation() == PIN_LEFT || last->GetOrientation() == PIN_RIGHT )
333 pos.y -= schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
334 else
335 pos.x += schIUScale.MilsToIU( cfg->m_Repeat.pin_step );
336
337 newPin->SetPosition( pos );
338
339 pins.push_back( newPin );
340 m_pinTable->AddPin( newPin );
341 }
342
343 i++;
344 }
345
346 while( pins.size() > i )
347 {
348 m_pinTable->RemovePin( pins.back() );
349 pins.pop_back();
350 }
351
352 m_rows[aRow] = pins;
353 m_edited = true;
354
355 return;
356 }
357
358 for( LIB_PIN* pin : pins )
359 {
360 switch( aCol )
361 {
362 case COL_NUMBER:
364 pin->SetNumber( value );
365
366 break;
367
368 case COL_NAME:
369 pin->SetName( value );
370 break;
371
372 case COL_TYPE:
373 if( PinTypeNames().Index( value ) != wxNOT_FOUND )
374 pin->SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( value ) );
375
376 break;
377
378 case COL_SHAPE:
379 if( PinShapeNames().Index( value ) != wxNOT_FOUND )
380 pin->SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( value ) );
381
382 break;
383
384 case COL_ORIENTATION:
385 if( PinOrientationNames().Index( value ) != wxNOT_FOUND )
386 pin->SetOrientation( PinOrientationCode( PinOrientationNames().Index( value ) ) );
387 break;
388
389 case COL_NUMBER_SIZE:
390 pin->SetNumberTextSize( m_frame->ValueFromString( value ) );
391 break;
392
393 case COL_NAME_SIZE:
394 pin->SetNameTextSize( m_frame->ValueFromString( value ) );
395 break;
396
397 case COL_LENGTH:
398 pin->ChangeLength( m_frame->ValueFromString( value ) );
399 break;
400
401 case COL_POSX:
402 pin->SetPosition( VECTOR2I( m_frame->ValueFromString( value ),
403 pin->GetPosition().y ) );
404 break;
405
406 case COL_POSY:
407 pin->SetPosition( VECTOR2I( pin->GetPosition().x,
408 -m_frame->ValueFromString( value ) ) );
409 break;
410
411 case COL_VISIBLE:
412 pin->SetVisible(BoolFromString( value ));
413 break;
414
415 case COL_UNIT:
416 if( value == UNITS_ALL )
417 {
418 pin->SetUnit( 0 );
419 }
420 else
421 {
422 for( int i = 1; i <= m_symbol->GetUnitCount(); i++ )
423 {
424 if( value == LIB_SYMBOL::SubReference( i, false ) )
425 {
426 pin->SetUnit( i );
427 break;
428 }
429 }
430 }
431 break;
432
433 case COL_DEMORGAN:
434 if( value == DEMORGAN_STD )
435 pin->SetConvert( 1 );
436 else if( value == DEMORGAN_ALT )
437 pin->SetConvert( 2 );
438 else
439 pin->SetConvert( 0 );
440 break;
441
442 default:
443 wxFAIL;
444 break;
445 }
446 }
447
448 m_edited = true;
449 }
450
451 static int findRow( const std::vector<LIB_PINS>& aRowSet, const wxString& aName )
452 {
453 for( size_t i = 0; i < aRowSet.size(); ++i )
454 {
455 if( aRowSet[ i ][ 0 ] && aRowSet[ i ][ 0 ]->GetName() == aName )
456 return i;
457 }
458
459 return -1;
460 }
461
462 static bool compare( const LIB_PINS& lhs, const LIB_PINS& rhs, int sortCol, bool ascending,
463 EDA_DRAW_FRAME* parentFrame )
464 {
465 wxString lhStr = GetValue( lhs, sortCol, parentFrame );
466 wxString rhStr = GetValue( rhs, sortCol, parentFrame );
467
468 if( lhStr == rhStr )
469 {
470 // Secondary sort key is always COL_NUMBER
471 sortCol = COL_NUMBER;
472 lhStr = GetValue( lhs, sortCol, parentFrame );
473 rhStr = GetValue( rhs, sortCol, parentFrame );
474 }
475
476 bool res;
477
478 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
479 // to get the opposite sort. i.e. ~(a<b) != (a>b)
480 auto cmp = [ ascending ]( const auto a, const auto b )
481 {
482 if( ascending )
483 return a < b;
484 else
485 return b < a;
486 };
487
488 switch( sortCol )
489 {
490 case COL_NUMBER:
491 case COL_NAME:
492 res = cmp( PIN_NUMBERS::Compare( lhStr, rhStr ), 0 );
493 break;
494 case COL_NUMBER_SIZE:
495 case COL_NAME_SIZE:
496 res = cmp( parentFrame->ValueFromString( lhStr ),
497 parentFrame->ValueFromString( rhStr ) );
498 break;
499 case COL_LENGTH:
500 case COL_POSX:
501 case COL_POSY:
502 res = cmp( parentFrame->ValueFromString( lhStr ),
503 parentFrame->ValueFromString( rhStr ) );
504 break;
505 case COL_VISIBLE:
506 case COL_DEMORGAN:
507 default:
508 res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
509 break;
510 }
511
512 return res;
513 }
514
515 void RebuildRows( const LIB_PINS& aPins, bool groupByName, bool groupBySelection )
516 {
517 WX_GRID* grid = dynamic_cast<WX_GRID*>( GetView() );
518 std::vector<LIB_PIN*> clear_flags;
519
520 clear_flags.reserve( aPins.size() );
521
522 if( grid )
523 {
524 if( groupBySelection )
525 {
526 for( LIB_PIN* pin : aPins )
527 pin->ClearTempFlags();
528
529 int firstSelectedRow;
530 int selectedRowCount;
531
532 getSelectedArea( grid, &firstSelectedRow, &selectedRowCount );
533
534 for( int ii = 0; ii < selectedRowCount; ++ii )
535 {
536 for( LIB_PIN* pin : m_rows[ firstSelectedRow + ii ] )
537 {
538 pin->SetFlags( CANDIDATE );
539 clear_flags.push_back( pin );
540 }
541 }
542 }
543
544 // Commit any pending in-place edits before the row gets moved out from under
545 // the editor.
546 grid->CommitPendingChanges( true );
547
548 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
549 GetView()->ProcessTableMessage( msg );
550 }
551
552 m_rows.clear();
553
554 if( groupBySelection )
555 m_rows.emplace_back( LIB_PINS() );
556
557 for( LIB_PIN* pin : aPins )
558 {
559 if( m_unitFilter == -1 || pin->GetUnit() == 0 || pin->GetUnit() == m_unitFilter )
560 {
561 int rowIndex = -1;
562
563 if( groupByName )
564 rowIndex = findRow( m_rows, pin->GetName() );
565 else if( groupBySelection && ( pin->GetFlags() & CANDIDATE ) )
566 rowIndex = 0;
567
568 if( rowIndex < 0 )
569 {
570 m_rows.emplace_back( LIB_PINS() );
571 rowIndex = m_rows.size() - 1;
572 }
573
574 m_rows[ rowIndex ].push_back( pin );
575 }
576 }
577
578 int sortCol = 0;
579 bool ascending = true;
580
581 if( GetView() && GetView()->GetSortingColumn() != wxNOT_FOUND )
582 {
583 sortCol = GetView()->GetSortingColumn();
584 ascending = GetView()->IsSortOrderAscending();
585 }
586
587 for( LIB_PINS& row : m_rows )
588 SortPins( row );
589
590 if( !groupBySelection )
591 SortRows( sortCol, ascending );
592
593 if ( GetView() )
594 {
595 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
596 GetView()->ProcessTableMessage( msg );
597
598 if( groupBySelection )
599 GetView()->SelectRow( 0 );
600 }
601
602 for( LIB_PIN* pin : clear_flags )
603 pin->ClearFlags( CANDIDATE );
604 }
605
606 void SortRows( int aSortCol, bool ascending )
607 {
608 std::sort( m_rows.begin(), m_rows.end(),
609 [ aSortCol, ascending, this ]( const LIB_PINS& lhs, const LIB_PINS& rhs ) -> bool
610 {
611 return compare( lhs, rhs, aSortCol, ascending, m_frame );
612 } );
613 }
614
615 void SortPins( LIB_PINS& aRow )
616 {
617 std::sort( aRow.begin(), aRow.end(),
618 []( LIB_PIN* lhs, LIB_PIN* rhs ) -> bool
619 {
620 return PIN_NUMBERS::Compare( lhs->GetNumber(), rhs->GetNumber() ) < 0;
621 } );
622 }
623
624 void AppendRow( LIB_PIN* aPin )
625 {
626 LIB_PINS row;
627 row.push_back( aPin );
628 m_rows.push_back( row );
629
630 if ( GetView() )
631 {
632 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
633 GetView()->ProcessTableMessage( msg );
634 }
635 }
636
637 LIB_PINS RemoveRow( int aRow )
638 {
639 LIB_PINS removedRow = m_rows[ aRow ];
640
641 m_rows.erase( m_rows.begin() + aRow );
642
643 if ( GetView() )
644 {
645 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
646 GetView()->ProcessTableMessage( msg );
647 }
648
649 return removedRow;
650 }
651
652 bool IsEdited()
653 {
654 return m_edited;
655 }
656
657private:
658 static wxString StringFromBool( bool aValue )
659 {
660 if( aValue )
661 return wxT( "1" );
662 else
663 return wxT( "0" );
664 }
665
666 static bool BoolFromString( wxString aValue )
667 {
668 if( aValue == wxS( "1" ) )
669 {
670 return true;
671 }
672 else if( aValue == wxS( "0" ) )
673 {
674 return false;
675 }
676 else
677 {
678 wxFAIL_MSG( wxString::Format( "string '%s' can't be converted to boolean correctly, "
679 "it will have been perceived as FALSE",
680 aValue ) );
681 return false;
682 }
683 }
684
685private:
687
688 // Because the rows of the grid can either be a single pin or a group of pins, the
689 // data model is a 2D vector. If we're in the single pin case, each row's LIB_PINS
690 // contains only a single pin.
691 std::vector<LIB_PINS> m_rows;
692 int m_unitFilter; // 0 to show pins for all units
693
695
697 LIB_SYMBOL* m_symbol; // Parent symbol that the pins belong to.
698
699 std::unique_ptr<NUMERIC_EVALUATOR> m_eval;
700 std::map< std::pair<LIB_PINS, int>, wxString > m_evalOriginal;
701};
702
703
705 LIB_SYMBOL* aSymbol ) :
707 m_editFrame( parent ),
708 m_symbol( aSymbol )
709{
711
712 // Save original columns widths so we can do proportional sizing.
713 for( int i = 0; i < COL_COUNT; ++i )
714 m_originalColWidths[ i ] = m_grid->GetColSize( i );
715
716 // Give a bit more room for combobox editors
717 m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
718
720 m_grid->PushEventHandler( new GRID_TRICKS( m_grid, [this]( wxCommandEvent& aEvent )
721 {
722 OnAddRow( aEvent );
723 } ) );
724
725 // Show/hide columns according to the user's preference
726 if( SYMBOL_EDITOR_SETTINGS* cfg = parent->GetSettings() )
727 {
728 m_grid->ShowHideColumns( cfg->m_PinTableVisibleColumns );
730 }
731
732 // Set special attributes
733 wxGridCellAttr* attr;
734
735 attr = new wxGridCellAttr;
736 attr->SetReadOnly( true );
737 m_grid->SetColAttr( COL_PIN_COUNT, attr );
738
739 attr = new wxGridCellAttr;
740 wxArrayString typeNames = PinTypeNames();
741 typeNames.push_back( INDETERMINATE_STATE );
742 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinTypeIcons(), typeNames ) );
743 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinTypeIcons(), typeNames ) );
744 m_grid->SetColAttr( COL_TYPE, attr );
745
746 attr = new wxGridCellAttr;
747 wxArrayString shapeNames = PinShapeNames();
748 shapeNames.push_back( INDETERMINATE_STATE );
749 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinShapeIcons(), shapeNames ) );
750 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinShapeIcons(), shapeNames ) );
751 m_grid->SetColAttr( COL_SHAPE, attr );
752
753 attr = new wxGridCellAttr;
754 wxArrayString orientationNames = PinOrientationNames();
755 orientationNames.push_back( INDETERMINATE_STATE );
756 attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinOrientationIcons(),
757 orientationNames ) );
758 attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinOrientationIcons(), orientationNames ) );
759 m_grid->SetColAttr( COL_ORIENTATION, attr );
760
761 attr = new wxGridCellAttr;
762 wxArrayString unitNames;
763 unitNames.push_back( UNITS_ALL );
764
765 for( int i = 1; i <= aSymbol->GetUnitCount(); i++ )
766 unitNames.push_back( LIB_SYMBOL::SubReference( i, false ) );
767
768 attr->SetEditor( new GRID_CELL_COMBOBOX( unitNames ) );
769 m_grid->SetColAttr( COL_UNIT, attr );
770
771 attr = new wxGridCellAttr;
772 wxArrayString demorganNames;
773 demorganNames.push_back( DEMORGAN_ALL );
774 demorganNames.push_back( DEMORGAN_STD );
775 demorganNames.push_back( DEMORGAN_ALT );
776 attr->SetEditor( new GRID_CELL_COMBOBOX( demorganNames ) );
777 m_grid->SetColAttr( COL_DEMORGAN, attr );
778
779 attr = new wxGridCellAttr;
780 attr->SetRenderer( new wxGridCellBoolRenderer() );
781 attr->SetEditor( new wxGridCellBoolEditor() );
782 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
783 m_grid->SetColAttr( COL_VISIBLE, attr );
784
785 /* Right-aligned position values look much better, but only MSW and GTK2+
786 * currently support right-aligned textEditCtrls, so the text jumps on all
787 * the other platforms when you edit it.
788 attr = new wxGridCellAttr;
789 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
790 m_grid->SetColAttr( COL_POSX, attr );
791
792 attr = new wxGridCellAttr;
793 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
794 m_grid->SetColAttr( COL_POSY, attr );
795 */
796
797 m_addButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
798 m_deleteButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
799 m_refreshButton->SetBitmap( KiBitmap( BITMAPS::small_refresh ) );
800
803
804 GetSizer()->SetSizeHints(this);
805 Centre();
806
807 if( aSymbol->IsMulti() )
808 {
809 m_unitFilter->Append( UNITS_ALL );
810
811 for( int ii = 0; ii < aSymbol->GetUnitCount(); ++ii )
812 m_unitFilter->Append( aSymbol->GetUnitReference( ii + 1 ) );
813
814 m_unitFilter->SetSelection( -1 );
815 }
816 else
817 {
818 m_cbFilterByUnit->Show( false );
819 m_unitFilter->Show( false );
820 }
821
823
824 if( !parent->IsSymbolEditable() || parent->IsSymbolAlias() )
825 {
826 m_ButtonsCancel->SetDefault();
827 m_ButtonsOK->SetLabel( _( "Read Only" ) );
828 m_ButtonsOK->Enable( false );
829 }
830
831 m_initialized = true;
832 m_modified = false;
833
834 // Connect Events
835 m_grid->Connect( wxEVT_GRID_COL_SORT,
836 wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
837}
838
839
841{
843 cfg->m_PinTableVisibleColumns = m_grid->GetShownColumnsAsString();
844
845 // Disconnect Events
846 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
847 wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
848
849 // Prevents crash bug in wxGrid's d'tor
851
852 // Delete the GRID_TRICKS.
853 m_grid->PopEventHandler( true );
854
855 // This is our copy of the pins. If they were transferred to the part on an OK, then
856 // m_pins will already be empty.
857 for( LIB_PIN* pin : m_pins )
858 delete pin;
859}
860
861
863{
864 // Make a copy of the pins for editing
865 std::vector<LIB_PIN*> pins = m_symbol->GetAllLibPins();
866
867 for( LIB_PIN* pin : pins )
868 m_pins.push_back( new LIB_PIN( *pin ) );
869
870 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
871
872 if( m_symbol->IsMulti() )
873 m_grid->ShowCol( COL_UNIT );
874 else
875 m_grid->HideCol( COL_UNIT );
876
878 m_grid->ShowCol( COL_DEMORGAN );
879 else
880 m_grid->HideCol( COL_DEMORGAN );
881
883
884 return true;
885}
886
887
889{
891 return false;
892
893 // Delete the part's pins
894 std::vector<LIB_PIN*> pins = m_symbol->GetAllLibPins();
895
896 for( LIB_PIN* pin : pins )
898
899 // Transfer our pins to the part
900 for( LIB_PIN* pin : m_pins )
901 {
903 }
904
905 m_pins.clear();
906
907 return true;
908}
909
910
911void DIALOG_LIB_EDIT_PIN_TABLE::OnColSort( wxGridEvent& aEvent )
912{
913 int sortCol = aEvent.GetCol();
914 bool ascending;
915
916 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
917 // event, and if we ask it will give us pre-event info.
918 if( m_grid->IsSortingBy( sortCol ) )
919 // same column; invert ascending
920 ascending = !m_grid->IsSortOrderAscending();
921 else
922 // different column; start with ascending
923 ascending = true;
924
925 m_dataModel->SortRows( sortCol, ascending );
926}
927
928
929void DIALOG_LIB_EDIT_PIN_TABLE::OnAddRow( wxCommandEvent& event )
930{
932 return;
933
934 LIB_PIN* newPin = new LIB_PIN( this->m_symbol );
935
936 // Copy the settings of the last pin onto the new pin.
937 if( m_pins.size() > 0 )
938 {
939 LIB_PIN* last = m_pins.back();
940
941 newPin->SetOrientation( last->GetOrientation() );
942 newPin->SetType( last->GetType() );
943 newPin->SetShape( last->GetShape() );
944 newPin->SetUnit( last->GetUnit() );
945
946 VECTOR2I pos = last->GetPosition();
947
949
950 if( last->GetOrientation() == PIN_LEFT || last->GetOrientation() == PIN_RIGHT )
952 else
954
955 newPin->SetPosition( pos );
956 }
957
958 m_pins.push_back( newPin );
959
960 m_dataModel->AppendRow( m_pins[ m_pins.size() - 1 ] );
961
962 m_grid->MakeCellVisible( m_grid->GetNumberRows() - 1, 1 );
963 m_grid->SetGridCursor( m_grid->GetNumberRows() - 1, 1 );
964
965 m_grid->EnableCellEditControl( true );
966 m_grid->ShowCellEditControl();
967
969}
970
971
973{
974 m_pins.push_back( pin );
976}
977
978
979void DIALOG_LIB_EDIT_PIN_TABLE::OnDeleteRow( wxCommandEvent& event )
980{
981 // TODO: handle delete of multiple rows....
982
984 return;
985
986 if( m_pins.size() == 0 ) // empty table
987 return;
988
989 int curRow = m_grid->GetGridCursorRow();
990
991 if( curRow < 0 )
992 return;
993
994 LIB_PINS removedRow = m_dataModel->RemoveRow( curRow );
995
996 for( LIB_PIN* pin : removedRow )
997 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
998
999 curRow = std::min( curRow, m_grid->GetNumberRows() - 1 );
1000 m_grid->GoToCell( curRow, m_grid->GetGridCursorCol() );
1001 m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
1002 m_grid->SelectRow( curRow );
1003
1004 updateSummary();
1005}
1006
1007
1009{
1010 m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
1011 updateSummary();
1012}
1013
1014
1016{
1017 updateSummary();
1018}
1019
1020
1022{
1023 return m_cbGroup->GetValue();
1024}
1025
1026
1028{
1029 m_cbGroup->SetValue( false );
1030
1031 m_dataModel->RebuildRows( m_pins, false, true );
1032
1033 m_grid->ShowCol( COL_PIN_COUNT );
1034 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1035
1037}
1038
1039
1041{
1043 return;
1044
1045 m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue(), false );
1046
1047 if( m_cbGroup->GetValue() )
1048 {
1049 m_grid->ShowCol( COL_PIN_COUNT );
1050 m_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1051 }
1052
1054}
1055
1056
1058{
1059 if( event.IsChecked() )
1060 {
1061 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1062 }
1063 else
1064 {
1066 m_unitFilter->SetSelection( -1 );
1067 }
1068
1069 OnRebuildRows( event );
1070}
1071
1072
1074{
1075 m_cbFilterByUnit->SetValue( true );
1076 m_dataModel->SetUnitFilter( m_unitFilter->GetSelection() );
1077
1078 OnRebuildRows( event );
1079}
1080
1081
1083{
1084 // Account for scroll bars
1086
1087 wxGridUpdateLocker deferRepaintsTillLeavingScope;
1088
1089 // The Number and Name columns must be at least wide enough to hold their contents, but
1090 // no less wide than their original widths.
1091
1092 m_grid->AutoSizeColumn( COL_NUMBER );
1093
1094 if( m_grid->GetColSize( COL_NUMBER ) < m_originalColWidths[ COL_NUMBER ] )
1096
1097 m_grid->AutoSizeColumn( COL_NAME );
1098
1099 if( m_grid->GetColSize( COL_NAME ) < m_originalColWidths[ COL_NAME ] )
1100 m_grid->SetColSize( COL_NAME, m_originalColWidths[ COL_NAME ] );
1101
1102 // If the grid is still wider than the columns, then stretch the Number and Name columns
1103 // to fit.
1104
1105 for( int i = 0; i < COL_COUNT; ++i )
1106 width -= m_grid->GetColSize( i );
1107
1108 if( width > 0 )
1109 {
1110 m_grid->SetColSize( COL_NUMBER, m_grid->GetColSize( COL_NUMBER ) + width / 2 );
1111 m_grid->SetColSize( COL_NAME, m_grid->GetColSize( COL_NAME ) + width / 2 );
1112 }
1113}
1114
1115
1116void DIALOG_LIB_EDIT_PIN_TABLE::OnSize( wxSizeEvent& event )
1117{
1118 wxSize new_size = event.GetSize();
1119
1120 if( m_initialized && m_size != new_size )
1121 {
1122 m_size = new_size;
1123
1125 }
1126
1127 // Always propagate for a grid repaint (needed if the height changes, as well as width)
1128 event.Skip();
1129}
1130
1131
1132void DIALOG_LIB_EDIT_PIN_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
1133{
1134 std::bitset<64> columnsShown = m_grid->GetShownColumns();
1135
1136 if( columnsShown != m_columnsShown )
1137 {
1138 m_columnsShown = columnsShown;
1139
1140 if( !m_grid->IsCellEditControlShown() )
1142 }
1143
1144 int firstSelectedRow;
1145 int selectedRowCount;
1146
1147 getSelectedArea( m_grid, &firstSelectedRow, &selectedRowCount );
1148
1149 if( ( selectedRowCount > 1 ) != m_groupSelected->IsEnabled() )
1150 m_groupSelected->Enable( selectedRowCount > 1 );
1151}
1152
1153
1154void DIALOG_LIB_EDIT_PIN_TABLE::OnCancel( wxCommandEvent& event )
1155{
1156 Close();
1157}
1158
1159
1160void DIALOG_LIB_EDIT_PIN_TABLE::OnClose( wxCloseEvent& event )
1161{
1162 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1164
1165 int retval = wxID_CANCEL;
1166
1167 if( m_dataModel->IsEdited() )
1168 {
1169 if( HandleUnsavedChanges( this, _( "Save changes?" ),
1170 [&]() -> bool
1171 {
1173 {
1174 retval = wxID_OK;
1175 return true;
1176 }
1177
1178 return false;
1179 } ) )
1180 {
1181 if( IsQuasiModal() )
1182 EndQuasiModal( retval );
1183 else
1184 EndDialog( retval );
1185
1186 return;
1187 }
1188 else
1189 {
1190 event.Veto();
1191 return;
1192 }
1193 }
1194
1195 // No change in dialog: we can close it
1196 if( IsQuasiModal() )
1197 EndQuasiModal( retval );
1198 else
1199 EndDialog( retval );
1200
1201 return;
1202}
1203
1204
1206{
1207 PIN_NUMBERS pinNumbers;
1208
1209 for( LIB_PIN* pin : m_pins )
1210 {
1211 if( pin->GetNumber().Length() )
1212 pinNumbers.insert( pin->GetNumber() );
1213 }
1214
1215 m_pin_numbers_summary->SetLabel( pinNumbers.GetSummary() );
1216 m_pin_count->SetLabel( wxString::Format( wxT( "%u" ), (unsigned) m_pins.size() ) );
1217 m_duplicate_pins->SetLabel( pinNumbers.GetDuplicates() );
1218
1219 Layout();
1220}
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:111
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:106
void SetIsSeparator()
Render button as a toolbar separator.
Class DIALOG_LIB_EDIT_PIN_TABLE_BASE.
bool m_modified
true when there are unsaved changes
void OnRebuildRows(wxCommandEvent &event) override
void OnCancel(wxCommandEvent &event) override
void OnUpdateUI(wxUpdateUIEvent &event) override
void OnColSort(wxGridEvent &aEvent)
void OnGroupSelected(wxCommandEvent &event) override
void OnClose(wxCloseEvent &event) override
void OnSize(wxSizeEvent &event) override
PIN_TABLE_DATA_MODEL * m_dataModel
void OnAddRow(wxCommandEvent &event) override
void OnDeleteRow(wxCommandEvent &event) override
void OnCellEdited(wxGridEvent &event) override
DIALOG_LIB_EDIT_PIN_TABLE(SYMBOL_EDIT_FRAME *parent, LIB_SYMBOL *aSymbol)
void OnFilterCheckBox(wxCommandEvent &event) override
void OnFilterChoice(wxCommandEvent &event) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
bool IsQuasiModal() const
Definition: dialog_shim.h:106
void EndQuasiModal(int retCode)
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
int GetUnit() const
Definition: lib_item.h:293
@ BASE
Definition: lib_item.h:70
@ DEMORGAN
Definition: lib_item.h:70
void SetUnit(int aUnit)
Definition: lib_item.h:292
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition: lib_pin.h:83
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:95
void SetPosition(const VECTOR2I &aPos) override
Definition: lib_pin.h:244
int GetOrientation() const
Definition: lib_pin.h:79
void SetName(const wxString &aName)
Definition: lib_pin.h:119
void SetType(ELECTRICAL_PINTYPE aType)
Definition: lib_pin.h:96
VECTOR2I GetPosition() const override
Definition: lib_pin.h:243
GRAPHIC_PINSHAPE GetShape() const
Definition: lib_pin.h:82
void SetNumber(const wxString &aNumber)
Definition: lib_pin.h:130
const wxString & GetName() const
Definition: lib_pin.h:117
void SetOrientation(int aOrientation)
Definition: lib_pin.h:80
Define a library symbol object.
Definition: lib_symbol.h:99
void RemoveDrawItem(LIB_ITEM *aItem)
Remove draw aItem from list.
Definition: lib_symbol.cpp:934
bool IsMulti() const
Definition: lib_symbol.h:564
void AddDrawItem(LIB_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
Definition: lib_symbol.cpp:960
static wxString SubReference(int aUnit, bool aAddSeparator=true)
Definition: lib_symbol.cpp:720
wxString GetUnitReference(int aUnit) override
Return an identifier for aUnit for symbols with units.
Definition: lib_symbol.cpp:522
std::vector< LIB_PIN * > GetAllLibPins() const
Return a list of pin pointers for all units / converts.
Definition: lib_symbol.cpp:998
int GetUnitCount() const override
For items with units, return the number of units.
wxString GetDuplicates() const
Gets a formatted string of all the pins that have duplicate numbers.
static int Compare(const wxString &lhs, const wxString &rhs)
void insert(value_type const &v)
Definition: pin_numbers.h:63
wxString GetSummary() const
Definition: pin_numbers.cpp:70
PIN_TABLE_DATA_MODEL(SYMBOL_EDIT_FRAME *aFrame, DIALOG_LIB_EDIT_PIN_TABLE *aPinTable, LIB_SYMBOL *aSymbol)
void RebuildRows(const LIB_PINS &aPins, bool groupByName, bool groupBySelection)
static wxString StringFromBool(bool aValue)
static wxString GetValue(const LIB_PINS &pins, int aCol, EDA_DRAW_FRAME *aParentFrame)
std::vector< LIB_PINS > m_rows
static bool compare(const LIB_PINS &lhs, const LIB_PINS &rhs, int sortCol, bool ascending, EDA_DRAW_FRAME *parentFrame)
bool IsEmptyCell(int row, int col) override
static int findRow(const std::vector< LIB_PINS > &aRowSet, const wxString &aName)
DIALOG_LIB_EDIT_PIN_TABLE * m_pinTable
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 SortRows(int aSortCol, bool ascending)
std::map< std::pair< LIB_PINS, int >, wxString > m_evalOriginal
void onUnitsChanged(wxCommandEvent &aEvent)
static bool BoolFromString(wxString aValue)
wxString GetColLabelValue(int aCol) override
void SetBitmap(const wxBitmap &aBmp)
The symbol library editor main window.
bool IsSymbolAlias() const
Return true if aLibId is an alias for the editor screen symbol.
bool GetShowDeMorgan() const
SYMBOL_EDITOR_SETTINGS * GetSettings() const
bool IsSymbolEditable() const
Test if a symbol is loaded and can be edited.
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Converts aValue in internal units into a united string.
int ValueFromString(const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Converts aTextValue in aUnits to internal units used by the frame.
EDA_UNITS GetUserUnits() const
void ShowHideColumns(const wxString &shownColumns)
Show/hide the grid columns based on a tokenized string of shown column indexes.
Definition: wx_grid.cpp:306
void SetTable(wxGridTableBase *table, bool aTakeOwnership=false)
Hide wxGrid's SetTable() method with one which doesn't mess up the grid column widths when setting th...
Definition: wx_grid.cpp:164
void DestroyTable(wxGridTableBase *aTable)
Work-around for a bug in wxGrid which crashes when deleting the table if the cell edit control was no...
Definition: wx_grid.cpp:262
wxString GetShownColumnsAsString()
Get a tokenized string containing the shown column indexes.
Definition: wx_grid.cpp:276
std::bitset< 64 > GetShownColumns()
Definition: wx_grid.cpp:295
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:474
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:243
This file is part of the common library.
#define UNITS_ALL
void getSelectedArea(WX_GRID *aGrid, int *aRowStart, int *aRowCount)
#define DEMORGAN_ALL
#define DEMORGAN_ALT
#define DEMORGAN_STD
#define _(s)
#define CANDIDATE
flag indicating that the structure is connected
static std::map< int, wxString > shapeNames
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_item.h:54
@ PIN_LEFT
Definition: lib_pin.h:46
@ PIN_RIGHT
Definition: lib_pin.h:45
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:172
see class PGM_BASE
const std::vector< BITMAPS > & PinTypeIcons()
Definition: pin_type.cpp:194
int PinOrientationIndex(int code)
Definition: pin_type.cpp:149
const wxArrayString & PinTypeNames()
Definition: pin_type.cpp:185
const wxArrayString & PinShapeNames()
Definition: pin_type.cpp:203
const std::vector< BITMAPS > & PinShapeIcons()
Definition: pin_type.cpp:212
const wxArrayString & PinOrientationNames()
Definition: pin_type.cpp:221
int PinOrientationCode(int index)
Definition: pin_type.cpp:140
const std::vector< BITMAPS > & PinOrientationIcons()
Definition: pin_type.cpp:230
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:36
GRAPHIC_PINSHAPE
Definition: pin_type.h:56
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
constexpr int MilsToIU(int mils) const
Definition: base_units.h:94
VECTOR3I res
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: ui_common.h:42
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588