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-2021 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 <bitmaps.h>
29 #include <confirm.h>
30 #include <symbol_edit_frame.h>
31 #include <symbol_editor_settings.h>
33 #include <widgets/wx_grid.h>
35 #include <string_utils.h>
36 
37 class PIN_TABLE_DATA_MODEL : public wxGridTableBase
38 {
39 public:
41  m_frame( aFrame ),
42  m_edited( false )
43  {
44  m_frame->Bind( UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
45  }
46 
48  {
49  m_frame->Unbind( UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
50  }
51 
52  void onUnitsChanged( wxCommandEvent& aEvent )
53  {
54  if( GetView() )
55  GetView()->ForceRefresh();
56 
57  aEvent.Skip();
58  }
59 
60 
61  int GetNumberRows() override { return (int) m_rows.size(); }
62  int GetNumberCols() override { return COL_COUNT; }
63 
64  wxString GetColLabelValue( int aCol ) override
65  {
66  switch( aCol )
67  {
68  case COL_NUMBER: return _( "Number" );
69  case COL_NAME: return _( "Name" );
70  case COL_TYPE: return _( "Electrical Type" );
71  case COL_SHAPE: return _( "Graphic Style" );
72  case COL_ORIENTATION: return _( "Orientation" );
73  case COL_NUMBER_SIZE: return _( "Number Text Size" );
74  case COL_NAME_SIZE: return _( "Name Text Size" );
75  case COL_LENGTH: return _( "Length" );
76  case COL_POSX: return _( "X Position" );
77  case COL_POSY: return _( "Y Position" );
78  case COL_VISIBLE: return _( "Visible" );
79  default: wxFAIL; return wxEmptyString;
80  }
81  }
82 
83  bool IsEmptyCell( int row, int col ) override
84  {
85  return false; // don't allow adjacent cell overflow, even if we are actually empty
86  }
87 
88  wxString GetValue( int aRow, int aCol ) override
89  {
90  return GetValue( m_rows[ aRow ], aCol, m_frame->GetUserUnits() );
91  }
92 
93  static wxString GetValue( const LIB_PINS& pins, int aCol, EDA_UNITS aUserUnits )
94  {
95  wxString fieldValue;
96 
97  if( pins.empty() )
98  return fieldValue;
99 
100  for( LIB_PIN* pin : pins )
101  {
102  wxString val;
103 
104  switch( aCol )
105  {
106  case COL_NUMBER:
107  val = pin->GetNumber();
108  break;
109  case COL_NAME:
110  val = pin->GetName();
111  break;
112  case COL_TYPE:
113  val = PinTypeNames()[static_cast<int>( pin->GetType() )];
114  break;
115  case COL_SHAPE:
116  val = PinShapeNames()[static_cast<int>( pin->GetShape() )];
117  break;
118  case COL_ORIENTATION:
119  if( PinOrientationIndex( pin->GetOrientation() ) >= 0 )
120  val = PinOrientationNames()[ PinOrientationIndex( pin->GetOrientation() ) ];
121  break;
122  case COL_NUMBER_SIZE:
123  val = StringFromValue( aUserUnits, pin->GetNumberTextSize(), true );
124  break;
125  case COL_NAME_SIZE:
126  val = StringFromValue( aUserUnits, pin->GetNameTextSize(), true );
127  break;
128  case COL_LENGTH:
129  val = StringFromValue( aUserUnits, pin->GetLength(), true );
130  break;
131  case COL_POSX:
132  val = StringFromValue( aUserUnits, pin->GetPosition().x, true );
133  break;
134  case COL_POSY:
135  val = StringFromValue( aUserUnits, pin->GetPosition().y, true );
136  break;
137  case COL_VISIBLE:
138  val = StringFromBool( pin->IsVisible() );
139  break;
140  default:
141  wxFAIL;
142  break;
143  }
144 
145  if( aCol == COL_NUMBER )
146  {
147  if( fieldValue.length() )
148  fieldValue += wxT( ", " );
149  fieldValue += val;
150  }
151  else
152  {
153  if( !fieldValue.Length() )
154  fieldValue = val;
155  else if( val != fieldValue )
156  fieldValue = INDETERMINATE_STATE;
157  }
158  }
159 
160  return fieldValue;
161  }
162 
163  void SetValue( int aRow, int aCol, const wxString &aValue ) override
164  {
165  if( aValue == INDETERMINATE_STATE )
166  return;
167 
168  LIB_PINS pins = m_rows[ aRow ];
169 
170  for( LIB_PIN* pin : pins )
171  {
172  switch( aCol )
173  {
174  case COL_NUMBER:
175  if( pins.size() == 1 )
176  pin->SetNumber( aValue );
177  break;
178 
179  case COL_NAME:
180  pin->SetName( aValue );
181  break;
182 
183  case COL_TYPE:
184  if( PinTypeNames().Index( aValue ) != wxNOT_FOUND )
185  pin->SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( aValue ) );
186 
187  break;
188 
189  case COL_SHAPE:
190  if( PinShapeNames().Index( aValue ) != wxNOT_FOUND )
191  pin->SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( aValue ) );
192 
193  break;
194 
195  case COL_ORIENTATION:
196  if( PinOrientationNames().Index( aValue ) != wxNOT_FOUND )
197  pin->SetOrientation( PinOrientationCode( PinOrientationNames().Index( aValue ) ) );
198  break;
199 
200  case COL_NUMBER_SIZE:
201  pin->SetNumberTextSize( ValueFromString( m_frame->GetUserUnits(), aValue ) );
202  break;
203 
204  case COL_NAME_SIZE:
205  pin->SetNameTextSize( ValueFromString( m_frame->GetUserUnits(), aValue ) );
206  break;
207 
208  case COL_LENGTH:
209  pin->SetLength( ValueFromString( m_frame->GetUserUnits(), aValue ) );
210  break;
211 
212  case COL_POSX:
213  pin->SetPosition( wxPoint( ValueFromString( m_frame->GetUserUnits(), aValue ),
214  pin->GetPosition().y ) );
215  break;
216 
217  case COL_POSY:
218  pin->SetPosition( wxPoint( pin->GetPosition().x,
219  ValueFromString( m_frame->GetUserUnits(), aValue ) ) );
220  break;
221 
222  case COL_VISIBLE:
223  pin->SetVisible(BoolFromString( aValue ));
224  break;
225  default:
226  wxFAIL;
227  break;
228  }
229  }
230 
231  m_edited = true;
232  }
233 
234  static int findRow( const std::vector<LIB_PINS>& aRowSet, const wxString& aName )
235  {
236  for( size_t i = 0; i < aRowSet.size(); ++i )
237  {
238  if( aRowSet[ i ][ 0 ] && aRowSet[ i ][ 0 ]->GetName() == aName )
239  return i;
240  }
241 
242  return -1;
243  }
244 
245  static bool compare( const LIB_PINS& lhs, const LIB_PINS& rhs, int sortCol, bool ascending,
246  EDA_UNITS units )
247  {
248  wxString lhStr = GetValue( lhs, sortCol, units );
249  wxString rhStr = GetValue( rhs, sortCol, units );
250 
251  if( lhStr == rhStr )
252  {
253  // Secondary sort key is always COL_NUMBER
254  sortCol = COL_NUMBER;
255  lhStr = GetValue( lhs, sortCol, units );
256  rhStr = GetValue( rhs, sortCol, units );
257  }
258 
259  bool res;
260 
261  // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
262  // to get the opposite sort. i.e. ~(a<b) != (a>b)
263  auto cmp = [ ascending ]( const auto a, const auto b )
264  {
265  if( ascending )
266  return a < b;
267  else
268  return b < a;
269  };
270 
271  switch( sortCol )
272  {
273  case COL_NUMBER:
274  case COL_NAME:
275  res = cmp( PIN_NUMBERS::Compare( lhStr, rhStr ), 0 );
276  break;
277  case COL_NUMBER_SIZE:
278  case COL_NAME_SIZE:
279  res = cmp( ValueFromString( units, lhStr ), ValueFromString( units, rhStr ) );
280  break;
281  case COL_LENGTH:
282  case COL_POSX:
283  case COL_POSY:
284  res = cmp( ValueFromString( units, lhStr ), ValueFromString( units, rhStr ) );
285  break;
286  case COL_VISIBLE:
287  default:
288  res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
289  break;
290  }
291 
292  return res;
293  }
294 
295  void RebuildRows( const LIB_PINS& aPins, bool groupByName )
296  {
297  if ( GetView() )
298  {
299  // Commit any pending in-place edits before the row gets moved out from under
300  // the editor.
301  if( auto grid = dynamic_cast<WX_GRID*>( GetView() ) )
302  grid->CommitPendingChanges( true );
303 
304  wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
305  GetView()->ProcessTableMessage( msg );
306  }
307 
308  m_rows.clear();
309 
310  for( LIB_PIN* pin : aPins )
311  {
312  int rowIndex = -1;
313 
314  if( groupByName )
315  rowIndex = findRow( m_rows, pin->GetName() );
316 
317  if( rowIndex < 0 )
318  {
319  m_rows.emplace_back( LIB_PINS() );
320  rowIndex = m_rows.size() - 1;
321  }
322 
323  m_rows[ rowIndex ].push_back( pin );
324  }
325 
326  int sortCol = 0;
327  bool ascending = true;
328 
329  if( GetView() && GetView()->GetSortingColumn() != wxNOT_FOUND )
330  {
331  sortCol = GetView()->GetSortingColumn();
332  ascending = GetView()->IsSortOrderAscending();
333  }
334 
335  for( LIB_PINS& row : m_rows )
336  SortPins( row );
337 
338  SortRows( sortCol, ascending );
339 
340  if ( GetView() )
341  {
342  wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
343  GetView()->ProcessTableMessage( msg );
344  }
345  }
346 
347  void SortRows( int aSortCol, bool ascending )
348  {
349  std::sort( m_rows.begin(), m_rows.end(),
350  [ aSortCol, ascending, this ]( const LIB_PINS& lhs, const LIB_PINS& rhs ) -> bool
351  {
352  return compare( lhs, rhs, aSortCol, ascending, m_frame->GetUserUnits() );
353  } );
354  }
355 
356  void SortPins( LIB_PINS& aRow )
357  {
358  std::sort( aRow.begin(), aRow.end(),
359  []( LIB_PIN* lhs, LIB_PIN* rhs ) -> bool
360  {
361  return PIN_NUMBERS::Compare( lhs->GetNumber(), rhs->GetNumber() ) < 0;
362  } );
363  }
364 
365  void AppendRow( LIB_PIN* aPin )
366  {
367  LIB_PINS row;
368  row.push_back( aPin );
369  m_rows.push_back( row );
370 
371  if ( GetView() )
372  {
373  wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
374  GetView()->ProcessTableMessage( msg );
375  }
376  }
377 
378  LIB_PINS RemoveRow( int aRow )
379  {
380  LIB_PINS removedRow = m_rows[ aRow ];
381 
382  m_rows.erase( m_rows.begin() + aRow );
383 
384  if ( GetView() )
385  {
386  wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
387  GetView()->ProcessTableMessage( msg );
388  }
389 
390  return removedRow;
391  }
392 
393  bool IsEdited()
394  {
395  return m_edited;
396  }
397 
398 private:
399  static wxString StringFromBool( bool aValue )
400  {
401  if( aValue )
402  return wxT( "1" );
403  else
404  return wxT( "0" );
405  }
406 
407  static bool BoolFromString( wxString aValue )
408  {
409  if( aValue == wxT( "1" ) )
410  {
411  return true;
412  }
413  else if( aValue == wxT( "0" ) )
414  {
415  return false;
416  }
417  else
418  {
419  wxFAIL_MSG( wxString::Format( wxT( "string '%s' can't be converted to boolean " )
420  wxT( "correctly, it will have been perceived as FALSE" ),
421  aValue ) );
422  return false;
423  }
424  }
425 
426 private:
428 
429  // Because the rows of the grid can either be a single pin or a group of pins, the
430  // data model is a 2D vector. If we're in the single pin case, each row's LIB_PINS
431  // contains only a single pin.
432  std::vector<LIB_PINS> m_rows;
433 
434  bool m_edited;
435 };
436 
437 
439  LIB_SYMBOL* aSymbol ) :
441  m_editFrame( parent ),
442  m_part( aSymbol )
443 {
445 
446  // Save original columns widths so we can do proportional sizing.
447  for( int i = 0; i < COL_COUNT; ++i )
448  m_originalColWidths[ i ] = m_grid->GetColSize( i );
449 
450  // Give a bit more room for combobox editors
451  m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
452 
454  m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
455 
456  // Show/hide columns according to the user's preference
457  auto cfg = parent->GetSettings();
458  m_columnsShown = cfg->m_PinTableVisibleColumns;
459 
461 
462  // Set special attributes
463  wxGridCellAttr* attr;
464 
465  attr = new wxGridCellAttr;
466  wxArrayString typeNames = PinTypeNames();
467  typeNames.push_back( INDETERMINATE_STATE );
468  attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinTypeIcons(), typeNames ) );
469  attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinTypeIcons(), typeNames ) );
470  m_grid->SetColAttr( COL_TYPE, attr );
471 
472  attr = new wxGridCellAttr;
473  wxArrayString shapeNames = PinShapeNames();
474  shapeNames.push_back( INDETERMINATE_STATE );
475  attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinShapeIcons(), shapeNames ) );
476  attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinShapeIcons(), shapeNames ) );
477  m_grid->SetColAttr( COL_SHAPE, attr );
478 
479  attr = new wxGridCellAttr;
480  wxArrayString orientationNames = PinOrientationNames();
481  orientationNames.push_back( INDETERMINATE_STATE );
482  attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( PinOrientationIcons(),
483  orientationNames ) );
484  attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( PinOrientationIcons(), orientationNames ) );
485  m_grid->SetColAttr( COL_ORIENTATION, attr );
486 
487  attr = new wxGridCellAttr;
488  attr->SetRenderer( new wxGridCellBoolRenderer() );
489  attr->SetEditor( new wxGridCellBoolEditor() );
490  attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
491  m_grid->SetColAttr( COL_VISIBLE, attr );
492 
493  /* Right-aligned position values look much better, but only MSW and GTK2+
494  * currently support right-aligned textEditCtrls, so the text jumps on all
495  * the other platforms when you edit it.
496  attr = new wxGridCellAttr;
497  attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
498  m_grid->SetColAttr( COL_POSX, attr );
499 
500  attr = new wxGridCellAttr;
501  attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
502  m_grid->SetColAttr( COL_POSY, attr );
503  */
504 
505  m_addButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
508 
509  GetSizer()->SetSizeHints(this);
510  Centre();
511 
512  if( !parent->IsSymbolEditable() || parent->IsSymbolAlias() )
513  {
514  m_ButtonsCancel->SetDefault();
515  m_ButtonsOK->SetLabel( _( "Read Only" ) );
516  m_ButtonsOK->Enable( false );
517  }
518  else
519  {
520  m_ButtonsOK->SetDefault();
521  }
522 
523  m_ButtonsOK->SetDefault();
524  m_initialized = true;
525  m_modified = false;
526  m_width = 0;
527 
528  // Connect Events
529  m_grid->Connect( wxEVT_GRID_COL_SORT,
530  wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
531 }
532 
533 
535 {
536  auto cfg = m_editFrame->GetSettings();
537  cfg->m_PinTableVisibleColumns = m_grid->GetShownColumns().ToStdString();
538 
539  // Disconnect Events
540  m_grid->Disconnect( wxEVT_GRID_COL_SORT,
541  wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
542 
543  // Prevents crash bug in wxGrid's d'tor
545 
546  // Delete the GRID_TRICKS.
547  m_grid->PopEventHandler( true );
548 
549  // This is our copy of the pins. If they were transferred to the part on an OK, then
550  // m_pins will already be empty.
551  for( auto pin : m_pins )
552  delete pin;
553 }
554 
555 
557 {
558  // Make a copy of the pins for editing
559  for( LIB_PIN* pin = m_part->GetNextPin( nullptr ); pin; pin = m_part->GetNextPin( pin ) )
560  m_pins.push_back( new LIB_PIN( *pin ) );
561 
562  m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue() );
563 
564  updateSummary();
565 
566  return true;
567 }
568 
569 
571 {
572  if( !m_grid->CommitPendingChanges() )
573  return false;
574 
575  // Delete the part's pins
576  while( LIB_PIN* pin = m_part->GetNextPin( nullptr ) )
578 
579  // Transfer our pins to the part
580  for( LIB_PIN* pin : m_pins )
581  {
582  pin->SetParent( m_part );
583  m_part->AddDrawItem( pin );
584  }
585 
586  m_pins.clear();
587 
588  return true;
589 }
590 
591 
592 void DIALOG_LIB_EDIT_PIN_TABLE::OnColSort( wxGridEvent& aEvent )
593 {
594  int sortCol = aEvent.GetCol();
595  bool ascending;
596 
597  // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
598  // event, and if we ask it will give us pre-event info.
599  if( m_grid->IsSortingBy( sortCol ) )
600  // same column; invert ascending
601  ascending = !m_grid->IsSortOrderAscending();
602  else
603  // different column; start with ascending
604  ascending = true;
605 
606  m_dataModel->SortRows( sortCol, ascending );
607 }
608 
609 
610 void DIALOG_LIB_EDIT_PIN_TABLE::OnAddRow( wxCommandEvent& event )
611 {
612  if( !m_grid->CommitPendingChanges() )
613  return;
614 
615  LIB_PIN* newPin = new LIB_PIN( nullptr );
616 
617  if( m_pins.size() > 0 )
618  {
619  LIB_PIN* last = m_pins.back();
620 
621  newPin->SetOrientation( last->GetOrientation() );
622  newPin->SetType( last->GetType() );
623  newPin->SetShape( last->GetShape() );
624  newPin->SetParent( last->GetParent() );
625 
626  wxPoint pos = last->GetPosition();
627 
629 
630  if( last->GetOrientation() == PIN_LEFT || last->GetOrientation() == PIN_RIGHT )
631  pos.y -= Mils2iu(cfg->m_Repeat.pin_step);
632  else
633  pos.x += Mils2iu(cfg->m_Repeat.pin_step);
634 
635  newPin->SetPosition( pos );
636  }
637 
638  m_pins.push_back( newPin );
639 
640  m_dataModel->AppendRow( m_pins[ m_pins.size() - 1 ] );
641 
642  m_grid->MakeCellVisible( m_grid->GetNumberRows() - 1, 0 );
643  m_grid->SetGridCursor( m_grid->GetNumberRows() - 1, 0 );
644 
645  m_grid->EnableCellEditControl( true );
646  m_grid->ShowCellEditControl();
647 
648  updateSummary();
649 }
650 
651 
652 void DIALOG_LIB_EDIT_PIN_TABLE::OnDeleteRow( wxCommandEvent& event )
653 {
654  // TODO: handle delete of multiple rows....
655 
656  if( !m_grid->CommitPendingChanges() )
657  return;
658 
659  if( m_pins.size() == 0 ) // empty table
660  return;
661 
662  int curRow = m_grid->GetGridCursorRow();
663 
664  if( curRow < 0 )
665  return;
666 
667  LIB_PINS removedRow = m_dataModel->RemoveRow( curRow );
668 
669  for( auto pin : removedRow )
670  m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
671 
672  curRow = std::min( curRow, m_grid->GetNumberRows() - 1 );
673  m_grid->GoToCell( curRow, m_grid->GetGridCursorCol() );
674  m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
675  m_grid->SelectRow( curRow );
676 
677  updateSummary();
678 }
679 
680 
681 void DIALOG_LIB_EDIT_PIN_TABLE::OnCellEdited( wxGridEvent& event )
682 {
683  updateSummary();
684 }
685 
686 
688 {
689  if( !m_grid->CommitPendingChanges() )
690  return;
691 
692  m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue() );
693 
694  adjustGridColumns( m_grid->GetRect().GetWidth() );
695 }
696 
697 
699 {
700  m_width = aWidth;
701 
702  // Account for scroll bars
703  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
704 
705  wxGridUpdateLocker deferRepaintsTillLeavingScope;
706 
707  // The Number and Name columns must be at least wide enough to hold their contents, but
708  // no less wide than their original widths.
709 
710  m_grid->AutoSizeColumn( COL_NUMBER );
711 
712  if( m_grid->GetColSize( COL_NUMBER ) < m_originalColWidths[ COL_NUMBER ] )
713  m_grid->SetColSize( COL_NUMBER, m_originalColWidths[ COL_NUMBER ] );
714 
715  m_grid->AutoSizeColumn( COL_NAME );
716 
717  if( m_grid->GetColSize( COL_NAME ) < m_originalColWidths[ COL_NAME ] )
718  m_grid->SetColSize( COL_NAME, m_originalColWidths[ COL_NAME ] );
719 
720  // If the grid is still wider than the columns, then stretch the Number and Name columns
721  // to fit.
722 
723  for( int i = 0; i < COL_COUNT; ++i )
724  aWidth -= m_grid->GetColSize( i );
725 
726  if( aWidth > 0 )
727  {
728  m_grid->SetColSize( COL_NUMBER, m_grid->GetColSize( COL_NUMBER ) + aWidth / 2 );
729  m_grid->SetColSize( COL_NAME, m_grid->GetColSize( COL_NAME ) + aWidth / 2 );
730  }
731 }
732 
733 
734 void DIALOG_LIB_EDIT_PIN_TABLE::OnSize( wxSizeEvent& event )
735 {
736  auto new_size = event.GetSize().GetX();
737 
738  if( m_initialized && m_width != new_size )
739  {
740  adjustGridColumns( new_size );
741  }
742 
743  // Always propagate for a grid repaint (needed if the height changes, as well as width)
744  event.Skip();
745 }
746 
747 
748 void DIALOG_LIB_EDIT_PIN_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
749 {
750  wxString columnsShown = m_grid->GetShownColumns();
751 
752  if( columnsShown != m_columnsShown )
753  {
754  m_columnsShown = columnsShown;
755 
756  if( !m_grid->IsCellEditControlShown() )
757  adjustGridColumns( m_grid->GetRect().GetWidth() );
758  }
759 }
760 
761 
762 void DIALOG_LIB_EDIT_PIN_TABLE::OnCancel( wxCommandEvent& event )
763 {
764  Close();
765 }
766 
767 
768 void DIALOG_LIB_EDIT_PIN_TABLE::OnClose( wxCloseEvent& event )
769 {
770  // This is a cancel, so commit quietly as we're going to throw the results away anyway.
771  m_grid->CommitPendingChanges( true );
772 
773  int retval = wxID_CANCEL;
774 
775  if( m_dataModel->IsEdited() )
776  {
777  if( HandleUnsavedChanges( this, _( "Save changes?" ),
778  [&]() -> bool
779  {
780  if( TransferDataFromWindow() )
781  {
782  retval = wxID_OK;
783  return true;
784  }
785 
786  return false;
787  } ) )
788  {
789  if( IsQuasiModal() )
790  EndQuasiModal( retval );
791  else
792  EndDialog( retval );
793 
794  return;
795  }
796  else
797  {
798  event.Veto();
799  return;
800  }
801  }
802 
803  // No change in dialog: we can close it
804  if( IsQuasiModal() )
805  EndQuasiModal( retval );
806  else
807  EndDialog( retval );
808 
809  return;
810 }
811 
812 
814 {
815  PIN_NUMBERS pinNumbers;
816 
817  for( LIB_PIN* pin : m_pins )
818  {
819  if( pin->GetNumber().Length() )
820  pinNumbers.insert( pin->GetNumber() );
821  }
822 
823  m_summary->SetLabel( pinNumbers.GetSummary() );
824 }
PIN_TABLE_DATA_MODEL(SYMBOL_EDIT_FRAME *aFrame)
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:239
const std::vector< BITMAPS > & PinOrientationIcons()
Definition: pin_type.cpp:231
void OnClose(wxCloseEvent &event) override
void OnSize(wxSizeEvent &event) override
void SetOrientation(int aOrientation)
Definition: lib_pin.h:76
const std::vector< BITMAPS > & PinShapeIcons()
Definition: pin_type.cpp:213
This file is part of the common library.
int GetOrientation() const
Definition: lib_pin.h:75
void ShowHideColumns(const wxString &shownColumns)
Show/hide the grid columns based on a tokenized string of shown column indexes.
Definition: wx_grid.cpp:172
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_item.h:55
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition: lib_pin.h:79
GRAPHIC_PINSHAPE GetShape() const
Definition: lib_pin.h:78
void OnAddRow(wxCommandEvent &event) override
SYMBOL_EDITOR_SETTINGS * GetSettings() const
void OnCellEdited(wxGridEvent &event) override
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:55
bool IsQuasiModal() const
Definition: dialog_shim.h:106
int PinOrientationIndex(int code)
Definition: pin_type.cpp:150
int PinOrientationCode(int index)
Definition: pin_type.cpp:141
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 RemoveDrawItem(LIB_ITEM *aItem)
Remove draw aItem from list.
Definition: lib_symbol.cpp:690
GRAPHIC_PINSHAPE
Definition: pin_type.h:55
wxString GetSummary() const
Definition: pin_numbers.cpp:69
void SetPosition(const wxPoint &aPos) override
Definition: lib_pin.h:213
Define a library symbol object.
Definition: lib_symbol.h:96
void RebuildRows(const LIB_PINS &aPins, bool groupByName)
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:115
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
bool m_modified
true when there are unsaved changes
const wxArrayString & PinShapeNames()
Definition: pin_type.cpp:204
void SetType(ELECTRICAL_PINTYPE aType)
Definition: lib_pin.h:85
bool IsEmptyCell(int row, int col) override
static int Compare(const wxString &lhs, const wxString &rhs)
bool IsSymbolAlias() const
Restore the empty editor screen, without any symbol or library selected.
void insert(value_type const &v)
Definition: pin_numbers.h:57
static bool BoolFromString(wxString aValue)
void SortRows(int aSortCol, bool ascending)
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:416
void OnRebuildRows(wxCommandEvent &event) override
void OnUpdateUI(wxUpdateUIEvent &event) override
const wxArrayString & PinTypeNames()
Definition: pin_type.cpp:186
#define _(s)
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:716
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:226
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
wxString GetColLabelValue(int aCol) override
PIN_TABLE_DATA_MODEL * m_dataModel
wxString GetValue(int aRow, int aCol) override
static bool compare(const LIB_PINS &lhs, const LIB_PINS &rhs, int sortCol, bool ascending, EDA_UNITS units)
wxPoint GetPosition() const override
Definition: lib_pin.h:212
Class DIALOG_LIB_EDIT_PIN_TABLE_BASE.
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
const wxArrayString & PinOrientationNames()
Definition: pin_type.cpp:222
EDA_UNITS
Definition: eda_units.h:38
wxString GetShownColumns()
Get a tokenized string containing the shown column indexes.
Definition: wx_grid.cpp:153
const wxString & GetNumber() const
Definition: lib_pin.h:116
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:84
LIB_SYMBOL * GetParent() const
Definition: lib_item.h:164
void EndQuasiModal(int retCode)
static int findRow(const std::vector< LIB_PINS > &aRowSet, const wxString &aName)
bool IsSymbolEditable() const
Test if a symbol is loaded and can be edited.
DIALOG_LIB_EDIT_PIN_TABLE(SYMBOL_EDIT_FRAME *parent, LIB_SYMBOL *aSymbol)
void OnDeleteRow(wxCommandEvent &event) override
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:35
void OnColSort(wxGridEvent &aEvent)
static wxString GetValue(const LIB_PINS &pins, int aCol, EDA_UNITS aUserUnits)
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:204
LIB_PIN * GetNextPin(LIB_PIN *aItem=nullptr)
Return the next pin object from the draw list.
Definition: lib_symbol.h:383
void SetValue(int aRow, int aCol, const wxString &aValue) override
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
static std::map< int, wxString > shapeNames
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: base_units.h:47
std::vector< LIB_PINS > m_rows
void OnCancel(wxCommandEvent &event) override
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
static wxString StringFromBool(bool aValue)
const std::vector< BITMAPS > & PinTypeIcons()
Definition: pin_type.cpp:195
void onUnitsChanged(wxCommandEvent &aEvent)
The symbol library editor main window.