KiCad PCB EDA Suite
Loading...
Searching...
No Matches
grid_text_helpers.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 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <string_utils.h>
22#include <wx/stc/stc.h>
23#include <wx/dc.h>
25#include <widgets/wx_grid.h>
26#include <scintilla_tricks.h>
28#include <kiplatform/ui.h>
29
30
31//-------- GRID_CELL_TEXT_EDITOR ------------------------------------------------------
32//
33
35{}
36
37
38void GRID_CELL_TEXT_EDITOR::SetValidator( const wxValidator& validator )
39{
40 // keep our own copy because wxGridCellTextEditor's is annoyingly private
41 m_validator.reset( static_cast<wxValidator*>( validator.Clone() ) );
42
43 wxGridCellTextEditor::SetValidator( *m_validator );
44}
45
46
47void GRID_CELL_TEXT_EDITOR::StartingKey( wxKeyEvent& event )
48{
49 if( m_validator )
50 {
51 m_validator->SetWindow( Text() );
52 m_validator->ProcessEvent( event );
53 }
54
55 if( event.GetSkipped() )
56 {
57 wxGridCellTextEditor::StartingKey( event );
58 event.Skip( false );
59 }
60}
61
62
63void GRID_CELL_TEXT_EDITOR::SetSize( const wxRect& aRect )
64{
65 wxRect rect( aRect );
67
68#if defined( __WXMSW__ )
69 rect.Offset( 0, 1 );
70#elif defined( __WXMAC__ )
71 rect.Offset( 0, 2 );
72 rect.SetHeight( rect.GetHeight() - 4 );
73#endif
74
75 wxGridCellEditor::SetSize( rect ); // NOLINT(*-parent-virtual-call)
76}
77
78
79//-------- GRID_CELL_TEXT_RENDERER ------------------------------------------------------
80//
81
83 wxGridCellStringRenderer()
84{}
85
86
87void GRID_CELL_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, const wxRect& aRect,
88 int aRow, int aCol, bool isSelected )
89{
90 WX_GRID_TABLE_BASE* table = dynamic_cast<WX_GRID_TABLE_BASE*>( aGrid.GetTable() );
91
92 if( !table || !table->IsExpanderColumn( aCol ) )
93 return wxGridCellStringRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
94
95 wxString value = aGrid.GetCellValue( aRow, aCol );
96
97 wxRect rect = aRect;
98 rect.Inflate( -1 );
99
100 // erase background
101 wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
102
103 // draw the icon
104 int leftCut = aDC.FromDIP( 4 );
105
107
108 if( table->GetGroupType( aRow ) == GROUP_COLLAPSED )
110 else if( table->GetGroupType( aRow ) == GROUP_EXPANDED )
112
113 wxBitmap bitmap = static_cast<WX_GRID&>( aGrid ).GetRowIconProvider()->GetIndicatorIcon( state );
114 bitmap.SetScaleFactor( KIPLATFORM::UI::GetPixelScaleFactor( &aGrid ) );
115
116 aDC.DrawBitmap( bitmap,
117 rect.GetLeft() + leftCut,
118 rect.GetTop() + ( rect.GetHeight() - bitmap.GetLogicalHeight() ) / 2,
119 true );
120
121 leftCut += bitmap.GetLogicalWidth();
122
123 leftCut += aDC.FromDIP( 4 );
124
125 if( table->GetGroupType( aRow ) == CHILD_ITEM )
126 leftCut += aDC.FromDIP( 12 );
127
128 rect.x += leftCut;
129 rect.width -= leftCut;
130
131 // draw the text
132 SetTextColoursAndFont( aGrid, aAttr, aDC, isSelected );
133 aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
134}
135
136
137wxSize GRID_CELL_TEXT_RENDERER::GetBestSize( wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row, int col )
138{
139 WX_GRID_TABLE_BASE* table = dynamic_cast<WX_GRID_TABLE_BASE*>( grid.GetTable() );
140
141 if( !table || !table->IsExpanderColumn( col ) )
142 return wxGridCellStringRenderer::GetBestSize( grid, attr, dc, row, col );
143
145 wxBitmap bitmap = static_cast<WX_GRID&>( grid ).GetRowIconProvider()->GetIndicatorIcon( state );
146
147 bitmap.SetScaleFactor( KIPLATFORM::UI::GetPixelScaleFactor( &grid ) );
148
149 wxString text = grid.GetCellValue( row, col );
150 wxSize size = wxGridCellStringRenderer::DoGetBestSize( attr, dc, text );
151
152 size.x += bitmap.GetLogicalWidth() + dc.FromDIP( 8 );
153
154 if( table->GetGroupType( row ) == CHILD_ITEM )
155 size.x += dc.FromDIP( 12 );
156
157 size.y = std::max( size.y, dc.FromDIP( 2 ) );
158
159 return size;
160}
161
162
163//-------- GRID_CELL_ESCAPED_TEXT_RENDERER ------------------------------------------------------
164//
165
169
170void GRID_CELL_ESCAPED_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, const wxRect& aRect,
171 int aRow, int aCol, bool isSelected )
172{
173 wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
174
175 wxRect rect = aRect;
176 rect.Inflate( -1 );
177
178 // erase background
179 wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
180
181 SetTextColoursAndFont( aGrid, aAttr, aDC, isSelected );
182 aGrid.DrawTextRectangle( aDC, unescaped, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
183}
184
185
186wxSize GRID_CELL_ESCAPED_TEXT_RENDERER::GetBestSize( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
187 int aRow, int aCol )
188{
189 wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
190 return wxGridCellStringRenderer::DoGetBestSize( aAttr, aDC, unescaped );
191}
192
193
194//-------- GRID_CELL_STC_EDITOR -----------------------------------------------------------------
195//
196
197GRID_CELL_STC_EDITOR::GRID_CELL_STC_EDITOR( bool aIgnoreCase, bool aSingleLine,
198 std::function<void( wxStyledTextEvent&, SCINTILLA_TRICKS* )> onCharFn ) :
199 m_scintillaTricks( nullptr ),
200 m_ignoreCase( aIgnoreCase ),
201 m_singleLine( aSingleLine ),
202 m_onCharFn( std::move( onCharFn ) )
203{}
204
205
206void GRID_CELL_STC_EDITOR::SetSize( const wxRect& aRect )
207{
208 wxRect rect( aRect );
210
211#if defined( __WXMSW__ )
212#if !wxCHECK_VERSION( 3, 3, 0 )
213 rect.Offset( -1, 0 );
214 // hack no longer needed with wx 3.3
215 rect.SetHeight( rect.GetHeight() + 6 );
216#else
217 rect.Offset( 0, 1 );
218#endif
219#elif defined( __WXGTK__ )
220 rect.Offset( -1, 3 );
221#else
222 rect.Offset( 1, 3 );
223 rect.SetWidth( rect.GetWidth() - 1 );
224 rect.SetHeight( rect.GetHeight() - 4 );
225#endif
226 wxGridCellEditor::SetSize( rect );
227}
228
229
230void GRID_CELL_STC_EDITOR::Create( wxWindow* aParent, wxWindowID aId, wxEvtHandler* aEventHandler )
231{
232 m_control = new wxStyledTextCtrl( aParent, wxID_ANY, wxDefaultPosition, wxSize( 0, 0 ),
233 wxBORDER_NONE );
234
235 stc_ctrl()->SetTabIndents( false );
236 stc_ctrl()->SetBackSpaceUnIndents( false );
237 stc_ctrl()->SetViewEOL( false );
238 stc_ctrl()->SetViewWhiteSpace( false );
239 stc_ctrl()->SetIndentationGuides( false );
240 stc_ctrl()->SetMarginWidth( 0, 0 ); // Symbol margin
241 stc_ctrl()->SetMarginWidth( 1, 0 ); // Line-number margin
242 stc_ctrl()->SetEOLMode( wxSTC_EOL_LF );
243
244 if( !m_singleLine )
245 {
246 // Wrapping is display-only and does not insert newlines into the stored text.
247 stc_ctrl()->SetWrapMode( wxSTC_WRAP_WORD );
248 stc_ctrl()->SetWrapVisualFlags( wxSTC_WRAPVISUALFLAG_END );
249 stc_ctrl()->SetWrapIndentMode( wxSTC_WRAPINDENT_INDENT );
250 }
251
252 stc_ctrl()->AutoCompSetMaxWidth( 25 );
253 stc_ctrl()->AutoCompSetIgnoreCase( m_ignoreCase );
254 stc_ctrl()->UsePopUp( 0 );
255
256 // A hack which causes Scintilla to auto-size the text editor canvas
257 // See: https://github.com/jacobslusser/ScintillaNET/issues/216
258 stc_ctrl()->SetScrollWidth( 1 );
259 stc_ctrl()->SetScrollWidthTracking( true );
260
262 stc_ctrl(), wxEmptyString, m_singleLine,
263
264 // onAcceptFn
265 [this]( wxKeyEvent& aEvent )
266 {
267 HandleReturn( aEvent );
268 },
269
270 // onCharFn
271 [this]( wxStyledTextEvent& aEvent )
272 {
273 m_onCharFn( aEvent, m_scintillaTricks );
274 } );
275
276 stc_ctrl()->Bind( wxEVT_KILL_FOCUS, &GRID_CELL_STC_EDITOR::onFocusLoss, this );
277
278 wxGridCellEditor::Create( aParent, aId, aEventHandler );
279}
280
281
282wxStyledTextCtrl* GRID_CELL_STC_EDITOR::stc_ctrl() const
283{
284 return static_cast<wxStyledTextCtrl*>( m_control );
285}
286
287
289{
290 return stc_ctrl()->GetText();
291}
292
293
294void GRID_CELL_STC_EDITOR::StartingKey( wxKeyEvent& event )
295{
296 int ch;
297
298 bool isPrintable;
299
300#if wxUSE_UNICODE
301 ch = event.GetUnicodeKey();
302
303 if( ch != WXK_NONE )
304 isPrintable = true;
305 else
306#endif // wxUSE_UNICODE
307 {
308 ch = event.GetKeyCode();
309 isPrintable = ch >= WXK_SPACE && ch < WXK_START;
310 }
311
312 switch( ch )
313 {
314 case WXK_DELETE:
315 // Delete the initial character when starting to edit with DELETE.
316 stc_ctrl()->DeleteRange( 0, 1 );
317 break;
318
319 case WXK_BACK:
320 // Delete the last character when starting to edit with BACKSPACE.
321 stc_ctrl()->DeleteBack();
322 break;
323
324 default:
325 if( isPrintable )
326 stc_ctrl()->WriteText( static_cast<wxChar>( ch ) );
327 break;
328 }
329}
330
331
332void GRID_CELL_STC_EDITOR::Show( bool aShow, wxGridCellAttr* aAttr )
333{
334 if( !aShow )
335 stc_ctrl()->AutoCompCancel();
336
337 wxGridCellEditor::Show( aShow, aAttr );
338}
339
340
341void GRID_CELL_STC_EDITOR::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
342{
343 auto evtHandler = static_cast<wxGridCellEditorEvtHandler*>( m_control->GetEventHandler() );
344
345 // Don't immediately end if we get a kill focus event within BeginEdit
346 evtHandler->SetInSetFocus( true );
347
348 m_value = aGrid->GetTable()->GetValue( aRow, aCol );
349
350 stc_ctrl()->SetFocus();
351 stc_ctrl()->SetText( m_value );
352 stc_ctrl()->SelectAll();
353}
354
355
356bool GRID_CELL_STC_EDITOR::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
357{
358 const wxString value = stc_ctrl()->GetText();
359
360 if( value == m_value )
361 return false;
362
363 m_value = value;
364
365 if( aNewVal )
366 *aNewVal = value;
367
368 return true;
369}
370
371
372void GRID_CELL_STC_EDITOR::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
373{
374 aGrid->GetTable()->SetValue( aRow, aCol, m_value );
375}
376
377
378void GRID_CELL_STC_EDITOR::onFocusLoss( wxFocusEvent& aEvent )
379{
380 if( stc_ctrl() )
381 stc_ctrl()->AutoCompCancel();
382
383 aEvent.Skip();
384}
385
386
387//-------- Editor Base Class for GRID_TEXT_BUTTON_HELPERS ------------
388//
389// Note: this implementation is an adaptation of wxGridCellChoiceEditor
390//
391// Note: this class is here instead of in grid_text_button_helpers.h/cpp to
392// keep from dragging a ton of stuff into kicommon.
393
394
396{
397 return Combo()->GetValue();
398}
399
400
401void GRID_CELL_TEXT_BUTTON::SetSize( const wxRect& aRect )
402{
403 wxRect rect( aRect );
405
406 wxGridCellEditor::SetSize( rect );
407}
408
409
410void GRID_CELL_TEXT_BUTTON::StartingKey( wxKeyEvent& event )
411{
412 // Note: this is a copy of wxGridCellTextEditor's StartingKey()
413
414 // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
415 // longer an appropriate way to get the character into the text control.
416 // Do it ourselves instead. We know that if we get this far that we have
417 // a valid character, so not a whole lot of testing needs to be done.
418
419 // wxComboCtrl inherits from wxTextEntry, so can statically cast
420 wxTextEntry* textEntry = static_cast<wxTextEntry*>( Combo() );
421 int ch;
422
423 bool isPrintable;
424
425#if wxUSE_UNICODE
426 ch = event.GetUnicodeKey();
427
428 if( ch != WXK_NONE )
429 isPrintable = true;
430 else
431#endif // wxUSE_UNICODE
432 {
433 ch = event.GetKeyCode();
434 isPrintable = ch >= WXK_SPACE && ch < WXK_START;
435 }
436
437 switch( ch )
438 {
439 case WXK_DELETE:
440 // Delete the initial character when starting to edit with DELETE.
441 textEntry->Remove( 0, 1 );
442 break;
443
444 case WXK_BACK:
445 // Delete the last character when starting to edit with BACKSPACE.
446 {
447 const long pos = textEntry->GetLastPosition();
448 textEntry->Remove( pos - 1, pos );
449 }
450 break;
451
452 default:
453 if( isPrintable )
454 textEntry->WriteText( static_cast<wxChar>( ch ) );
455
456 break;
457 }
458}
459
460
461void GRID_CELL_TEXT_BUTTON::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
462{
463 m_grid = aGrid;
464 m_row = aRow;
465 m_col = aCol;
466
467 auto evtHandler = static_cast< wxGridCellEditorEvtHandler* >( m_control->GetEventHandler() );
468
469 // Don't immediately end if we get a kill focus event within BeginEdit
470 evtHandler->SetInSetFocus( true );
471
472 m_value = aGrid->GetTable()->GetValue( aRow, aCol );
473
474 Combo()->SetValue( m_value );
475 Combo()->SetFocus();
476
477 // Bind event to handle text changes
478 Combo()->Bind( wxEVT_TEXT, &GRID_CELL_TEXT_BUTTON::OnTextChange, this );
479}
480
481
482void GRID_CELL_TEXT_BUTTON::OnTextChange( wxCommandEvent& event )
483{
484 if( m_grid && m_row >= 0 && m_row < m_grid->GetNumberRows() && m_col >= 0 && m_col < m_grid->GetNumberCols() )
485 {
486 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_CHANGING, m_grid, m_row, m_col );
487 evt.SetString( Combo()->GetValue() );
488 m_grid->GetEventHandler()->ProcessEvent( evt );
489 }
490
491 event.Skip(); // Ensure that other handlers can process this event too
492}
493
494
495bool GRID_CELL_TEXT_BUTTON::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
496{
497 Combo()->Unbind( wxEVT_TEXT, &GRID_CELL_TEXT_BUTTON::OnTextChange, this );
498 m_grid = nullptr;
499 m_row = -1;
500 m_col = -1;
501
502 const wxString value = Combo()->GetValue();
503
504 if( value == m_value )
505 return false;
506
507 m_value = value;
508
509 if( aNewVal )
510 *aNewVal = value;
511
512 return true;
513}
514
515
516void GRID_CELL_TEXT_BUTTON::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
517{
518 aGrid->GetTable()->SetValue( aRow, aCol, m_value );
519}
520
521
523{
524 Combo()->SetValue( m_value );
525}
526
527
528#if wxUSE_VALIDATORS
529void GRID_CELL_TEXT_BUTTON::SetValidator( const wxValidator& validator )
530{
531 m_validator.reset( static_cast< wxValidator* >( validator.Clone() ) );
532}
533#endif
534
535
wxSize GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, int col) override
void Draw(wxGrid &aGrid, wxGridCellAttr &aAttr, wxDC &aDC, const wxRect &aRect, int aRow, int aCol, bool isSelected) override
void SetSize(const wxRect &aRect) override
void ApplyEdit(int aRow, int aCol, wxGrid *aGrid) override
void onFocusLoss(wxFocusEvent &aEvent)
void StartingKey(wxKeyEvent &event) override
std::function< void(wxStyledTextEvent &, SCINTILLA_TRICKS *)> m_onCharFn
GRID_CELL_STC_EDITOR(bool aIgnoreCase, bool aSingleLine, std::function< void(wxStyledTextEvent &, SCINTILLA_TRICKS *)> onCharFn)
bool EndEdit(int aRow, int aCol, const wxGrid *, const wxString &, wxString *aNewVal) override
void BeginEdit(int aRow, int aCol, wxGrid *aGrid) override
wxString GetValue() const override
void Create(wxWindow *aParent, wxWindowID aId, wxEvtHandler *aEventHandler) override
void Show(bool aShow, wxGridCellAttr *aAttr=nullptr) override
wxStyledTextCtrl * stc_ctrl() const
SCINTILLA_TRICKS * m_scintillaTricks
wxString GetValue() const override
wxComboCtrl * Combo() const
void StartingKey(wxKeyEvent &event) override
void BeginEdit(int aRow, int aCol, wxGrid *aGrid) override
bool EndEdit(int, int, const wxGrid *, const wxString &, wxString *aNewVal) override
void ApplyEdit(int aRow, int aCol, wxGrid *aGrid) override
void SetSize(const wxRect &aRect) override
void OnTextChange(wxCommandEvent &event)
std::unique_ptr< wxValidator > m_validator
void SetSize(const wxRect &aRect) override
virtual void StartingKey(wxKeyEvent &event) override
virtual void SetValidator(const wxValidator &validator) override
void Draw(wxGrid &aGrid, wxGridCellAttr &aAttr, wxDC &aDC, const wxRect &aRect, int aRow, int aCol, bool isSelected) override
wxSize GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int row, int col) override
int ICON_ID
An id that refers to a certain icon state.
@ OPEN
Tree control open.
@ CLOSED
Tree control closed.
@ OFF
Row "off" or "deselected".
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
static void CellEditorTransformSizeRect(wxRect &aRect)
A helper function to tweak sizes of text-based cell editors depending on OS.
Definition wx_grid.cpp:82
double GetPixelScaleFactor(const wxWindow *aWindow)
Tries to determine the pixel scaling factor currently in use for the window.
Definition wxgtk/ui.cpp:274
STL namespace.
wxString UnescapeString(const wxString &aSource)
std::vector< std::vector< std::string > > table
GROUP_COLLAPSED
Definition wx_grid.h:42
GROUP_EXPANDED
Definition wx_grid.h:44