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