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.

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