You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
22 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2019 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. #include "dialog_lib_edit_pin_table.h"
  24. #include "grid_tricks.h"
  25. #include "lib_pin.h"
  26. #include "pin_number.h"
  27. #include <base_units.h>
  28. #include <bitmaps.h>
  29. #include <confirm.h>
  30. #include <kicad_string.h>
  31. #include <kiface_i.h>
  32. #include <lib_edit_frame.h>
  33. #include <libedit_settings.h>
  34. #include <queue>
  35. #include <widgets/grid_icon_text_helpers.h>
  36. #include <widgets/wx_grid.h>
  37. #include <wx/bmpcbox.h>
  38. static std::vector<BITMAP_DEF> g_typeIcons;
  39. static wxArrayString g_typeNames;
  40. static std::vector<BITMAP_DEF> g_shapeIcons;
  41. static wxArrayString g_shapeNames;
  42. static std::vector<BITMAP_DEF> g_orientationIcons;
  43. static wxArrayString g_orientationNames;
  44. class PIN_TABLE_DATA_MODEL : public wxGridTableBase
  45. {
  46. private:
  47. // Because the rows of the grid can either be a single pin or a group of pins, the
  48. // data model is a 2D vector. If we're in the single pin case, each row's LIB_PINS
  49. // contains only a single pin.
  50. std::vector<LIB_PINS> m_rows;
  51. EDA_UNITS m_userUnits;
  52. bool m_edited;
  53. public:
  54. PIN_TABLE_DATA_MODEL( EDA_UNITS aUserUnits ) : m_userUnits( aUserUnits ), m_edited( false )
  55. {
  56. }
  57. int GetNumberRows() override { return (int) m_rows.size(); }
  58. int GetNumberCols() override { return COL_COUNT; }
  59. wxString GetColLabelValue( int aCol ) override
  60. {
  61. switch( aCol )
  62. {
  63. case COL_NUMBER: return _( "Number" );
  64. case COL_NAME: return _( "Name" );
  65. case COL_TYPE: return _( "Electrical Type" );
  66. case COL_SHAPE: return _( "Graphic Style" );
  67. case COL_ORIENTATION: return _( "Orientation" );
  68. case COL_NUMBER_SIZE: return _( "Number Text Size" );
  69. case COL_NAME_SIZE: return _( "Name Text Size" );
  70. case COL_LENGTH: return _( "Length" );
  71. case COL_POSX: return _( "X Position" );
  72. case COL_POSY: return _( "Y Position" );
  73. default: wxFAIL; return wxEmptyString;
  74. }
  75. }
  76. bool IsEmptyCell( int row, int col ) override
  77. {
  78. return false; // don't allow adjacent cell overflow, even if we are actually empty
  79. }
  80. wxString GetValue( int aRow, int aCol ) override
  81. {
  82. return GetValue( m_rows[ aRow ], aCol, m_userUnits );
  83. }
  84. static wxString GetValue( const LIB_PINS& pins, int aCol, EDA_UNITS aUserUnits )
  85. {
  86. wxString fieldValue;
  87. if( pins.empty())
  88. return fieldValue;
  89. for( LIB_PIN* pin : pins )
  90. {
  91. wxString val;
  92. switch( aCol )
  93. {
  94. case COL_NUMBER:
  95. val = pin->GetNumber();
  96. break;
  97. case COL_NAME:
  98. val = pin->GetName();
  99. break;
  100. case COL_TYPE:
  101. val = g_typeNames[static_cast<int>( pin->GetType() )];
  102. break;
  103. case COL_SHAPE:
  104. val = g_shapeNames[static_cast<int>( pin->GetShape() )];
  105. break;
  106. case COL_ORIENTATION:
  107. if( LIB_PIN::GetOrientationIndex( pin->GetOrientation() ) >= 0 )
  108. val = g_orientationNames[ LIB_PIN::GetOrientationIndex( pin->GetOrientation() ) ];
  109. break;
  110. case COL_NUMBER_SIZE:
  111. val = StringFromValue( aUserUnits, pin->GetNumberTextSize(), true, true );
  112. break;
  113. case COL_NAME_SIZE:
  114. val = StringFromValue( aUserUnits, pin->GetNameTextSize(), true, true );
  115. break;
  116. case COL_LENGTH:
  117. val = StringFromValue( aUserUnits, pin->GetLength(), true );
  118. break;
  119. case COL_POSX:
  120. val = StringFromValue( aUserUnits, pin->GetPosition().x, true );
  121. break;
  122. case COL_POSY:
  123. val = StringFromValue( aUserUnits, pin->GetPosition().y, true );
  124. break;
  125. default:
  126. wxFAIL;
  127. break;
  128. }
  129. if( aCol == COL_NUMBER )
  130. {
  131. if( fieldValue.length() )
  132. fieldValue += wxT( ", " );
  133. fieldValue += val;
  134. }
  135. else
  136. {
  137. if( !fieldValue.Length() )
  138. fieldValue = val;
  139. else if( val != fieldValue )
  140. fieldValue = INDETERMINATE;
  141. }
  142. }
  143. return fieldValue;
  144. }
  145. void SetValue( int aRow, int aCol, const wxString &aValue ) override
  146. {
  147. if( aValue == INDETERMINATE )
  148. return;
  149. LIB_PINS pins = m_rows[ aRow ];
  150. for( LIB_PIN* pin : pins )
  151. {
  152. switch( aCol )
  153. {
  154. case COL_NUMBER:
  155. pin->SetNumber( aValue );
  156. break;
  157. case COL_NAME:
  158. pin->SetName( aValue );
  159. break;
  160. case COL_TYPE:
  161. if( g_typeNames.Index( aValue ) != wxNOT_FOUND )
  162. pin->SetType( (ELECTRICAL_PINTYPE) g_typeNames.Index( aValue ), false );
  163. break;
  164. case COL_SHAPE:
  165. if( g_shapeNames.Index( aValue ) != wxNOT_FOUND )
  166. pin->SetShape( (GRAPHIC_PINSHAPE) g_shapeNames.Index( aValue ) );
  167. break;
  168. case COL_ORIENTATION:
  169. if( g_orientationNames.Index( aValue ) != wxNOT_FOUND )
  170. pin->SetOrientation( LIB_PIN::GetOrientationCode(
  171. g_orientationNames.Index( aValue ) ), false );
  172. break;
  173. case COL_NUMBER_SIZE:
  174. pin->SetNumberTextSize( ValueFromString( m_userUnits, aValue, true ) );
  175. break;
  176. case COL_NAME_SIZE:
  177. pin->SetNameTextSize( ValueFromString( m_userUnits, aValue, true ) );
  178. break;
  179. case COL_LENGTH:
  180. pin->SetLength( ValueFromString( m_userUnits, aValue ) );
  181. break;
  182. case COL_POSX:
  183. pin->SetPinPosition( wxPoint( ValueFromString( m_userUnits, aValue ),
  184. pin->GetPosition().y ) );
  185. break;
  186. case COL_POSY:
  187. pin->SetPinPosition( wxPoint( pin->GetPosition().x,
  188. ValueFromString( m_userUnits, aValue ) ) );
  189. break;
  190. default:
  191. wxFAIL;
  192. break;
  193. }
  194. }
  195. m_edited = true;
  196. }
  197. static int findRow( const std::vector<LIB_PINS>& aRowSet, const wxString& aName )
  198. {
  199. for( size_t i = 0; i < aRowSet.size(); ++i )
  200. {
  201. if( aRowSet[ i ][ 0 ] && aRowSet[ i ][ 0 ]->GetName() == aName )
  202. return i;
  203. }
  204. return -1;
  205. }
  206. static bool compare(
  207. const LIB_PINS& lhs, const LIB_PINS& rhs, int sortCol, bool ascending, EDA_UNITS units )
  208. {
  209. wxString lhStr = GetValue( lhs, sortCol, units );
  210. wxString rhStr = GetValue( rhs, sortCol, units );
  211. if( lhStr == rhStr )
  212. {
  213. // Secondary sort key is always COL_NUMBER
  214. sortCol = COL_NUMBER;
  215. lhStr = GetValue( lhs, sortCol, units );
  216. rhStr = GetValue( rhs, sortCol, units );
  217. }
  218. bool res;
  219. // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
  220. // to get the opposite sort. i.e. ~(a<b) != (a>b)
  221. auto cmp = [ ascending ]( const auto a, const auto b )
  222. {
  223. if( ascending )
  224. return a < b;
  225. else
  226. return b < a;
  227. };
  228. switch( sortCol )
  229. {
  230. case COL_NUMBER:
  231. case COL_NAME:
  232. res = cmp( PinNumbers::Compare( lhStr, rhStr ), 0 );
  233. break;
  234. case COL_NUMBER_SIZE:
  235. case COL_NAME_SIZE:
  236. res = cmp( ValueFromString( units, lhStr, true ),
  237. ValueFromString( units, rhStr, true ) );
  238. break;
  239. case COL_LENGTH:
  240. case COL_POSX:
  241. case COL_POSY:
  242. res = cmp( ValueFromString( units, lhStr ), ValueFromString( units, rhStr ) );
  243. break;
  244. default:
  245. res = cmp( StrNumCmp( lhStr, rhStr ), 0 );
  246. break;
  247. }
  248. return res;
  249. }
  250. void RebuildRows( LIB_PINS& aPins, bool groupByName )
  251. {
  252. if ( GetView() )
  253. {
  254. // Commit any pending in-place edits before the row gets moved out from under
  255. // the editor.
  256. if( auto grid = dynamic_cast<WX_GRID*>( GetView() ) )
  257. grid->CommitPendingChanges( true );
  258. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
  259. GetView()->ProcessTableMessage( msg );
  260. }
  261. m_rows.clear();
  262. for( LIB_PIN* pin : aPins )
  263. {
  264. int rowIndex = -1;
  265. if( groupByName )
  266. rowIndex = findRow( m_rows, pin->GetName() );
  267. if( rowIndex < 0 )
  268. {
  269. m_rows.emplace_back( LIB_PINS() );
  270. rowIndex = m_rows.size() - 1;
  271. }
  272. m_rows[ rowIndex ].push_back( pin );
  273. }
  274. int sortCol = 0;
  275. bool ascending = true;
  276. if( GetView() && GetView()->GetSortingColumn() != wxNOT_FOUND )
  277. {
  278. sortCol = GetView()->GetSortingColumn();
  279. ascending = GetView()->IsSortOrderAscending();
  280. }
  281. for( LIB_PINS& row : m_rows )
  282. SortPins( row );
  283. SortRows( sortCol, ascending );
  284. if ( GetView() )
  285. {
  286. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
  287. GetView()->ProcessTableMessage( msg );
  288. }
  289. }
  290. void SortRows( int aSortCol, bool ascending )
  291. {
  292. std::sort( m_rows.begin(), m_rows.end(),
  293. [ aSortCol, ascending, this ]( const LIB_PINS& lhs, const LIB_PINS& rhs ) -> bool
  294. {
  295. return compare( lhs, rhs, aSortCol, ascending, m_userUnits );
  296. } );
  297. }
  298. void SortPins( LIB_PINS& aRow )
  299. {
  300. std::sort( aRow.begin(), aRow.end(),
  301. []( LIB_PIN* lhs, LIB_PIN* rhs ) -> bool
  302. {
  303. return PinNumbers::Compare( lhs->GetNumber(), rhs->GetNumber() ) < 0;
  304. } );
  305. }
  306. void AppendRow( LIB_PIN* aPin )
  307. {
  308. LIB_PINS row;
  309. row.push_back( aPin );
  310. m_rows.push_back( row );
  311. if ( GetView() )
  312. {
  313. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
  314. GetView()->ProcessTableMessage( msg );
  315. }
  316. }
  317. LIB_PINS RemoveRow( int aRow )
  318. {
  319. LIB_PINS removedRow = m_rows[ aRow ];
  320. m_rows.erase( m_rows.begin() + aRow );
  321. if ( GetView() )
  322. {
  323. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
  324. GetView()->ProcessTableMessage( msg );
  325. }
  326. return removedRow;
  327. }
  328. bool IsEdited()
  329. {
  330. return m_edited;
  331. }
  332. };
  333. DIALOG_LIB_EDIT_PIN_TABLE::DIALOG_LIB_EDIT_PIN_TABLE( LIB_EDIT_FRAME* parent, LIB_PART* aPart ) :
  334. DIALOG_LIB_EDIT_PIN_TABLE_BASE( parent ),
  335. m_editFrame( parent ),
  336. m_part( aPart )
  337. {
  338. if( g_typeNames.empty())
  339. {
  340. for( unsigned i = 0; i < ELECTRICAL_PINTYPES_TOTAL; ++i )
  341. g_typeIcons.push_back( GetBitmap( static_cast<ELECTRICAL_PINTYPE>( i ) ) );
  342. for( unsigned i = 0; i < ELECTRICAL_PINTYPES_TOTAL; ++i )
  343. g_typeNames.push_back( GetText( static_cast<ELECTRICAL_PINTYPE>( i ) ) );
  344. g_typeNames.push_back( INDETERMINATE );
  345. for( unsigned i = 0; i < GRAPHIC_PINSHAPES_TOTAL; ++i )
  346. g_shapeIcons.push_back( GetBitmap( static_cast<GRAPHIC_PINSHAPE>( i ) ) );
  347. for( unsigned i = 0; i < GRAPHIC_PINSHAPES_TOTAL; ++i )
  348. g_shapeNames.push_back( GetText( static_cast<GRAPHIC_PINSHAPE>( i ) ) );
  349. g_shapeNames.push_back( INDETERMINATE );
  350. for( unsigned i = 0; i < LIB_PIN::GetOrientationNames().size(); ++i )
  351. g_orientationIcons.push_back( LIB_PIN::GetOrientationSymbols()[ i ] );
  352. g_orientationNames = LIB_PIN::GetOrientationNames();
  353. g_orientationNames.push_back( INDETERMINATE );
  354. }
  355. m_dataModel = new PIN_TABLE_DATA_MODEL( GetUserUnits() );
  356. // Save original columns widths so we can do proportional sizing.
  357. for( int i = 0; i < COL_COUNT; ++i )
  358. m_originalColWidths[ i ] = m_grid->GetColSize( i );
  359. // Give a bit more room for combobox editors
  360. m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
  361. m_grid->SetTable( m_dataModel );
  362. m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
  363. // Show/hide columns according to the user's preference
  364. auto cfg = parent->GetSettings();
  365. m_columnsShown = cfg->m_PinTableVisibleColumns;
  366. m_grid->ShowHideColumns( m_columnsShown );
  367. // Set special attributes
  368. wxGridCellAttr* attr;
  369. attr = new wxGridCellAttr;
  370. attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( g_typeIcons, g_typeNames ) );
  371. attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( g_typeIcons, g_typeNames ) );
  372. m_grid->SetColAttr( COL_TYPE, attr );
  373. attr = new wxGridCellAttr;
  374. attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( g_shapeIcons, g_shapeNames ) );
  375. attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( g_shapeIcons, g_shapeNames ) );
  376. m_grid->SetColAttr( COL_SHAPE, attr );
  377. attr = new wxGridCellAttr;
  378. attr->SetRenderer( new GRID_CELL_ICON_TEXT_RENDERER( g_orientationIcons, g_orientationNames ) );
  379. attr->SetEditor( new GRID_CELL_ICON_TEXT_POPUP( g_orientationIcons, g_orientationNames ) );
  380. m_grid->SetColAttr( COL_ORIENTATION, attr );
  381. /* Right-aligned position values look much better, but only MSW and GTK2+
  382. * currently support righ-aligned textEditCtrls, so the text jumps on all
  383. * the other platforms when you edit it.
  384. attr = new wxGridCellAttr;
  385. attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
  386. m_grid->SetColAttr( COL_POSX, attr );
  387. attr = new wxGridCellAttr;
  388. attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_TOP );
  389. m_grid->SetColAttr( COL_POSY, attr );
  390. */
  391. m_addButton->SetBitmap( KiBitmap( small_plus_xpm ) );
  392. m_deleteButton->SetBitmap( KiBitmap( trash_xpm ) );
  393. m_refreshButton->SetBitmap( KiBitmap( refresh_xpm ) );
  394. GetSizer()->SetSizeHints(this);
  395. Centre();
  396. m_ButtonsOK->SetDefault();
  397. m_initialized = true;
  398. m_modified = false;
  399. m_width = 0;
  400. // Connect Events
  401. m_grid->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
  402. }
  403. DIALOG_LIB_EDIT_PIN_TABLE::~DIALOG_LIB_EDIT_PIN_TABLE()
  404. {
  405. auto cfg = m_editFrame->GetSettings();
  406. cfg->m_PinTableVisibleColumns = m_grid->GetShownColumns().ToStdString();
  407. // Disconnect Events
  408. m_grid->Disconnect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE::OnColSort ), nullptr, this );
  409. // Prevents crash bug in wxGrid's d'tor
  410. m_grid->DestroyTable( m_dataModel );
  411. // Delete the GRID_TRICKS.
  412. m_grid->PopEventHandler( true );
  413. // This is our copy of the pins. If they were transfered to the part on an OK, then
  414. // m_pins will already be empty.
  415. for( auto pin : m_pins )
  416. delete pin;
  417. }
  418. bool DIALOG_LIB_EDIT_PIN_TABLE::TransferDataToWindow()
  419. {
  420. // Make a copy of the pins for editing
  421. for( LIB_PIN* pin = m_part->GetNextPin( nullptr ); pin; pin = m_part->GetNextPin( pin ) )
  422. m_pins.push_back( new LIB_PIN( *pin ) );
  423. m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue() );
  424. updateSummary();
  425. return true;
  426. }
  427. bool DIALOG_LIB_EDIT_PIN_TABLE::TransferDataFromWindow()
  428. {
  429. if( !m_grid->CommitPendingChanges() )
  430. return false;
  431. // Delete the part's pins
  432. while( LIB_PIN* pin = m_part->GetNextPin( nullptr ) )
  433. m_part->RemoveDrawItem( pin );
  434. // Transfer our pins to the part
  435. for( LIB_PIN* pin : m_pins )
  436. {
  437. pin->SetParent( m_part );
  438. m_part->AddDrawItem( pin );
  439. }
  440. m_pins.clear();
  441. return true;
  442. }
  443. void DIALOG_LIB_EDIT_PIN_TABLE::OnColSort( wxGridEvent& aEvent )
  444. {
  445. int sortCol = aEvent.GetCol();
  446. bool ascending;
  447. // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the
  448. // event, and if we ask it will give us pre-event info.
  449. if( m_grid->IsSortingBy( sortCol ) )
  450. // same column; invert ascending
  451. ascending = !m_grid->IsSortOrderAscending();
  452. else
  453. // different column; start with ascending
  454. ascending = true;
  455. m_dataModel->SortRows( sortCol, ascending );
  456. }
  457. void DIALOG_LIB_EDIT_PIN_TABLE::OnAddRow( wxCommandEvent& event )
  458. {
  459. if( !m_grid->CommitPendingChanges() )
  460. return;
  461. LIB_PIN* newPin = new LIB_PIN( nullptr );
  462. if( m_pins.size() > 0 )
  463. {
  464. LIB_PIN* last = m_pins.back();
  465. newPin->SetOrientation( last->GetOrientation() );
  466. newPin->SetType( last->GetType() );
  467. newPin->SetShape( last->GetShape() );
  468. wxPoint pos = last->GetPosition();
  469. if( last->GetOrientation() == PIN_LEFT || last->GetOrientation() == PIN_RIGHT )
  470. pos.y -= m_editFrame->GetRepeatPinStep();
  471. else
  472. pos.x += m_editFrame->GetRepeatPinStep();
  473. newPin->SetPosition( pos );
  474. }
  475. m_pins.push_back( newPin );
  476. m_dataModel->AppendRow( m_pins[ m_pins.size() - 1 ] );
  477. m_grid->MakeCellVisible( m_grid->GetNumberRows() - 1, 0 );
  478. m_grid->SetGridCursor( m_grid->GetNumberRows() - 1, 0 );
  479. m_grid->EnableCellEditControl( true );
  480. m_grid->ShowCellEditControl();
  481. updateSummary();
  482. }
  483. void DIALOG_LIB_EDIT_PIN_TABLE::OnDeleteRow( wxCommandEvent& event )
  484. {
  485. if( !m_grid->CommitPendingChanges() )
  486. return;
  487. if( m_pins.size() == 0 ) // empty table
  488. return;
  489. int curRow = m_grid->GetGridCursorRow();
  490. if( curRow < 0 )
  491. return;
  492. LIB_PINS removedRow = m_dataModel->RemoveRow( curRow );
  493. for( auto pin : removedRow )
  494. m_pins.erase( std::find( m_pins.begin(), m_pins.end(), pin ) );
  495. curRow = std::max( 0, curRow - 1 );
  496. m_grid->MakeCellVisible( curRow, m_grid->GetGridCursorCol() );
  497. m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
  498. updateSummary();
  499. }
  500. void DIALOG_LIB_EDIT_PIN_TABLE::OnCellEdited( wxGridEvent& event )
  501. {
  502. updateSummary();
  503. }
  504. void DIALOG_LIB_EDIT_PIN_TABLE::OnRebuildRows( wxCommandEvent& )
  505. {
  506. if( !m_grid->CommitPendingChanges() )
  507. return;
  508. m_dataModel->RebuildRows( m_pins, m_cbGroup->GetValue() );
  509. adjustGridColumns( m_grid->GetRect().GetWidth() );
  510. }
  511. void DIALOG_LIB_EDIT_PIN_TABLE::adjustGridColumns( int aWidth )
  512. {
  513. m_width = aWidth;
  514. // Account for scroll bars
  515. aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
  516. wxGridUpdateLocker deferRepaintsTillLeavingScope;
  517. // The Number and Name columns must be at least wide enough to hold their contents, but
  518. // no less wide than their original widths.
  519. m_grid->AutoSizeColumn( COL_NUMBER );
  520. if( m_grid->GetColSize( COL_NUMBER ) < m_originalColWidths[ COL_NUMBER ] )
  521. m_grid->SetColSize( COL_NUMBER, m_originalColWidths[ COL_NUMBER ] );
  522. m_grid->AutoSizeColumn( COL_NAME );
  523. if( m_grid->GetColSize( COL_NAME ) < m_originalColWidths[ COL_NAME ] )
  524. m_grid->SetColSize( COL_NAME, m_originalColWidths[ COL_NAME ] );
  525. // If the grid is still wider than the columns, then stretch the Number and Name columns
  526. // to fit.
  527. for( int i = 0; i < COL_COUNT; ++i )
  528. aWidth -= m_grid->GetColSize( i );
  529. if( aWidth > 0 )
  530. {
  531. m_grid->SetColSize( COL_NUMBER, m_grid->GetColSize( COL_NUMBER ) + aWidth / 2 );
  532. m_grid->SetColSize( COL_NAME, m_grid->GetColSize( COL_NAME ) + aWidth / 2 );
  533. }
  534. }
  535. void DIALOG_LIB_EDIT_PIN_TABLE::OnSize( wxSizeEvent& event )
  536. {
  537. auto new_size = event.GetSize().GetX();
  538. if( m_initialized && m_width != new_size )
  539. {
  540. adjustGridColumns( new_size );
  541. }
  542. // Always propagate for a grid repaint (needed if the height changes, as well as width)
  543. event.Skip();
  544. }
  545. void DIALOG_LIB_EDIT_PIN_TABLE::OnUpdateUI( wxUpdateUIEvent& event )
  546. {
  547. wxString columnsShown = m_grid->GetShownColumns();
  548. if( columnsShown != m_columnsShown )
  549. {
  550. m_columnsShown = columnsShown;
  551. if( !m_grid->IsCellEditControlShown() )
  552. adjustGridColumns( m_grid->GetRect().GetWidth() );
  553. }
  554. }
  555. void DIALOG_LIB_EDIT_PIN_TABLE::OnCancel( wxCommandEvent& event )
  556. {
  557. Close();
  558. }
  559. void DIALOG_LIB_EDIT_PIN_TABLE::OnClose( wxCloseEvent& event )
  560. {
  561. // This is a cancel, so commit quietly as we're going to throw the results away anyway.
  562. m_grid->CommitPendingChanges( true );
  563. if( m_dataModel->IsEdited() )
  564. {
  565. if( !HandleUnsavedChanges( this, wxEmptyString,
  566. [&]()->bool { return TransferDataFromWindow(); } ) )
  567. {
  568. event.Veto();
  569. return;
  570. }
  571. }
  572. if( IsQuasiModal() )
  573. EndQuasiModal( wxID_CANCEL );
  574. else if( IsModal() )
  575. EndModal( wxID_CANCEL );
  576. else
  577. event.Skip();
  578. }
  579. void DIALOG_LIB_EDIT_PIN_TABLE::updateSummary()
  580. {
  581. PinNumbers pinNumbers;
  582. for( LIB_PIN* pin : m_pins )
  583. {
  584. if( pin->GetNumber().Length() )
  585. pinNumbers.insert( pin->GetNumber() );
  586. }
  587. m_summary->SetLabel( pinNumbers.GetSummary() );
  588. }