KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_grid.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 The 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
24#include <wx/colour.h>
25#include <wx/tokenzr.h>
26#include <wx/dc.h>
27#include <wx/settings.h>
28#include <wx/event.h> // Needed for textentry.h on MSW
29#include <wx/textentry.h>
30
32#include <widgets/wx_grid.h>
33#include <widgets/ui_common.h>
34#include <algorithm>
35#include <core/kicad_algo.h>
36#include <gal/color4d.h>
37#include <kiplatform/ui.h>
38#include <utility>
39
40#include <pgm_base.h>
42#include <dialog_shim.h>
43
44
45wxGridCellAttr* WX_GRID_TABLE_BASE::enhanceAttr( wxGridCellAttr* aInputAttr, int aRow, int aCol,
46 wxGridCellAttr::wxAttrKind aKind )
47{
48 wxGridCellAttr* attr = aInputAttr;
49
50 if( wxGridCellAttrProvider* provider = GetAttrProvider() )
51 {
52 wxGridCellAttr* providerAttr = provider->GetAttr( aRow, aCol, aKind );
53
54 if( providerAttr )
55 {
56 attr = new wxGridCellAttr;
57 attr->SetKind( wxGridCellAttr::Merged );
58
59 if( aInputAttr )
60 {
61 attr->MergeWith( aInputAttr );
62 aInputAttr->DecRef();
63 }
64
65 attr->MergeWith( providerAttr );
66 providerAttr->DecRef();
67 }
68 }
69
70 return attr;
71}
72
73
74#define MIN_GRIDCELL_MARGIN FromDIP( 2 )
75
76
77void WX_GRID::CellEditorSetMargins( wxTextEntryBase* aEntry )
78{
79 // This is consistent with wxGridCellTextEditor. But works differently across platforms of course.
80 aEntry->SetMargins( 0, 0 );
81}
82
83
85{
86#if defined( __WXMSW__ ) || defined( __WXGTK__ )
87 aRect.Deflate( 2 );
88#endif
89}
90
91
93{
94 KIGFX::COLOR4D bg = wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK );
95 KIGFX::COLOR4D fg = wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER );
96 KIGFX::COLOR4D border = fg.Mix( bg, 0.50 );
97 return border.ToColour();
98}
99
100
101class WX_GRID_CORNER_HEADER_RENDERER : public wxGridCornerHeaderRendererDefault
102{
103public:
104 void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
105 {
106 wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
107 wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
108
109 rect.SetTop( rect.GetTop() + 1 );
110 rect.SetLeft( rect.GetLeft() + 1 );
111 rect.SetBottom( rect.GetBottom() - 1 );
112 rect.SetRight( rect.GetRight() - 1 );
113 dc.DrawRectangle( rect );
114 }
115};
116
117
118class WX_GRID_COLUMN_HEADER_RENDERER : public wxGridColumnHeaderRendererDefault
119{
120public:
121 void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
122 {
123 wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
124 wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
125
126 rect.SetTop( rect.GetTop() + 1 );
127 rect.SetLeft( rect.GetLeft() );
128 rect.SetBottom( rect.GetBottom() - 1 );
129 rect.SetRight( rect.GetRight() - 1 );
130 dc.DrawRectangle( rect );
131 }
132};
133
134
135class WX_GRID_ROW_HEADER_RENDERER : public wxGridRowHeaderRendererDefault
136{
137public:
138 void DrawBorder( const wxGrid& grid, wxDC& dc, wxRect& rect ) const override
139 {
140 wxDCBrushChanger SetBrush( dc, *wxTRANSPARENT_BRUSH );
141 wxDCPenChanger SetPen( dc, wxPen( getBorderColour(), 1 ) );
142
143 rect.SetTop( rect.GetTop() + 1 );
144 rect.SetLeft( rect.GetLeft() + 1 );
145 rect.SetBottom( rect.GetBottom() - 1 );
146 rect.SetRight( rect.GetRight() );
147 dc.DrawRectangle( rect );
148 }
149};
150
151
156class WX_GRID_ALT_ROW_COLOR_PROVIDER : public wxGridCellAttrProvider
157{
158public:
159 WX_GRID_ALT_ROW_COLOR_PROVIDER( const wxColor& aBaseColor ) :
160 wxGridCellAttrProvider(),
161 m_attrEven( new wxGridCellAttr() )
162 {
163 UpdateColors( aBaseColor );
164 }
165
166 void UpdateColors( const wxColor& aBaseColor )
167 {
168 // Choose the default color, taking into account if the dark mode theme is enabled
169 wxColor rowColor = aBaseColor.ChangeLightness( KIPLATFORM::UI::IsDarkTheme() ? 105 : 95 );
170
171 m_attrEven->SetBackgroundColour( rowColor );
172 }
173
174 wxGridCellAttr* GetAttr( int row, int col, wxGridCellAttr::wxAttrKind kind ) const override
175 {
176 wxGridCellAttrPtr cellAttr( wxGridCellAttrProvider::GetAttr( row, col, kind ) );
177
178 // Just pass through the cell attribute on odd rows (start normal to allow for the
179 // header row)
180 if( !( row % 2 ) )
181 return cellAttr.release();
182
183 if( !cellAttr )
184 {
185 cellAttr = m_attrEven;
186 }
187 else
188 {
189 if( !cellAttr->HasBackgroundColour() )
190 {
191 cellAttr = cellAttr->Clone();
192 cellAttr->SetBackgroundColour( m_attrEven->GetBackgroundColour() );
193 }
194 }
195
196 return cellAttr.release();
197 }
198
199private:
200 wxGridCellAttrPtr m_attrEven;
201};
202
203
204WX_GRID::WX_GRID( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style,
205 const wxString& name ) :
206 wxGrid( parent, id, pos, size, style, name ),
207 m_weOwnTable( false )
208{
209 // Grids with comboboxes need a bit of extra height; other grids look better if they're consistent.
210 SetDefaultRowSize( GetDefaultRowSize() + FromDIP( 4 ) );
211
212 SetDefaultCellOverflow( false );
213
214 // Make sure the GUI font scales properly
215 SetDefaultCellFont( KIUI::GetControlFont( this ) );
217
218 Connect( wxEVT_DPI_CHANGED, wxDPIChangedEventHandler( WX_GRID::onDPIChanged ), nullptr, this );
219 Connect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( WX_GRID::onCellEditorShown ), nullptr, this );
220 Connect( wxEVT_GRID_EDITOR_HIDDEN, wxGridEventHandler( WX_GRID::onCellEditorHidden ), nullptr, this );
221}
222
223
225{
226 if( m_weOwnTable )
227 DestroyTable( GetTable() );
228
229 Disconnect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( WX_GRID::onCellEditorShown ), nullptr, this );
230 Disconnect( wxEVT_GRID_EDITOR_HIDDEN, wxGridEventHandler( WX_GRID::onCellEditorHidden ), nullptr, this );
231 Disconnect( wxEVT_DPI_CHANGED, wxDPIChangedEventHandler( WX_GRID::onDPIChanged ), nullptr, this );
232}
233
234
235void WX_GRID::onDPIChanged(wxDPIChangedEvent& aEvt)
236{
237 CallAfter( [&]()
238 {
239 wxGrid::SetColLabelSize( wxGRID_AUTOSIZE );
240 } );
241
242 // Don't skip, otherwise the grid gets too big
243}
244
245
246void WX_GRID::SetColLabelSize( int aHeight )
247{
248 if( aHeight == 0 || aHeight == wxGRID_AUTOSIZE )
249 {
250 wxGrid::SetColLabelSize( aHeight );
251 return;
252 }
253
254 // Correct wxFormBuilder height for large fonts
255 int minHeight = GetCharHeight() + 2 * MIN_GRIDCELL_MARGIN;
256
257 wxGrid::SetColLabelSize( std::max( aHeight, minHeight ) );
258}
259
260
261void WX_GRID::SetLabelFont( const wxFont& aFont )
262{
263 wxGrid::SetLabelFont( KIUI::GetControlFont( this ) );
264}
265
266
267void WX_GRID::SetTable( wxGridTableBase* aTable, bool aTakeOwnership )
268{
269 // wxGrid::SetTable() messes up the column widths from wxFormBuilder so we have to save
270 // and restore them.
271 int numberCols = GetNumberCols();
272 int* formBuilderColWidths = new int[numberCols];
273
274 for( int i = 0; i < numberCols; ++i )
275 formBuilderColWidths[ i ] = GetColSize( i );
276
277 wxGrid::SetTable( aTable );
278
279 // wxGrid::SetTable() may change the number of columns, so prevent out-of-bounds access
280 // to formBuilderColWidths
281 numberCols = std::min( numberCols, GetNumberCols() );
282
283 for( int i = 0; i < numberCols; ++i )
284 {
285 // correct wxFormBuilder width for large fonts and/or long translations
286 int headingWidth = GetTextExtent( GetColLabelValue( i ) ).x + 2 * MIN_GRIDCELL_MARGIN;
287
288 SetColSize( i, std::max( formBuilderColWidths[ i ], headingWidth ) );
289 }
290
291 delete[] formBuilderColWidths;
292
293 EnableAlternateRowColors( Pgm().GetCommonSettings() && Pgm().GetCommonSettings()->m_Appearance.grid_striping );
294
295 Connect( wxEVT_GRID_COL_MOVE, wxGridEventHandler( WX_GRID::onGridColMove ), nullptr, this );
296 Connect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( WX_GRID::onGridCellSelect ), nullptr, this );
297
298 m_weOwnTable = aTakeOwnership;
299}
300
301
303{
304 wxGridTableBase* table = wxGrid::GetTable();
305
306 wxCHECK_MSG( table, /* void */, "Tried to enable alternate row colors without a table assigned to the grid" );
307
308 if( aEnable )
309 {
310 wxColor color = wxGrid::GetDefaultCellBackgroundColour();
311 table->SetAttrProvider( new WX_GRID_ALT_ROW_COLOR_PROVIDER( color ) );
312 }
313 else
314 {
315 table->SetAttrProvider( nullptr );
316 }
317}
318
319
320void WX_GRID::onGridCellSelect( wxGridEvent& aEvent )
321{
322 // Highlight the selected cell.
323 // Calling SelectBlock() allows a visual effect when cells are selected by tab or arrow keys.
324 // Otherwise, one cannot really know what actual cell is selected.
325 int row = aEvent.GetRow();
326 int col = aEvent.GetCol();
327
328 if( row >= 0 && row < GetNumberRows() && col >= 0 && col < GetNumberCols() )
329 {
330 if( GetSelectionMode() == wxGrid::wxGridSelectCells )
331 {
332 SelectBlock( row, col, row, col, false );
333 }
334 else if( GetSelectionMode() == wxGrid::wxGridSelectRows
335 || GetSelectionMode() == wxGrid::wxGridSelectRowsOrColumns )
336 {
337 SelectBlock( row, 0, row, GetNumberCols() - 1, false );
338 }
339 else if( GetSelectionMode() == wxGrid::wxGridSelectColumns )
340 {
341 SelectBlock( 0, col, GetNumberRows() - 1, col, false );
342 }
343 }
344}
345
346
347void WX_GRID::onCellEditorShown( wxGridEvent& aEvent )
348{
349 if( alg::contains( m_autoEvalCols, aEvent.GetCol() ) )
350 {
351 int row = aEvent.GetRow();
352 int col = aEvent.GetCol();
353
354 const std::pair<wxString, wxString>& beforeAfter = m_evalBeforeAfter[ { row, col } ];
355
356 if( GetCellValue( row, col ) == beforeAfter.second )
357 SetCellValue( row, col, beforeAfter.first );
358 }
359}
360
361
362void WX_GRID::onCellEditorHidden( wxGridEvent& aEvent )
363{
364 const int col = aEvent.GetCol();
365
366 if( alg::contains( m_autoEvalCols, col ) )
367 {
368 UNITS_PROVIDER* unitsProvider = getUnitsProvider( col );
369
370 auto cellUnitsData = getColumnUnits( col );
371 EDA_UNITS cellUnits = cellUnitsData.first;
372 EDA_DATA_TYPE cellDataType = cellUnitsData.second;
373
374 m_eval->SetDefaultUnits( cellUnits );
375
376 const int row = aEvent.GetRow();
377
378 // Determine if this cell is marked as holding nullable values
379 bool isNullable = false;
380 wxGridCellEditor* cellEditor = GetCellEditor( row, col );
381
382 if( cellEditor )
383 {
384 if( GRID_CELL_MARK_AS_NULLABLE* nullable = dynamic_cast<GRID_CELL_MARK_AS_NULLABLE*>( cellEditor ) )
385 isNullable = nullable->IsNullable();
386
387 cellEditor->DecRef();
388 }
389
390 CallAfter(
391 [this, row, col, isNullable, unitsProvider, cellDataType]()
392 {
393 // Careful; if called from CommitPendingChange() in a delete operation, the cell may
394 // no longer exist.
395 if( row >= GetNumberRows() || col >= GetNumberCols() )
396 return;
397
398 wxString stringValue = GetCellValue( row, col );
399 bool processedOk = true;
400
401 if( stringValue != UNITS_PROVIDER::NullUiString )
402 processedOk = m_eval->Process( stringValue );
403
404 if( processedOk )
405 {
406 wxString evalValue;
407
408 if( isNullable )
409 {
410 std::optional<int> val;
411
412 if( stringValue == UNITS_PROVIDER::NullUiString )
413 {
415 cellDataType );
416 }
417 else
418 {
419 val = unitsProvider->OptionalValueFromString( m_eval->Result(), cellDataType );
420 }
421
422 evalValue = unitsProvider->StringFromOptionalValue( val, true, cellDataType );
423 }
424 else
425 {
426 int val = unitsProvider->ValueFromString( m_eval->Result(), cellDataType );
427 evalValue = unitsProvider->StringFromValue( val, true, cellDataType );
428 }
429
430 if( stringValue != evalValue )
431 {
432 SetCellValue( row, col, evalValue );
433 m_evalBeforeAfter[{ row, col }] = { stringValue, evalValue };
434 }
435 }
436 } );
437 }
438
439 aEvent.Skip();
440}
441
442
443void WX_GRID::DestroyTable( wxGridTableBase* aTable )
444{
445 // wxGrid's destructor will crash trying to look up the cell attr if the edit control
446 // is left open. Normally it's closed in Validate(), but not if the user hit Cancel.
447 CommitPendingChanges( true /* quiet mode */ );
448
449 Disconnect( wxEVT_GRID_COL_MOVE, wxGridEventHandler( WX_GRID::onGridColMove ), nullptr, this );
450 Disconnect( wxEVT_GRID_SELECT_CELL, wxGridEventHandler( WX_GRID::onGridCellSelect ), nullptr, this );
451
452 wxGrid::SetTable( nullptr );
453 delete aTable;
454}
455
456
458{
459 wxString shownColumns;
460
461 for( int i = 0; i < GetNumberCols(); ++i )
462 {
463 if( IsColShown( i ) )
464 {
465 if( shownColumns.Length() )
466 shownColumns << wxT( " " );
467
468 shownColumns << i;
469 }
470 }
471
472 return shownColumns;
473}
474
475
477{
478 std::bitset<64> shownColumns;
479
480 for( int ii = 0; ii < GetNumberCols(); ++ii )
481 shownColumns[ii] = IsColShown( ii );
482
483 return shownColumns;
484}
485
486
487void WX_GRID::ShowHideColumns( const wxString& shownColumns )
488{
489 for( int i = 0; i < GetNumberCols(); ++i )
490 HideCol( i );
491
492 wxStringTokenizer shownTokens( shownColumns, " \t\r\n", wxTOKEN_STRTOK );
493
494 while( shownTokens.HasMoreTokens() )
495 {
496 long colNumber;
497 shownTokens.GetNextToken().ToLong( &colNumber );
498
499 if( colNumber >= 0 && colNumber < GetNumberCols() )
500 ShowCol( (int) colNumber );
501 }
502}
503
504
506{
507 if( m_nativeColumnLabels )
508 wxGrid::DrawCornerLabel( dc );
509
510 wxRect rect( wxSize( m_rowLabelWidth, m_colLabelHeight ) );
511
513
514 // It is reported that we need to erase the background to avoid display artifacts; see #12055.
515 {
516 wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
517 wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
518 dc.DrawRectangle( rect.Inflate( 1 ) );
519 }
520
521 rend.DrawBorder( *this, dc, rect );
522}
523
524
525void WX_GRID::DrawColLabel( wxDC& dc, int col )
526{
527 if( m_nativeColumnLabels )
528 wxGrid::DrawColLabel( dc, col );
529
530 if( GetColWidth( col ) <= 0 || m_colLabelHeight <= 0 )
531 return;
532
533 wxRect rect( GetColLeft( col ), 0, GetColWidth( col ), m_colLabelHeight );
534
536
537 // It is reported that we need to erase the background to avoid display artifacts; see #12055.
538 {
539 wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
540 wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
541 dc.DrawRectangle( rect.Inflate( 1 ) );
542 }
543
544 rend.DrawBorder( *this, dc, rect );
545
546 // Make sure fonts get scaled correctly on GTK HiDPI monitors
547 dc.SetFont( GetLabelFont() );
548
549 int hAlign, vAlign;
550 GetColLabelAlignment( &hAlign, &vAlign );
551 const int orient = GetColLabelTextOrientation();
552
553 if( col == 0 )
554 hAlign = wxALIGN_LEFT;
555
556 if( hAlign == wxALIGN_LEFT )
557 rect.SetLeft( rect.GetLeft() + MIN_GRIDCELL_MARGIN );
558
559 rend.DrawLabel( *this, dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient );
560}
561
562
563void WX_GRID::DrawRowLabel( wxDC& dc, int row )
564{
565 if( GetRowHeight( row ) <= 0 || m_rowLabelWidth <= 0 )
566 return;
567
568 wxRect rect( 0, GetRowTop( row ), m_rowLabelWidth, GetRowHeight( row ) );
569
570 static WX_GRID_ROW_HEADER_RENDERER rend;
571
572 // It is reported that we need to erase the background to avoid display artifacts; see #12055.
573 {
574 wxDCBrushChanger setBrush( dc, m_colLabelWin->GetBackgroundColour() );
575 wxDCPenChanger setPen( dc, m_colLabelWin->GetBackgroundColour() );
576 dc.DrawRectangle( rect.Inflate( 1 ) );
577 }
578
579 rend.DrawBorder( *this, dc, rect );
580
581 // Make sure fonts get scaled correctly on GTK HiDPI monitors
582 dc.SetFont( GetLabelFont() );
583
584 int hAlign, vAlign;
585 GetRowLabelAlignment(&hAlign, &vAlign);
586
587 if( hAlign == wxALIGN_LEFT )
588 rect.SetLeft( rect.GetLeft() + MIN_GRIDCELL_MARGIN );
589
590 rend.DrawLabel( *this, dc, GetRowLabelValue( row ), rect, hAlign, vAlign, wxHORIZONTAL );
591}
592
593
595{
596 if( !IsCellEditControlEnabled() )
597 return true;
598
599 HideCellEditControl();
600
601 // do it after HideCellEditControl()
602 m_cellEditCtrlEnabled = false;
603
604 int row = m_currentCellCoords.GetRow();
605 int col = m_currentCellCoords.GetCol();
606
607 wxString oldval = GetCellValue( row, col );
608 wxString newval;
609
610 wxGridCellAttr* attr = GetCellAttr( row, col );
611 wxGridCellEditor* editor = attr->GetEditor( this, row, col );
612
613 editor->EndEdit( row, col, this, oldval, &newval );
614
615 editor->DecRef();
616 attr->DecRef();
617
618 return true;
619}
620
621
622bool WX_GRID::CommitPendingChanges( bool aQuietMode )
623{
624 if( !IsCellEditControlEnabled() )
625 return true;
626
627 if( !aQuietMode && SendEvent( wxEVT_GRID_EDITOR_HIDDEN ) == -1 )
628 return false;
629
630 HideCellEditControl();
631
632 // do it after HideCellEditControl()
633 m_cellEditCtrlEnabled = false;
634
635 int row = m_currentCellCoords.GetRow();
636 int col = m_currentCellCoords.GetCol();
637
638 wxString oldval = GetCellValue( row, col );
639 wxString newval;
640
641 wxGridCellAttr* attr = GetCellAttr( row, col );
642 wxGridCellEditor* editor = attr->GetEditor( this, row, col );
643
644 bool changed = editor->EndEdit( row, col, this, oldval, &newval );
645
646 editor->DecRef();
647 attr->DecRef();
648
649 if( changed )
650 {
651 if( !aQuietMode && SendEvent( wxEVT_GRID_CELL_CHANGING, newval ) == -1 )
652 return false;
653
654 editor->ApplyEdit( row, col, this );
655
656 // for compatibility reasons dating back to wx 2.8 when this event
657 // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
658 // didn't exist we allow vetoing this one too
659 if( !aQuietMode && SendEvent( wxEVT_GRID_CELL_CHANGED, oldval ) == -1 )
660 {
661 // Event has been vetoed, set the data back.
662 SetCellValue( row, col, oldval );
663 return false;
664 }
665
666 if( DIALOG_SHIM* dlg = dynamic_cast<DIALOG_SHIM*>( wxGetTopLevelParent( this ) ) )
667 dlg->OnModify();
668 }
669
670 return true;
671}
672
673
674void WX_GRID::OnAddRow( const std::function<std::pair<int, int>()>& aAdder )
675{
676 if( !CommitPendingChanges() )
677 return;
678
679 auto [row, editCol] = aAdder();
680
681 // wx documentation is wrong, SetGridCursor does not make visible.
682 SetFocus();
683 MakeCellVisible( row, std::max( editCol, 0 ) );
684 SetGridCursor( row, std::max( editCol, 0 ) );
685
686 if( editCol >= 0 )
687 {
688 EnableCellEditControl( true );
689 ShowCellEditControl();
690 }
691}
692
693
694void WX_GRID::OnDeleteRows( const std::function<void( int row )>& aDeleter )
695{
697 []( int row )
698 {
699 return true;
700 },
701 aDeleter );
702}
703
704
705void WX_GRID::OnDeleteRows( const std::function<bool( int row )>& aFilter,
706 const std::function<void( int row )>& aDeleter )
707{
708 wxArrayInt selectedRows = GetSelectedRows();
709
710 if( selectedRows.empty() && GetGridCursorRow() >= 0 )
711 selectedRows.push_back( GetGridCursorRow() );
712
713 if( selectedRows.empty() )
714 return;
715
716 for( int row : selectedRows )
717 {
718 if( !aFilter( row ) )
719 return;
720 }
721
722 if( !CommitPendingChanges() )
723 return;
724
725 // Reverse sort so deleting a row doesn't change the indexes of the other rows.
726 selectedRows.Sort(
727 []( int* first, int* second )
728 {
729 return *second - *first;
730 } );
731
732 int nextSelRow = selectedRows.back() - 1;
733
734 if( nextSelRow >= 0 )
735 {
736 GoToCell( nextSelRow, GetGridCursorCol() );
737 SetGridCursor( nextSelRow, GetGridCursorCol() );
738 }
739
740 for( int row : selectedRows )
741 aDeleter( row );
742}
743
744
745void WX_GRID::SwapRows( int aRowA, int aRowB )
746{
747 for( int col = 0; col < GetNumberCols(); ++col )
748 {
749 wxString temp = GetCellValue( aRowA, col );
750 SetCellValue( aRowA, col, GetCellValue( aRowB, col ) );
751 SetCellValue( aRowB, col, temp );
752 }
753}
754
755
756void WX_GRID::OnMoveRowUp( const std::function<void( int row )>& aMover )
757{
759 []( int row )
760 {
761 return true;
762 },
763 aMover );
764}
765
766
767void WX_GRID::OnMoveRowUp( const std::function<bool( int row )>& aFilter,
768 const std::function<void( int row )>& aMover )
769{
770 if( !CommitPendingChanges() )
771 return;
772
773 int i = GetGridCursorRow();
774
775 if( i > 0 && aFilter( i ) )
776 {
777 aMover( i );
778
779 SetGridCursor( i - 1, GetGridCursorCol() );
780 MakeCellVisible( GetGridCursorRow(), GetGridCursorCol() );
781 }
782 else
783 {
784 wxBell();
785 }
786}
787
788
789void WX_GRID::OnMoveRowDown( const std::function<void( int row )>& aMover )
790{
792 []( int row )
793 {
794 return true;
795 },
796 aMover );
797}
798
799
800void WX_GRID::OnMoveRowDown( const std::function<bool( int row )>& aFilter,
801 const std::function<void( int row )>& aMover )
802{
803 if( !CommitPendingChanges() )
804 return;
805
806 int i = GetGridCursorRow();
807
808 if( i + 1 < GetNumberRows() && aFilter( i ) )
809 {
810 aMover( i );
811
812 SetGridCursor( i + 1, GetGridCursorCol() );
813 MakeCellVisible( GetGridCursorRow(), GetGridCursorCol() );
814 }
815 else
816 {
817 wxBell();
818 }
819}
820
821
822void WX_GRID::SetUnitsProvider( UNITS_PROVIDER* aProvider, int aCol )
823{
824 m_unitsProviders[ aCol ] = aProvider;
825
826 if( !m_eval )
827 m_eval = std::make_unique<NUMERIC_EVALUATOR>( aProvider->GetUserUnits() );
828}
829
830
831void WX_GRID::SetAutoEvalColUnits( const int col, EDA_UNITS aUnit, EDA_DATA_TYPE aUnitType )
832{
833 m_autoEvalColsUnits[col] = std::make_pair( aUnit, aUnitType );
834}
835
836
837void WX_GRID::SetAutoEvalColUnits( const int col, EDA_UNITS aUnit )
838{
840 SetAutoEvalColUnits( col, aUnit, type );
841}
842
843
844int WX_GRID::GetUnitValue( int aRow, int aCol )
845{
846 wxString stringValue = GetCellValue( aRow, aCol );
847
848 auto [cellUnits, cellDataType] = getColumnUnits( aCol );
849
850 if( alg::contains( m_autoEvalCols, aCol ) )
851 {
852 m_eval->SetDefaultUnits( cellUnits );
853
854 if( m_eval->Process( stringValue ) )
855 stringValue = m_eval->Result();
856 }
857
858 return getUnitsProvider( aCol )->ValueFromString( stringValue, cellDataType );
859}
860
861
862std::optional<int> WX_GRID::GetOptionalUnitValue( int aRow, int aCol )
863{
864 wxString stringValue = GetCellValue( aRow, aCol );
865
866 auto [cellUnits, cellDataType] = getColumnUnits( aCol );
867
868 if( alg::contains( m_autoEvalCols, aCol ) )
869 {
870 m_eval->SetDefaultUnits( cellUnits );
871
872 if( stringValue != UNITS_PROVIDER::NullUiString && m_eval->Process( stringValue ) )
873 stringValue = m_eval->Result();
874 }
875
876 return getUnitsProvider( aCol )->OptionalValueFromString( stringValue, cellDataType );
877}
878
879
880void WX_GRID::SetUnitValue( int aRow, int aCol, int aValue )
881{
882 EDA_DATA_TYPE cellDataType;
883
884 if( m_autoEvalColsUnits.contains( aCol ) )
885 cellDataType = m_autoEvalColsUnits[aCol].second;
886 else
887 cellDataType = EDA_DATA_TYPE::DISTANCE;
888
889 SetCellValue( aRow, aCol, getUnitsProvider( aCol )->StringFromValue( aValue, true, cellDataType ) );
890}
891
892
893void WX_GRID::SetOptionalUnitValue( int aRow, int aCol, std::optional<int> aValue )
894{
895 SetCellValue( aRow, aCol, getUnitsProvider( aCol )->StringFromOptionalValue( aValue, true ) );
896}
897
898
899void WX_GRID::onGridColMove( wxGridEvent& aEvent )
900{
901 // wxWidgets won't move an open editor, so better just to close it
902 CommitPendingChanges( true );
903}
904
905
906int WX_GRID::GetVisibleWidth( int aCol, bool aHeader, bool aContents, bool aKeep )
907{
908 int size = 0;
909
910 if( aCol < 0 )
911 {
912 if( aKeep )
913 size = GetRowLabelSize();
914
915 for( int row = 0; aContents && row < GetNumberRows(); row++ )
916 size = std::max( size, int( GetTextExtent( GetRowLabelValue( row ) + wxS( "M" ) ).x ) );
917 }
918 else
919 {
920 if( aKeep )
921 size = GetColSize( aCol );
922
923 // 'M' is generally the widest character, so we buffer the column width by default to
924 // ensure we don't write a continuous line of text at the column header
925 if( aHeader )
926 {
928
929 size = std::max( size, int( GetTextExtent( GetColLabelValue( aCol ) + wxS( "M" ) ).x ) );
930 }
931
932 for( int row = 0; aContents && row < GetNumberRows(); row++ )
933 {
934 // If we have text, get the size. Otherwise, use a placeholder for the checkbox
935 if( GetTable()->CanGetValueAs( row, aCol, wxGRID_VALUE_STRING ) )
936 size = std::max( size, GetTextExtent( GetCellValue( row, aCol ) + wxS( "M" ) ).x );
937 else
938 size = std::max( size, GetTextExtent( "MM" ).x );
939 }
940 }
941
942 return size;
943}
944
945
947{
948 int line_height = int( GetTextExtent( "Mj" ).y ) + 3;
949 int row_height = GetColLabelSize();
950 int initial_row_height = row_height;
951
952 // Headers can be multiline. Fix the Column Label Height to show the full header
953 // However GetTextExtent does not work on multiline strings,
954 // and do not return the full text height (only the height of one line)
955 for( int col = 0; col < GetNumberCols(); col++ )
956 {
957 int nl_count = GetColLabelValue( col ).Freq( '\n' );
958
959 if( nl_count )
960 {
961 // Col Label height must be able to show nl_count+1 lines
962 if( row_height < line_height * ( nl_count+1 ) )
963 row_height += line_height * nl_count;
964 }
965 }
966
967 // Update the column label size, but only if needed, to avoid generating useless
968 // and perhaps annoying UI events when the size does not change
969 if( initial_row_height != row_height )
970 SetColLabelSize( row_height );
971}
972
973
974std::pair<EDA_UNITS, EDA_DATA_TYPE> WX_GRID::getColumnUnits( const int aCol ) const
975{
976 if( m_autoEvalColsUnits.contains( aCol ) )
977 return { m_autoEvalColsUnits.at( aCol ).first, m_autoEvalColsUnits.at( aCol ).second };
978
979 // Legacy - default always DISTANCE
981}
int color
const char * name
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:104
wxColour ToColour() const
Definition color4d.cpp:220
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition color4d.h:295
static const wxString NullUiString
The string that is used in the UI to represent a null value.
std::optional< int > OptionalValueFromString(const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aTextValue in aUnits to internal units used by the frame.
wxString StringFromOptionalValue(std::optional< int > aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts an optional aValue in internal units into a united string.
EDA_UNITS GetUserUnits() const
static EDA_DATA_TYPE GetTypeFromUnits(const EDA_UNITS aUnits)
Gets the inferred type from the given units.
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aValue in internal units into a united string.
int ValueFromString(const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aTextValue in aUnits to internal units used by the frame.
Attribute provider that provides attributes (or modifies the existing attribute) to alternate a row c...
Definition wx_grid.cpp:157
WX_GRID_ALT_ROW_COLOR_PROVIDER(const wxColor &aBaseColor)
Definition wx_grid.cpp:159
wxGridCellAttr * GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) const override
Definition wx_grid.cpp:174
wxGridCellAttrPtr m_attrEven
Definition wx_grid.cpp:200
void UpdateColors(const wxColor &aBaseColor)
Definition wx_grid.cpp:166
void DrawBorder(const wxGrid &grid, wxDC &dc, wxRect &rect) const override
Definition wx_grid.cpp:121
void DrawBorder(const wxGrid &grid, wxDC &dc, wxRect &rect) const override
Definition wx_grid.cpp:104
void DrawBorder(const wxGrid &grid, wxDC &dc, wxRect &rect) const override
Definition wx_grid.cpp:138
wxGridCellAttr * enhanceAttr(wxGridCellAttr *aInputAttr, int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind)
Definition wx_grid.cpp:45
int GetVisibleWidth(int aCol, bool aHeader=true, bool aContents=true, bool aKeep=false)
Calculate the specified column based on the actual size of the text on screen.
Definition wx_grid.cpp:906
void onGridCellSelect(wxGridEvent &aEvent)
Definition wx_grid.cpp:320
~WX_GRID() override
Definition wx_grid.cpp:224
void SetLabelFont(const wxFont &aFont)
Hide wxGrid's SetLabelFont() because for some reason on MSW it's a one-shot and subsequent calls to i...
Definition wx_grid.cpp:261
bool m_weOwnTable
Definition wx_grid.h:321
void onDPIChanged(wxDPIChangedEvent &event)
Definition wx_grid.cpp:235
void ShowHideColumns(const wxString &shownColumns)
Show/hide the grid columns based on a tokenized string of shown column indexes.
Definition wx_grid.cpp:487
void OnMoveRowUp(const std::function< void(int row)> &aMover)
Definition wx_grid.cpp:756
std::map< int, UNITS_PROVIDER * > m_unitsProviders
Definition wx_grid.h:323
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:267
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:443
std::unordered_map< int, std::pair< EDA_UNITS, EDA_DATA_TYPE > > m_autoEvalColsUnits
Definition wx_grid.h:326
void SetAutoEvalColUnits(int col, EDA_UNITS aUnit, EDA_DATA_TYPE aUnitType)
Set the unit and unit data type to use for a given column.
Definition wx_grid.cpp:831
bool CancelPendingChanges()
Definition wx_grid.cpp:594
void SetColLabelSize(int aHeight)
Hide wxGrid's SetColLabelSize() method with one which makes sure the size is tall enough for the syst...
Definition wx_grid.cpp:246
void SwapRows(int aRowA, int aRowB)
These aren't that tricky, but might as well share code.
Definition wx_grid.cpp:745
std::pair< EDA_UNITS, EDA_DATA_TYPE > getColumnUnits(int aCol) const
Returns the units and data type associated with a given column.
Definition wx_grid.cpp:974
void SetUnitValue(int aRow, int aCol, int aValue)
Set a unitized cell's value.
Definition wx_grid.cpp:880
WX_GRID(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxWANTS_CHARS, const wxString &name=wxGridNameStr)
Definition wx_grid.cpp:204
int GetUnitValue(int aRow, int aCol)
Apply standard KiCad unit and eval services to a numeric cell.
Definition wx_grid.cpp:844
std::vector< int > m_autoEvalCols
Definition wx_grid.h:325
UNITS_PROVIDER * getUnitsProvider(int aCol) const
Definition wx_grid.h:307
std::map< std::pair< int, int >, std::pair< wxString, wxString > > m_evalBeforeAfter
Definition wx_grid.h:327
void DrawCornerLabel(wxDC &dc) override
A re-implementation of wxGrid::DrawCornerLabel which draws flat borders.
Definition wx_grid.cpp:505
void onCellEditorHidden(wxGridEvent &aEvent)
Definition wx_grid.cpp:362
void OnMoveRowDown(const std::function< void(int row)> &aMover)
Definition wx_grid.cpp:789
void onGridColMove(wxGridEvent &aEvent)
Definition wx_grid.cpp:899
void SetOptionalUnitValue(int aRow, int aCol, std::optional< int > aValue)
Set a unitized cell's optional value.
Definition wx_grid.cpp:893
void onCellEditorShown(wxGridEvent &aEvent)
Definition wx_grid.cpp:347
void EnsureColLabelsVisible()
Ensure the height of the row displaying the column labels is enough, even if labels are multiline tex...
Definition wx_grid.cpp:946
void OnDeleteRows(const std::function< void(int row)> &aDeleter)
Handles a row deletion event.
Definition wx_grid.cpp:694
wxString GetShownColumnsAsString()
Get a tokenized string containing the shown column indexes.
Definition wx_grid.cpp:457
void OnAddRow(const std::function< std::pair< int, int >()> &aAdder)
Definition wx_grid.cpp:674
void DrawRowLabel(wxDC &dc, int row) override
A re-implementation of wxGrid::DrawRowLabel which draws flat borders.
Definition wx_grid.cpp:563
std::bitset< 64 > GetShownColumns()
Definition wx_grid.cpp:476
static void CellEditorSetMargins(wxTextEntryBase *aEntry)
A helper function to set OS-specific margins for text-based cell editors.
Definition wx_grid.cpp:77
std::optional< int > GetOptionalUnitValue(int aRow, int aCol)
Apply standard KiCad unit and eval services to a numeric cell.
Definition wx_grid.cpp:862
void EnableAlternateRowColors(bool aEnable=true)
Enable alternate row highlighting, where every odd row has a different background color than the even...
Definition wx_grid.cpp:302
void SetUnitsProvider(UNITS_PROVIDER *aProvider, int aCol=0)
Set a EUNITS_PROVIDER to enable use of unit- and eval-based Getters.
Definition wx_grid.cpp:822
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition wx_grid.cpp:622
std::unique_ptr< NUMERIC_EVALUATOR > m_eval
Definition wx_grid.h:324
void DrawColLabel(wxDC &dc, int col) override
A re-implementation of wxGrid::DrawColLabel which left-aligns the first column and draws flat borders...
Definition wx_grid.cpp:525
static void CellEditorTransformSizeRect(wxRect &aRect)
A helper function to tweak sizes of text-based cell editors depending on OS.
Definition wx_grid.cpp:84
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:38
EDA_UNITS
Definition eda_units.h:48
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:48
KICOMMON_API wxFont GetControlFont(wxWindow *aWindow)
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:913
see class PGM_BASE
Functions to provide common constants and other functions to assist in making a consistent UI.
wxColour getBorderColour()
Definition wx_grid.cpp:92
#define MIN_GRIDCELL_MARGIN
Definition wx_grid.cpp:74