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>
27
28
29//-------- GRID_CELL_TEXT_EDITOR ------------------------------------------------------
30//
31
33{}
34
35
37{
38 // keep our own copy because wxGridCellTextEditor's is annoyingly private
39 m_validator.reset( static_cast<wxValidator*>( validator.Clone() ) );
40
41 wxGridCellTextEditor::SetValidator( *m_validator );
42}
43
44
45void GRID_CELL_TEXT_EDITOR::StartingKey( wxKeyEvent& event )
46{
47 if( m_validator )
48 {
49 m_validator->SetWindow( Text() );
50 m_validator->ProcessEvent( event );
51 }
52
53 if( event.GetSkipped() )
54 {
55 wxGridCellTextEditor::StartingKey( event );
56 event.Skip( false );
57 }
58}
59
60
61void GRID_CELL_TEXT_EDITOR::SetSize( const wxRect& aRect )
62{
63 wxRect rect( aRect );
65
66#if defined( __WXMSW__ )
67 rect.Offset( 0, 1 );
68#elif defined( __WXMAC__ )
69 rect.Offset( 0, 2 );
70 rect.SetHeight( rect.GetHeight() - 4 );
71#endif
72
73 wxGridCellEditor::SetSize( rect ); // NOLINT(*-parent-virtual-call)
74}
75
76
77//-------- GRID_CELL_ESCAPED_TEXT_RENDERER ------------------------------------------------------
78//
79
83
84void GRID_CELL_ESCAPED_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, const wxRect& aRect,
85 int aRow, int aCol, bool isSelected )
86{
87 wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
88
89 wxRect rect = aRect;
90 rect.Inflate( -1 );
91
92 // erase background
93 wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
94
95 SetTextColoursAndFont( aGrid, aAttr, aDC, isSelected );
96 aGrid.DrawTextRectangle( aDC, unescaped, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
97}
98
99
100wxSize GRID_CELL_ESCAPED_TEXT_RENDERER::GetBestSize( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
101 int aRow, int aCol )
102{
103 wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
104 return wxGridCellStringRenderer::DoGetBestSize( aAttr, aDC, unescaped );
105}
106
107
108//-------- GRID_CELL_STC_EDITOR -----------------------------------------------------------------
109//
110
111GRID_CELL_STC_EDITOR::GRID_CELL_STC_EDITOR( bool aIgnoreCase, bool aSingleLine,
112 std::function<void( wxStyledTextEvent&, SCINTILLA_TRICKS* )> onCharFn ) :
113 m_scintillaTricks( nullptr ),
114 m_ignoreCase( aIgnoreCase ),
115 m_singleLine( aSingleLine ),
116 m_onCharFn( std::move( onCharFn ) )
117{}
118
119
120void GRID_CELL_STC_EDITOR::SetSize( const wxRect& aRect )
121{
122 wxRect rect( aRect );
124
125#if defined( __WXMSW__ )
126#if !wxCHECK_VERSION( 3, 3, 0 )
127 rect.Offset( -1, 0 );
128 // hack no longer needed with wx 3.3
129 rect.SetHeight( rect.GetHeight() + 6 );
130#else
131 rect.Offset( 0, 1 );
132#endif
133#elif defined( __WXGTK__ )
134 rect.Offset( -1, 3 );
135#else
136 rect.Offset( 1, 3 );
137 rect.SetWidth( rect.GetWidth() - 1 );
138 rect.SetHeight( rect.GetHeight() - 4 );
139#endif
140 wxGridCellEditor::SetSize( rect );
141}
142
143
144void GRID_CELL_STC_EDITOR::Create( wxWindow* aParent, wxWindowID aId, wxEvtHandler* aEventHandler )
145{
146 m_control = new wxStyledTextCtrl( aParent, wxID_ANY, wxDefaultPosition, wxSize( 0, 0 ),
147 wxBORDER_NONE );
148
149 stc_ctrl()->SetTabIndents( false );
150 stc_ctrl()->SetBackSpaceUnIndents( false );
151 stc_ctrl()->SetViewEOL( false );
152 stc_ctrl()->SetViewWhiteSpace( false );
153 stc_ctrl()->SetIndentationGuides( false );
154 stc_ctrl()->SetMarginWidth( 0, 0 ); // Symbol margin
155 stc_ctrl()->SetMarginWidth( 1, 0 ); // Line-number margin
156 stc_ctrl()->SetEOLMode( wxSTC_EOL_LF );
157
158 if( !m_singleLine )
159 {
160 // Wrapping is display-only and does not insert newlines into the stored text.
161 stc_ctrl()->SetWrapMode( wxSTC_WRAP_WORD );
162 stc_ctrl()->SetWrapVisualFlags( wxSTC_WRAPVISUALFLAG_END );
163 stc_ctrl()->SetWrapIndentMode( wxSTC_WRAPINDENT_INDENT );
164 }
165
166 stc_ctrl()->AutoCompSetMaxWidth( 25 );
167 stc_ctrl()->AutoCompSetIgnoreCase( m_ignoreCase );
168 stc_ctrl()->UsePopUp( 0 );
169
170 // A hack which causes Scintilla to auto-size the text editor canvas
171 // See: https://github.com/jacobslusser/ScintillaNET/issues/216
172 stc_ctrl()->SetScrollWidth( 1 );
173 stc_ctrl()->SetScrollWidthTracking( true );
174
176 stc_ctrl(), wxEmptyString, m_singleLine,
177
178 // onAcceptFn
179 [this]( wxKeyEvent& aEvent )
180 {
181 HandleReturn( aEvent );
182 },
183
184 // onCharFn
185 [this]( wxStyledTextEvent& aEvent )
186 {
187 m_onCharFn( aEvent, m_scintillaTricks );
188 } );
189
190 stc_ctrl()->Bind( wxEVT_KILL_FOCUS, &GRID_CELL_STC_EDITOR::onFocusLoss, this );
191
192 wxGridCellEditor::Create( aParent, aId, aEventHandler );
193}
194
195
196wxStyledTextCtrl* GRID_CELL_STC_EDITOR::stc_ctrl() const
197{
198 return static_cast<wxStyledTextCtrl*>( m_control );
199}
200
201
203{
204 return stc_ctrl()->GetText();
205}
206
207
208void GRID_CELL_STC_EDITOR::StartingKey( wxKeyEvent& event )
209{
210 int ch;
211
212 bool isPrintable;
213
214#if wxUSE_UNICODE
215 ch = event.GetUnicodeKey();
216
217 if( ch != WXK_NONE )
218 isPrintable = true;
219 else
220#endif // wxUSE_UNICODE
221 {
222 ch = event.GetKeyCode();
223 isPrintable = ch >= WXK_SPACE && ch < WXK_START;
224 }
225
226 switch( ch )
227 {
228 case WXK_DELETE:
229 // Delete the initial character when starting to edit with DELETE.
230 stc_ctrl()->DeleteRange( 0, 1 );
231 break;
232
233 case WXK_BACK:
234 // Delete the last character when starting to edit with BACKSPACE.
235 stc_ctrl()->DeleteBack();
236 break;
237
238 default:
239 if( isPrintable )
240 stc_ctrl()->WriteText( static_cast<wxChar>( ch ) );
241 break;
242 }
243}
244
245
246void GRID_CELL_STC_EDITOR::Show( bool aShow, wxGridCellAttr* aAttr )
247{
248 if( !aShow )
249 stc_ctrl()->AutoCompCancel();
250
251 wxGridCellEditor::Show( aShow, aAttr );
252}
253
254
255void GRID_CELL_STC_EDITOR::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
256{
257 auto evtHandler = static_cast<wxGridCellEditorEvtHandler*>( m_control->GetEventHandler() );
258
259 // Don't immediately end if we get a kill focus event within BeginEdit
260 evtHandler->SetInSetFocus( true );
261
262 m_value = aGrid->GetTable()->GetValue( aRow, aCol );
263
264 stc_ctrl()->SetFocus();
265 stc_ctrl()->SetText( m_value );
266 stc_ctrl()->SelectAll();
267}
268
269
270bool GRID_CELL_STC_EDITOR::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
271{
272 const wxString value = stc_ctrl()->GetText();
273
274 if( value == m_value )
275 return false;
276
277 m_value = value;
278
279 if( aNewVal )
280 *aNewVal = value;
281
282 return true;
283}
284
285
286void GRID_CELL_STC_EDITOR::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
287{
288 aGrid->GetTable()->SetValue( aRow, aCol, m_value );
289}
290
291
292void GRID_CELL_STC_EDITOR::onFocusLoss( wxFocusEvent& aEvent )
293{
294 if( stc_ctrl() )
295 stc_ctrl()->AutoCompCancel();
296
297 aEvent.Skip();
298}
299
300
301//-------- Editor Base Class for GRID_TEXT_BUTTON_HELPERS ------------
302//
303// Note: this implementation is an adaptation of wxGridCellChoiceEditor
304//
305// Note: this class is here instead of in grid_text_button_helpers.h/cpp to
306// keep from dragging a ton of stuff into kicommon.
307
308
310{
311 return Combo()->GetValue();
312}
313
314
315void GRID_CELL_TEXT_BUTTON::SetSize( const wxRect& aRect )
316{
317 wxRect rect( aRect );
319
320 wxGridCellEditor::SetSize( rect );
321}
322
323
324void GRID_CELL_TEXT_BUTTON::StartingKey( wxKeyEvent& event )
325{
326 // Note: this is a copy of wxGridCellTextEditor's StartingKey()
327
328 // Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
329 // longer an appropriate way to get the character into the text control.
330 // Do it ourselves instead. We know that if we get this far that we have
331 // a valid character, so not a whole lot of testing needs to be done.
332
333 // wxComboCtrl inherits from wxTextEntry, so can statically cast
334 wxTextEntry* textEntry = static_cast<wxTextEntry*>( Combo() );
335 int ch;
336
337 bool isPrintable;
338
339#if wxUSE_UNICODE
340 ch = event.GetUnicodeKey();
341
342 if( ch != WXK_NONE )
343 isPrintable = true;
344 else
345#endif // wxUSE_UNICODE
346 {
347 ch = event.GetKeyCode();
348 isPrintable = ch >= WXK_SPACE && ch < WXK_START;
349 }
350
351 switch( ch )
352 {
353 case WXK_DELETE:
354 // Delete the initial character when starting to edit with DELETE.
355 textEntry->Remove( 0, 1 );
356 break;
357
358 case WXK_BACK:
359 // Delete the last character when starting to edit with BACKSPACE.
360 {
361 const long pos = textEntry->GetLastPosition();
362 textEntry->Remove( pos - 1, pos );
363 }
364 break;
365
366 default:
367 if( isPrintable )
368 textEntry->WriteText( static_cast<wxChar>( ch ) );
369
370 break;
371 }
372}
373
374
375void GRID_CELL_TEXT_BUTTON::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
376{
377 m_grid = aGrid;
378 m_row = aRow;
379 m_col = aCol;
380
381 auto evtHandler = static_cast< wxGridCellEditorEvtHandler* >( m_control->GetEventHandler() );
382
383 // Don't immediately end if we get a kill focus event within BeginEdit
384 evtHandler->SetInSetFocus( true );
385
386 m_value = aGrid->GetTable()->GetValue( aRow, aCol );
387
388 Combo()->SetValue( m_value );
389 Combo()->SetFocus();
390
391 // Bind event to handle text changes
392 Combo()->Bind( wxEVT_TEXT, &GRID_CELL_TEXT_BUTTON::OnTextChange, this );
393}
394
395
396void GRID_CELL_TEXT_BUTTON::OnTextChange( wxCommandEvent& event )
397{
398 if( m_grid && m_row >= 0 && m_row < m_grid->GetNumberRows() && m_col >= 0 && m_col < m_grid->GetNumberCols() )
399 {
400 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_CHANGING, m_grid, m_row, m_col );
401 evt.SetString( Combo()->GetValue() );
402 m_grid->GetEventHandler()->ProcessEvent( evt );
403 }
404
405 event.Skip(); // Ensure that other handlers can process this event too
406}
407
408
409bool GRID_CELL_TEXT_BUTTON::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
410{
411 Combo()->Unbind( wxEVT_TEXT, &GRID_CELL_TEXT_BUTTON::OnTextChange, this );
412 m_grid = nullptr;
413 m_row = -1;
414 m_col = -1;
415
416 const wxString value = Combo()->GetValue();
417
418 if( value == m_value )
419 return false;
420
421 m_value = value;
422
423 if( aNewVal )
424 *aNewVal = value;
425
426 return true;
427}
428
429
430void GRID_CELL_TEXT_BUTTON::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
431{
432 aGrid->GetTable()->SetValue( aRow, aCol, m_value );
433}
434
435
437{
438 Combo()->SetValue( m_value );
439}
440
441
442#if wxUSE_VALIDATORS
443void GRID_CELL_TEXT_BUTTON::SetValidator( const wxValidator& validator )
444{
445 m_validator.reset( static_cast< wxValidator* >( validator.Clone() ) );
446}
447#endif
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
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
STL namespace.
wxString UnescapeString(const wxString &aSource)
JSON_SCHEMA_VALIDATOR validator(schema)