2020-08-24 01:46:01 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2023-02-20 10:11:57 +00:00
|
|
|
* Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
2020-08-24 01:46:01 +00:00
|
|
|
* @author Jon Evans <jon@craftyjon.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2021-07-29 09:56:22 +00:00
|
|
|
#include <string_utils.h>
|
2023-02-20 10:11:57 +00:00
|
|
|
#include <wx/stc/stc.h>
|
2020-08-24 01:46:01 +00:00
|
|
|
#include <widgets/grid_text_helpers.h>
|
2024-04-13 20:25:57 +00:00
|
|
|
#include <widgets/wx_grid.h>
|
2023-02-20 10:11:57 +00:00
|
|
|
#include <scintilla_tricks.h>
|
2020-08-24 01:46:01 +00:00
|
|
|
|
|
|
|
|
2024-04-13 20:25:57 +00:00
|
|
|
//-------- GRID_CELL_TEXT_EDITOR ------------------------------------------------------
|
|
|
|
//
|
|
|
|
|
|
|
|
GRID_CELL_TEXT_EDITOR::GRID_CELL_TEXT_EDITOR() : wxGridCellTextEditor()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_TEXT_EDITOR::SetValidator( const wxValidator& validator )
|
|
|
|
{
|
|
|
|
// keep our own copy because wxGridCellTextEditor's is annoyingly private
|
|
|
|
m_validator.reset( static_cast<wxValidator*>( validator.Clone() ) );
|
|
|
|
|
|
|
|
wxGridCellTextEditor::SetValidator( *m_validator );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_TEXT_EDITOR::StartingKey( wxKeyEvent& event )
|
|
|
|
{
|
|
|
|
if( m_validator )
|
|
|
|
{
|
|
|
|
m_validator.get()->SetWindow( Text() );
|
|
|
|
m_validator.get()->ProcessEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( event.GetSkipped() )
|
|
|
|
{
|
|
|
|
wxGridCellTextEditor::StartingKey( event );
|
|
|
|
event.Skip( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_TEXT_EDITOR::SetSize( const wxRect& aRect )
|
|
|
|
{
|
|
|
|
wxRect rect( aRect );
|
|
|
|
WX_GRID::CellEditorTransformSizeRect( rect );
|
|
|
|
|
|
|
|
#if defined( __WXMSW__ )
|
|
|
|
rect.Offset( 0, 1 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
wxGridCellEditor::SetSize( rect );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-20 10:11:57 +00:00
|
|
|
//-------- GRID_CELL_ESCAPED_TEXT_RENDERER ------------------------------------------------------
|
|
|
|
//
|
|
|
|
|
2020-08-24 01:46:01 +00:00
|
|
|
GRID_CELL_ESCAPED_TEXT_RENDERER::GRID_CELL_ESCAPED_TEXT_RENDERER() :
|
|
|
|
wxGridCellStringRenderer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void GRID_CELL_ESCAPED_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC,
|
|
|
|
const wxRect& aRect, int aRow, int aCol,
|
|
|
|
bool isSelected )
|
|
|
|
{
|
|
|
|
wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
|
|
|
|
|
|
|
|
wxRect rect = aRect;
|
|
|
|
rect.Inflate( -1 );
|
|
|
|
|
|
|
|
// erase background
|
|
|
|
wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
|
|
|
|
|
|
|
|
SetTextColoursAndFont( aGrid, aAttr, aDC, isSelected );
|
|
|
|
aGrid.DrawTextRectangle( aDC, unescaped, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxSize GRID_CELL_ESCAPED_TEXT_RENDERER::GetBestSize( wxGrid & aGrid, wxGridCellAttr & aAttr,
|
|
|
|
wxDC & aDC, int aRow, int aCol )
|
|
|
|
{
|
|
|
|
wxString unescaped = UnescapeString( aGrid.GetCellValue( aRow, aCol ) );
|
|
|
|
return wxGridCellStringRenderer::DoGetBestSize( aAttr, aDC, unescaped );
|
|
|
|
}
|
2023-02-20 10:11:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
//-------- GRID_CELL_STC_EDITOR -----------------------------------------------------------------
|
|
|
|
//
|
|
|
|
|
2024-03-10 10:46:26 +00:00
|
|
|
GRID_CELL_STC_EDITOR::GRID_CELL_STC_EDITOR(
|
|
|
|
bool aIgnoreCase,
|
|
|
|
std::function<void( wxStyledTextEvent&, SCINTILLA_TRICKS* )> onCharFn ) :
|
|
|
|
m_scintillaTricks( nullptr ),
|
|
|
|
m_ignoreCase( aIgnoreCase ),
|
|
|
|
m_onCharFn( std::move( onCharFn ) )
|
2023-02-20 10:11:57 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
2024-04-13 20:25:57 +00:00
|
|
|
void GRID_CELL_STC_EDITOR::SetSize( const wxRect& aRect )
|
|
|
|
{
|
|
|
|
wxRect rect( aRect );
|
|
|
|
WX_GRID::CellEditorTransformSizeRect( rect );
|
|
|
|
|
|
|
|
#if defined( __WXMSW__ )
|
|
|
|
rect.Offset( -1, 1 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
wxGridCellEditor::SetSize( rect );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-20 10:11:57 +00:00
|
|
|
void GRID_CELL_STC_EDITOR::Create( wxWindow* aParent, wxWindowID aId, wxEvtHandler* aEventHandler )
|
|
|
|
{
|
2024-04-13 20:25:57 +00:00
|
|
|
m_control = new wxStyledTextCtrl( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
|
|
wxBORDER_NONE );
|
2023-02-20 10:11:57 +00:00
|
|
|
|
|
|
|
stc_ctrl()->SetTabIndents( false );
|
|
|
|
stc_ctrl()->SetBackSpaceUnIndents( false );
|
|
|
|
stc_ctrl()->SetViewEOL( false );
|
|
|
|
stc_ctrl()->SetViewWhiteSpace( false );
|
|
|
|
stc_ctrl()->SetIndentationGuides( false );
|
|
|
|
stc_ctrl()->SetMarginWidth( 0, 0 ); // Symbol margin
|
|
|
|
stc_ctrl()->SetMarginWidth( 1, 0 ); // Line-number margin
|
|
|
|
stc_ctrl()->SetEOLMode( wxSTC_EOL_LF );
|
|
|
|
stc_ctrl()->AutoCompSetMaxWidth( 25 );
|
2023-02-21 09:06:02 +00:00
|
|
|
stc_ctrl()->AutoCompSetIgnoreCase( m_ignoreCase );
|
2023-02-20 10:11:57 +00:00
|
|
|
stc_ctrl()->UsePopUp( 0 );
|
|
|
|
|
|
|
|
// A hack which causes Scintilla to auto-size the text editor canvas
|
|
|
|
// See: https://github.com/jacobslusser/ScintillaNET/issues/216
|
|
|
|
stc_ctrl()->SetScrollWidth( 1 );
|
|
|
|
stc_ctrl()->SetScrollWidthTracking( true );
|
|
|
|
|
|
|
|
m_scintillaTricks = new SCINTILLA_TRICKS(
|
|
|
|
stc_ctrl(), wxEmptyString, true,
|
2024-03-10 10:46:26 +00:00
|
|
|
// onAcceptFn
|
2023-08-02 19:11:59 +00:00
|
|
|
[this]( wxKeyEvent& aEvent )
|
2023-02-20 10:11:57 +00:00
|
|
|
{
|
2023-08-02 19:11:59 +00:00
|
|
|
HandleReturn( aEvent );
|
2023-02-20 10:11:57 +00:00
|
|
|
},
|
2024-03-10 10:46:26 +00:00
|
|
|
// onCharFn
|
2023-02-20 10:11:57 +00:00
|
|
|
[this]( wxStyledTextEvent& aEvent )
|
|
|
|
{
|
2024-03-10 10:46:26 +00:00
|
|
|
m_onCharFn( aEvent, m_scintillaTricks );
|
2023-02-20 10:11:57 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
stc_ctrl()->Bind( wxEVT_KILL_FOCUS, &GRID_CELL_STC_EDITOR::onFocusLoss, this );
|
|
|
|
|
|
|
|
wxGridCellEditor::Create( aParent, aId, aEventHandler );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxStyledTextCtrl* GRID_CELL_STC_EDITOR::stc_ctrl() const
|
|
|
|
{
|
|
|
|
return static_cast<wxStyledTextCtrl*>( m_control );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString GRID_CELL_STC_EDITOR::GetValue() const
|
|
|
|
{
|
|
|
|
return stc_ctrl()->GetText();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-11 14:13:26 +00:00
|
|
|
void GRID_CELL_STC_EDITOR::StartingKey( wxKeyEvent& event )
|
|
|
|
{
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
bool isPrintable;
|
|
|
|
|
|
|
|
#if wxUSE_UNICODE
|
|
|
|
ch = event.GetUnicodeKey();
|
|
|
|
|
|
|
|
if( ch != WXK_NONE )
|
|
|
|
isPrintable = true;
|
|
|
|
else
|
|
|
|
#endif // wxUSE_UNICODE
|
|
|
|
{
|
|
|
|
ch = event.GetKeyCode();
|
|
|
|
isPrintable = ch >= WXK_SPACE && ch < WXK_START;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch( ch )
|
|
|
|
{
|
|
|
|
case WXK_DELETE:
|
|
|
|
// Delete the initial character when starting to edit with DELETE.
|
|
|
|
stc_ctrl()->DeleteRange( 0, 1 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WXK_BACK:
|
|
|
|
// Delete the last character when starting to edit with BACKSPACE.
|
|
|
|
stc_ctrl()->DeleteBack();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if( isPrintable )
|
|
|
|
stc_ctrl()->WriteText( static_cast<wxChar>( ch ) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-20 10:11:57 +00:00
|
|
|
void GRID_CELL_STC_EDITOR::Show( bool aShow, wxGridCellAttr* aAttr )
|
|
|
|
{
|
|
|
|
if( !aShow )
|
|
|
|
stc_ctrl()->AutoCompCancel();
|
|
|
|
|
|
|
|
wxGridCellEditor::Show( aShow, aAttr );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_STC_EDITOR::BeginEdit( int aRow, int aCol, wxGrid* aGrid )
|
|
|
|
{
|
|
|
|
auto evtHandler = static_cast<wxGridCellEditorEvtHandler*>( m_control->GetEventHandler()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Don't immediately end if we get a kill focus event within BeginEdit
|
|
|
|
evtHandler->SetInSetFocus( true );
|
|
|
|
|
|
|
|
m_value = aGrid->GetTable()->GetValue( aRow, aCol );
|
|
|
|
|
|
|
|
stc_ctrl()->SetFocus();
|
|
|
|
stc_ctrl()->SetText( m_value );
|
|
|
|
stc_ctrl()->SelectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool GRID_CELL_STC_EDITOR::EndEdit( int, int, const wxGrid*, const wxString&, wxString *aNewVal )
|
|
|
|
{
|
|
|
|
const wxString value = stc_ctrl()->GetText();
|
|
|
|
|
|
|
|
if( value == m_value )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_value = value;
|
|
|
|
|
|
|
|
if( aNewVal )
|
|
|
|
*aNewVal = value;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_STC_EDITOR::ApplyEdit( int aRow, int aCol, wxGrid* aGrid )
|
|
|
|
{
|
|
|
|
aGrid->GetTable()->SetValue( aRow, aCol, m_value );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GRID_CELL_STC_EDITOR::onFocusLoss( wxFocusEvent& aEvent )
|
|
|
|
{
|
|
|
|
if( stc_ctrl() )
|
|
|
|
stc_ctrl()->AutoCompCancel();
|
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|