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.

834 lines
24 KiB

3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018-2023 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 3
  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 <wx/colour.h>
  24. #include <wx/tokenzr.h>
  25. #include <wx/dc.h>
  26. #include <wx/settings.h>
  27. #include <wx/event.h> // Needed for textentry.h on MSW
  28. #include <wx/textentry.h>
  29. #include <widgets/grid_icon_text_helpers.h>
  30. #include <widgets/wx_grid.h>
  31. #include <widgets/ui_common.h>
  32. #include <algorithm>
  33. #include <core/kicad_algo.h>
  34. #include <gal/color4d.h>
  35. #include <kiplatform/ui.h>
  36. #include <utility>
  37. #include <pgm_base.h>
  38. #include <settings/common_settings.h>
  39. #include <dialog_shim.h>
  40. wxGridCellAttr* WX_GRID_TABLE_BASE::enhanceAttr( wxGridCellAttr* aInputAttr, int aRow, int aCol,
  41. wxGridCellAttr::wxAttrKind aKind )
  42. {
  43. wxGridCellAttr* attr = aInputAttr;
  44. if( wxGridCellAttrProvider* provider = GetAttrProvider() )
  45. {
  46. wxGridCellAttr* providerAttr = provider->GetAttr( aRow, aCol, aKind );
  47. if( providerAttr )
  48. {
  49. attr = new wxGridCellAttr;
  50. attr->SetKind( wxGridCellAttr::Merged );
  51. if( aInputAttr )
  52. {
  53. attr->MergeWith( aInputAttr );
  54. aInputAttr->DecRef();
  55. }
  56. attr->MergeWith( providerAttr );
  57. providerAttr->DecRef();
  58. }
  59. }
  60. return attr;
  61. }
  62. #define MIN_GRIDCELL_MARGIN FromDIP( 2 )
  63. void WX_GRID::CellEditorSetMargins( wxTextEntryBase* aEntry )
  64. {
  65. // This is consistent with wxGridCellTextEditor. But works differently across platforms or course.
  66. aEntry->SetMargins( 0, 0 );
  67. }
  68. void WX_GRID::CellEditorTransformSizeRect( wxRect& aRect )
  69. {
  70. #if defined( __WXMSW__ ) || defined( __WXGTK__ )
  71. aRect.Deflate( 2 );
  72. #endif
  73. }
  74. wxColour getBorderColour()
  75. {
  76. KIGFX::COLOR4D bg = wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK );
  77. KIGFX::COLOR4D fg = wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER );
  78. KIGFX::COLOR4D border = fg.Mix( bg, 0.50 );
  79. return border.ToColour();
  80. }
  81. class WX_GRID_CORNER_HEADER_RENDERER : public wxGridCornerHeaderRendererDefault
  82. {
  83. public:
  84. void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
  85. {
  86. wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
  87. wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
  88. rect.SetTop( rect.GetTop() + 1 );
  89. rect.SetLeft( rect.GetLeft() + 1 );
  90. rect.SetBottom( rect.GetBottom() - 1 );
  91. rect.SetRight( rect.GetRight() - 1 );
  92. dc.DrawRectangle( rect );
  93. }
  94. };
  95. class WX_GRID_COLUMN_HEADER_RENDERER : public wxGridColumnHeaderRendererDefault
  96. {
  97. public:
  98. void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
  99. {
  100. wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
  101. wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
  102. rect.SetTop( rect.GetTop() + 1 );
  103. rect.SetLeft( rect.GetLeft() );
  104. rect.SetBottom( rect.GetBottom() - 1 );
  105. rect.SetRight( rect.GetRight() - 1 );
  106. dc.DrawRectangle( rect );
  107. }
  108. };
  109. class WX_GRID_ROW_HEADER_RENDERER : public wxGridRowHeaderRendererDefault
  110. {
  111. public:
  112. void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
  113. {
  114. wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
  115. wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
  116. rect.SetTop( rect.GetTop() + 1 );
  117. rect.SetLeft( rect.GetLeft() + 1 );
  118. rect.SetBottom( rect.GetBottom() - 1 );
  119. rect.SetRight( rect.GetRight() );
  120. dc.DrawRectangle( rect );
  121. }
  122. };
  123. /**
  124. * Attribute provider that provides attributes (or modifies the existing attribute) to alternate a row color
  125. * between the odd and even rows.
  126. */
  127. class WX_GRID_ALT_ROW_COLOR_PROVIDER : public wxGridCellAttrProvider
  128. {
  129. public:
  130. WX_GRID_ALT_ROW_COLOR_PROVIDER( const wxColor& aBaseColor ) : wxGridCellAttrProvider(),
  131. m_attrEven( new wxGridCellAttr() )
  132. {
  133. UpdateColors( aBaseColor );
  134. }
  135. void UpdateColors( const wxColor& aBaseColor )
  136. {
  137. // Choose the default color, taking into account if the dark mode theme is enabled
  138. wxColor rowColor = aBaseColor.ChangeLightness( KIPLATFORM::UI::IsDarkTheme() ? 105 : 95 );
  139. m_attrEven->SetBackgroundColour( rowColor );
  140. }
  141. wxGridCellAttr* GetAttr( int row, int col,
  142. wxGridCellAttr::wxAttrKind kind ) const override
  143. {
  144. wxGridCellAttrPtr cellAttr( wxGridCellAttrProvider::GetAttr( row, col, kind ) );
  145. // Just pass through the cell attribute on odd rows (start normal to allow for the header row)
  146. if( !( row % 2 ) )
  147. return cellAttr.release();
  148. if( !cellAttr )
  149. {
  150. cellAttr = m_attrEven;
  151. }
  152. else
  153. {
  154. if( !cellAttr->HasBackgroundColour() )
  155. {
  156. cellAttr = cellAttr->Clone();
  157. cellAttr->SetBackgroundColour( m_attrEven->GetBackgroundColour() );
  158. }
  159. }
  160. return cellAttr.release();
  161. }
  162. private:
  163. wxGridCellAttrPtr m_attrEven;
  164. };
  165. WX_GRID::WX_GRID( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
  166. long style, const wxString& name ) :
  167. wxGrid( parent, id, pos, size, style, name ),
  168. m_weOwnTable( false )
  169. {
  170. SetDefaultCellOverflow( false );
  171. // Make sure the GUI font scales properly
  172. SetDefaultCellFont( KIUI::GetControlFont( this ) );
  173. SetLabelFont( KIUI::GetControlFont( this ) );
  174. Connect( wxEVT_DPI_CHANGED, wxDPIChangedEventHandler( WX_GRID::onDPIChanged ), nullptr, this );
  175. Connect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( WX_GRID::onCellEditorShown ), nullptr, this );
  176. Connect( wxEVT_GRID_EDITOR_HIDDEN, wxGridEventHandler( WX_GRID::onCellEditorHidden ), nullptr, this );
  177. }
  178. WX_GRID::~WX_GRID()
  179. {
  180. if( m_weOwnTable )
  181. DestroyTable( GetTable() );
  182. Disconnect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( WX_GRID::onCellEditorShown ), nullptr, this );
  183. Disconnect( wxEVT_GRID_EDITOR_HIDDEN, wxGridEventHandler( WX_GRID::onCellEditorHidden ), nullptr, this );
  184. Disconnect( wxEVT_DPI_CHANGED, wxDPIChangedEventHandler( WX_GRID::onDPIChanged ), nullptr, this );
  185. }
  186. void WX_GRID::onDPIChanged(wxDPIChangedEvent& aEvt)
  187. {
  188. CallAfter(
  189. [&]()
  190. {
  191. wxGrid::SetColLabelSize( wxGRID_AUTOSIZE );
  192. } );
  193. /// This terrible hack is a way to avoid the incredibly disruptive resizing of grids that happens on Macs
  194. /// when moving a window between monitors of different DPIs.
  195. #ifndef __WXMAC__
  196. aEvt.Skip();
  197. #endif
  198. }
  199. void WX_GRID::SetColLabelSize( int aHeight )
  200. {
  201. if( aHeight == 0 || aHeight == wxGRID_AUTOSIZE )
  202. {
  203. wxGrid::SetColLabelSize( aHeight );
  204. return;
  205. }
  206. // Correct wxFormBuilder height for large fonts
  207. int minHeight = GetCharHeight() + 2 * MIN_GRIDCELL_MARGIN;
  208. wxGrid::SetColLabelSize( std::max( aHeight, minHeight ) );
  209. }
  210. void WX_GRID::SetLabelFont( const wxFont& aFont )
  211. {
  212. wxGrid::SetLabelFont( KIUI::GetControlFont( this ) );
  213. }
  214. void WX_GRID::SetTable( wxGridTableBase* aTable, bool aTakeOwnership )
  215. {
  216. // wxGrid::SetTable() messes up the column widths from wxFormBuilder so we have to save
  217. // and restore them.
  218. int numberCols = GetNumberCols();
  219. int* formBuilderColWidths = new int[numberCols];
  220. for( int i = 0; i < numberCols; ++i )
  221. formBuilderColWidths[ i ] = GetColSize( i );
  222. wxGrid::SetTable( aTable );
  223. // wxGrid::SetTable() may change the number of columns, so prevent out-of-bounds access
  224. // to formBuilderColWidths
  225. numberCols = std::min( numberCols, GetNumberCols() );
  226. for( int i = 0; i < numberCols; ++i )
  227. {
  228. // correct wxFormBuilder width for large fonts and/or long translations
  229. int headingWidth = GetTextExtent( GetColLabelValue( i ) ).x + 2 * MIN_GRIDCELL_MARGIN;
  230. SetColSize( i, std::max( formBuilderColWidths[ i ], headingWidth ) );
  231. }
  232. delete[] formBuilderColWidths;
  233. EnableAlternateRowColors( Pgm().GetCommonSettings()->m_Appearance.grid_striping );
  234. Connect( wxEVT_GRID_COL_MOVE, wxGridEventHandler( WX_GRID::onGridColMove ), nullptr, this );
  235. Connect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( WX_GRID::onGridCellSelect ), nullptr, this );
  236. m_weOwnTable = aTakeOwnership;
  237. }
  238. void WX_GRID::EnableAlternateRowColors( bool aEnable )
  239. {
  240. wxGridTableBase* table = wxGrid::GetTable();
  241. wxCHECK_MSG( table, /* void */,
  242. "Tried to enable alternate row colors without a table assigned to the grid" );
  243. if( aEnable )
  244. {
  245. wxColor color = wxGrid::GetDefaultCellBackgroundColour();
  246. table->SetAttrProvider( new WX_GRID_ALT_ROW_COLOR_PROVIDER( color ) );
  247. }
  248. else
  249. {
  250. table->SetAttrProvider( nullptr );
  251. }
  252. }
  253. void WX_GRID::onGridCellSelect( wxGridEvent& aEvent )
  254. {
  255. // Highlight the selected cell.
  256. // Calling SelectBlock() allows a visual effect when cells are selected by tab or arrow keys.
  257. // Otherwise, one cannot really know what actual cell is selected.
  258. int row = aEvent.GetRow();
  259. int col = aEvent.GetCol();
  260. if( row >= 0 && row < GetNumberRows() && col >= 0 && col < GetNumberCols() )
  261. {
  262. if( GetSelectionMode() == wxGrid::wxGridSelectCells )
  263. {
  264. SelectBlock( row, col, row, col, false );
  265. }
  266. else if( GetSelectionMode() == wxGrid::wxGridSelectRows
  267. || GetSelectionMode() == wxGrid::wxGridSelectRowsOrColumns )
  268. {
  269. SelectBlock( row, 0, row, GetNumberCols() - 1, false );
  270. }
  271. else if( GetSelectionMode() == wxGrid::wxGridSelectColumns )
  272. {
  273. SelectBlock( 0, col, GetNumberRows() - 1, col, false );
  274. }
  275. }
  276. }
  277. void WX_GRID::onCellEditorShown( wxGridEvent& aEvent )
  278. {
  279. if( alg::contains( m_autoEvalCols, aEvent.GetCol() ) )
  280. {
  281. int row = aEvent.GetRow();
  282. int col = aEvent.GetCol();
  283. const std::pair<wxString, wxString>& beforeAfter = m_evalBeforeAfter[ { row, col } ];
  284. if( GetCellValue( row, col ) == beforeAfter.second )
  285. SetCellValue( row, col, beforeAfter.first );
  286. }
  287. }
  288. void WX_GRID::onCellEditorHidden( wxGridEvent& aEvent )
  289. {
  290. if( alg::contains( m_autoEvalCols, aEvent.GetCol() ) )
  291. {
  292. UNITS_PROVIDER* unitsProvider = m_unitsProviders[ aEvent.GetCol() ];
  293. if( !unitsProvider )
  294. unitsProvider = m_unitsProviders.begin()->second;
  295. m_eval->SetDefaultUnits( unitsProvider->GetUserUnits() );
  296. int row = aEvent.GetRow();
  297. int col = aEvent.GetCol();
  298. // Determine if this cell is marked as holding nullable values
  299. bool isNullable = false;
  300. wxGridCellEditor* cellEditor = GetCellEditor( row, col );
  301. if( cellEditor )
  302. {
  303. GRID_CELL_MARK_AS_NULLABLE* nullableCell =
  304. dynamic_cast<GRID_CELL_MARK_AS_NULLABLE*>( cellEditor );
  305. if( nullableCell )
  306. isNullable = nullableCell->IsNullable();
  307. cellEditor->DecRef();
  308. }
  309. CallAfter(
  310. [this, row, col, isNullable, unitsProvider]()
  311. {
  312. wxString stringValue = GetCellValue( row, col );
  313. bool processedOk = true;
  314. if( stringValue != UNITS_PROVIDER::NullUiString )
  315. processedOk = m_eval->Process( stringValue );
  316. if( processedOk )
  317. {
  318. wxString evalValue;
  319. if( isNullable )
  320. {
  321. std::optional<int> val;
  322. if( stringValue == UNITS_PROVIDER::NullUiString )
  323. {
  324. val = unitsProvider->OptionalValueFromString(
  325. UNITS_PROVIDER::NullUiString );
  326. }
  327. else
  328. {
  329. val = unitsProvider->OptionalValueFromString( m_eval->Result() );
  330. }
  331. evalValue = unitsProvider->StringFromOptionalValue( val, true );
  332. }
  333. else
  334. {
  335. int val = unitsProvider->ValueFromString( m_eval->Result() );
  336. evalValue = unitsProvider->StringFromValue( val, true );
  337. }
  338. if( stringValue != evalValue )
  339. {
  340. SetCellValue( row, col, evalValue );
  341. m_evalBeforeAfter[{ row, col }] = { stringValue, evalValue };
  342. }
  343. }
  344. } );
  345. }
  346. aEvent.Skip();
  347. }
  348. void WX_GRID::DestroyTable( wxGridTableBase* aTable )
  349. {
  350. // wxGrid's destructor will crash trying to look up the cell attr if the edit control
  351. // is left open. Normally it's closed in Validate(), but not if the user hit Cancel.
  352. CommitPendingChanges( true /* quiet mode */ );
  353. Disconnect( wxEVT_GRID_COL_MOVE, wxGridEventHandler( WX_GRID::onGridColMove ), nullptr, this );
  354. Disconnect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( WX_GRID::onGridCellSelect ), nullptr, this );
  355. wxGrid::SetTable( nullptr );
  356. delete aTable;
  357. }
  358. wxString WX_GRID::GetShownColumnsAsString()
  359. {
  360. wxString shownColumns;
  361. for( int i = 0; i < GetNumberCols(); ++i )
  362. {
  363. if( IsColShown( i ) )
  364. {
  365. if( shownColumns.Length() )
  366. shownColumns << wxT( " " );
  367. shownColumns << i;
  368. }
  369. }
  370. return shownColumns;
  371. }
  372. std::bitset<64> WX_GRID::GetShownColumns()
  373. {
  374. std::bitset<64> shownColumns;
  375. for( int ii = 0; ii < GetNumberCols(); ++ii )
  376. shownColumns[ii] = IsColShown( ii );
  377. return shownColumns;
  378. }
  379. void WX_GRID::ShowHideColumns( const wxString& shownColumns )
  380. {
  381. for( int i = 0; i < GetNumberCols(); ++i )
  382. HideCol( i );
  383. wxStringTokenizer shownTokens( shownColumns );
  384. while( shownTokens.HasMoreTokens() )
  385. {
  386. long colNumber;
  387. shownTokens.GetNextToken().ToLong( &colNumber );
  388. if( colNumber >= 0 && colNumber < GetNumberCols() )
  389. ShowCol( (int) colNumber );
  390. }
  391. }
  392. void WX_GRID::ShowHideColumns( const std::bitset<64>& aShownColumns )
  393. {
  394. for( int ii = 0; ii < GetNumberCols(); ++ ii )
  395. {
  396. if( aShownColumns[ii] )
  397. ShowCol( ii );
  398. else
  399. HideCol( ii );
  400. }
  401. }
  402. void WX_GRID::DrawCornerLabel( wxDC& dc )
  403. {
  404. if( m_nativeColumnLabels )
  405. wxGrid::DrawCornerLabel( dc );
  406. wxRect rect( wxSize( m_rowLabelWidth, m_colLabelHeight ) );
  407. static WX_GRID_CORNER_HEADER_RENDERER rend;
  408. // It is reported that we need to erase the background to avoid display
  409. // artifacts, see #12055.
  410. {
  411. wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
  412. wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
  413. dc.DrawRectangle( rect.Inflate( 1 ) );
  414. }
  415. rend.DrawBorder( *this, dc, rect );
  416. }
  417. void WX_GRID::DrawColLabel( wxDC& dc, int col )
  418. {
  419. if( m_nativeColumnLabels )
  420. wxGrid::DrawColLabel( dc, col );
  421. if( GetColWidth( col ) <= 0 || m_colLabelHeight <= 0 )
  422. return;
  423. wxRect rect( GetColLeft( col ), 0, GetColWidth( col ), m_colLabelHeight );
  424. static WX_GRID_COLUMN_HEADER_RENDERER rend;
  425. // It is reported that we need to erase the background to avoid display
  426. // artifacts, see #12055.
  427. {
  428. wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
  429. wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
  430. dc.DrawRectangle( rect.Inflate( 1 ) );
  431. }
  432. rend.DrawBorder( *this, dc, rect );
  433. // Make sure fonts get scaled correctly on GTK HiDPI monitors
  434. dc.SetFont( GetLabelFont() );
  435. int hAlign, vAlign;
  436. GetColLabelAlignment( &hAlign, &vAlign );
  437. const int orient = GetColLabelTextOrientation();
  438. if( col == 0 )
  439. hAlign = wxALIGN_LEFT;
  440. if( hAlign == wxALIGN_LEFT )
  441. rect.SetLeft( rect.GetLeft() + MIN_GRIDCELL_MARGIN );
  442. rend.DrawLabel( *this, dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient );
  443. }
  444. void WX_GRID::DrawRowLabel( wxDC& dc, int row )
  445. {
  446. if ( GetRowHeight( row ) <= 0 || m_rowLabelWidth <= 0 )
  447. return;
  448. wxRect rect( 0, GetRowTop( row ), m_rowLabelWidth, GetRowHeight( row ) );
  449. static WX_GRID_ROW_HEADER_RENDERER rend;
  450. // It is reported that we need to erase the background to avoid display
  451. // artifacts, see #12055.
  452. {
  453. wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
  454. wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
  455. dc.DrawRectangle( rect.Inflate( 1 ) );
  456. }
  457. rend.DrawBorder( *this, dc, rect );
  458. // Make sure fonts get scaled correctly on GTK HiDPI monitors
  459. dc.SetFont( GetLabelFont() );
  460. int hAlign, vAlign;
  461. GetRowLabelAlignment(&hAlign, &vAlign);
  462. if( hAlign == wxALIGN_LEFT )
  463. rect.SetLeft( rect.GetLeft() + MIN_GRIDCELL_MARGIN );
  464. rend.DrawLabel( *this, dc, GetRowLabelValue( row ), rect, hAlign, vAlign, wxHORIZONTAL );
  465. }
  466. bool WX_GRID::CancelPendingChanges()
  467. {
  468. if( !IsCellEditControlEnabled() )
  469. return true;
  470. HideCellEditControl();
  471. // do it after HideCellEditControl()
  472. m_cellEditCtrlEnabled = false;
  473. int row = m_currentCellCoords.GetRow();
  474. int col = m_currentCellCoords.GetCol();
  475. wxString oldval = GetCellValue( row, col );
  476. wxString newval;
  477. wxGridCellAttr* attr = GetCellAttr( row, col );
  478. wxGridCellEditor* editor = attr->GetEditor( this, row, col );
  479. editor->EndEdit( row, col, this, oldval, &newval );
  480. editor->DecRef();
  481. attr->DecRef();
  482. return true;
  483. }
  484. bool WX_GRID::CommitPendingChanges( bool aQuietMode )
  485. {
  486. if( !IsCellEditControlEnabled() )
  487. return true;
  488. if( !aQuietMode && SendEvent( wxEVT_GRID_EDITOR_HIDDEN ) == -1 )
  489. return false;
  490. HideCellEditControl();
  491. // do it after HideCellEditControl()
  492. m_cellEditCtrlEnabled = false;
  493. int row = m_currentCellCoords.GetRow();
  494. int col = m_currentCellCoords.GetCol();
  495. wxString oldval = GetCellValue( row, col );
  496. wxString newval;
  497. wxGridCellAttr* attr = GetCellAttr( row, col );
  498. wxGridCellEditor* editor = attr->GetEditor( this, row, col );
  499. bool changed = editor->EndEdit( row, col, this, oldval, &newval );
  500. editor->DecRef();
  501. attr->DecRef();
  502. if( changed )
  503. {
  504. if( !aQuietMode && SendEvent( wxEVT_GRID_CELL_CHANGING, newval ) == -1 )
  505. return false;
  506. editor->ApplyEdit( row, col, this );
  507. // for compatibility reasons dating back to wx 2.8 when this event
  508. // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
  509. // didn't exist we allow vetoing this one too
  510. if( !aQuietMode && SendEvent( wxEVT_GRID_CELL_CHANGED, oldval ) == -1 )
  511. {
  512. // Event has been vetoed, set the data back.
  513. SetCellValue( row, col, oldval );
  514. return false;
  515. }
  516. if( DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
  517. dlg->OnModify();
  518. }
  519. return true;
  520. }
  521. void WX_GRID::SetUnitsProvider( UNITS_PROVIDER* aProvider, int aCol )
  522. {
  523. m_unitsProviders[ aCol ] = aProvider;
  524. if( !m_eval )
  525. m_eval = std::make_unique<NUMERIC_EVALUATOR>( aProvider->GetUserUnits() );
  526. }
  527. int WX_GRID::GetUnitValue( int aRow, int aCol )
  528. {
  529. UNITS_PROVIDER* unitsProvider = m_unitsProviders[ aCol ];
  530. if( !unitsProvider )
  531. unitsProvider = m_unitsProviders.begin()->second;
  532. wxString stringValue = GetCellValue( aRow, aCol );
  533. if( alg::contains( m_autoEvalCols, aCol ) )
  534. {
  535. m_eval->SetDefaultUnits( unitsProvider->GetUserUnits() );
  536. if( m_eval->Process( stringValue ) )
  537. stringValue = m_eval->Result();
  538. }
  539. return unitsProvider->ValueFromString( stringValue );
  540. }
  541. std::optional<int> WX_GRID::GetOptionalUnitValue( int aRow, int aCol )
  542. {
  543. UNITS_PROVIDER* unitsProvider = m_unitsProviders[aCol];
  544. if( !unitsProvider )
  545. unitsProvider = m_unitsProviders.begin()->second;
  546. wxString stringValue = GetCellValue( aRow, aCol );
  547. if( alg::contains( m_autoEvalCols, aCol ) )
  548. {
  549. m_eval->SetDefaultUnits( unitsProvider->GetUserUnits() );
  550. if( stringValue != UNITS_PROVIDER::NullUiString && m_eval->Process( stringValue ) )
  551. stringValue = m_eval->Result();
  552. }
  553. return unitsProvider->OptionalValueFromString( stringValue );
  554. }
  555. void WX_GRID::SetUnitValue( int aRow, int aCol, int aValue )
  556. {
  557. UNITS_PROVIDER* unitsProvider = m_unitsProviders[ aCol ];
  558. if( !unitsProvider )
  559. unitsProvider = m_unitsProviders.begin()->second;
  560. SetCellValue( aRow, aCol, unitsProvider->StringFromValue( aValue, true ) );
  561. }
  562. void WX_GRID::SetOptionalUnitValue( int aRow, int aCol, std::optional<int> aValue )
  563. {
  564. UNITS_PROVIDER* unitsProvider = m_unitsProviders[aCol];
  565. if( !unitsProvider )
  566. unitsProvider = m_unitsProviders.begin()->second;
  567. SetCellValue( aRow, aCol, unitsProvider->StringFromOptionalValue( aValue, true ) );
  568. }
  569. void WX_GRID::onGridColMove( wxGridEvent& aEvent )
  570. {
  571. // wxWidgets won't move an open editor, so better just to close it
  572. CommitPendingChanges( true );
  573. }
  574. int WX_GRID::GetVisibleWidth( int aCol, bool aHeader, bool aContents, bool aKeep )
  575. {
  576. int size = 0;
  577. if( aCol < 0 )
  578. {
  579. if( aKeep )
  580. size = GetRowLabelSize();
  581. for( int row = 0; aContents && row < GetNumberRows(); row++ )
  582. size = std::max( size, int( GetTextExtent( GetRowLabelValue( row ) + wxS( "M" ) ).x ) );
  583. }
  584. else
  585. {
  586. if( aKeep )
  587. size = GetColSize( aCol );
  588. // 'M' is generally the widest character, so we buffer the column width by default to
  589. // ensure we don't write a continuous line of text at the column header
  590. if( aHeader )
  591. {
  592. EnsureColLabelsVisible();
  593. size = std::max( size, int( GetTextExtent( GetColLabelValue( aCol ) + wxS( "M" ) ).x ) );
  594. }
  595. for( int row = 0; aContents && row < GetNumberRows(); row++ )
  596. {
  597. // If we have text, get the size. Otherwise, use a placeholder for the checkbox
  598. if( GetTable()->CanGetValueAs( row, aCol, wxGRID_VALUE_STRING ) )
  599. size = std::max( size, GetTextExtent( GetCellValue( row, aCol ) + wxS( "M" ) ).x );
  600. else
  601. size = std::max( size, GetTextExtent( "MM" ).x );
  602. }
  603. }
  604. return size;
  605. }
  606. void WX_GRID::EnsureColLabelsVisible()
  607. {
  608. int line_height = int( GetTextExtent( "Mj" ).y ) + 3;
  609. int row_height = GetColLabelSize();
  610. int initial_row_height = row_height;
  611. // Headers can be multiline. Fix the Column Label Height to show the full header
  612. // However GetTextExtent does not work on multiline strings,
  613. // and do not return the full text height (only the height of one line)
  614. for( int col = 0; col < GetNumberCols(); col++ )
  615. {
  616. int nl_count = GetColLabelValue( col ).Freq( '\n' );
  617. if( nl_count )
  618. {
  619. // Col Label height must be able to show nl_count+1 lines
  620. if( row_height < line_height * ( nl_count+1 ) )
  621. row_height += line_height * nl_count;
  622. }
  623. }
  624. // Update the column label size, but only if needed, to avoid generating useless
  625. // and perhaps annoying UI events when the size does not change
  626. if( initial_row_height != row_height )
  627. SetColLabelSize( row_height );
  628. }