Files
wxWidgets/src/generic/grideditors.cpp
Vadim Zeitlin ccc040255c Fix wxGrid editors background painting.
There were two fundamental problems: first, we painted on a separately created
wxClientDC instead of using the wxPaintDC already available in wxGrid. Second,
we invalidated the control while painting, resulting in endless repainting, at
least under wxGTK.

Fix the first problem by passing wxDC to wxGridCellEditor::PaintBackground()
and the second one by not refreshing the control from there as it just seems
unnecessary.

Also pass the attribute by reference for consistency with
wxGridCellRenderer::Draw() and because this pointer can never be NULL.

Closes #2628.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72697 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2012-10-17 16:44:02 +00:00

1686 lines
44 KiB
C++

///////////////////////////////////////////////////////////////////////////
// Name: src/generic/grideditors.cpp
// Purpose: wxGridCellEditorEvtHandler and wxGrid editors
// Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
// Modified by: Robin Dunn, Vadim Zeitlin, Santiago Palacios
// Created: 1/08/1999
// RCS-ID: $Id$
// Copyright: (c) Michael Bedward (mbedward@ozemail.com.au)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_GRID
#include "wx/grid.h"
#ifndef WX_PRECOMP
#include "wx/utils.h"
#include "wx/dcclient.h"
#include "wx/settings.h"
#include "wx/log.h"
#include "wx/textctrl.h"
#include "wx/checkbox.h"
#include "wx/combobox.h"
#include "wx/intl.h"
#include "wx/math.h"
#include "wx/listbox.h"
#endif
#include "wx/valnum.h"
#include "wx/textfile.h"
#include "wx/spinctrl.h"
#include "wx/tokenzr.h"
#include "wx/renderer.h"
#include "wx/headerctrl.h"
#include "wx/generic/gridsel.h"
#include "wx/generic/grideditors.h"
#include "wx/generic/private/grid.h"
#if defined(__WXMOTIF__)
#define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier)
#else
#define WXUNUSED_MOTIF(identifier) identifier
#endif
#if defined(__WXGTK__)
#define WXUNUSED_GTK(identifier) WXUNUSED(identifier)
#else
#define WXUNUSED_GTK(identifier) identifier
#endif
#ifdef __WXOSX__
#include "wx/osx/private.h"
#endif
// Required for wxIs... functions
#include <ctype.h>
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxGridCellEditorEvtHandler
// ----------------------------------------------------------------------------
void wxGridCellEditorEvtHandler::OnKillFocus(wxFocusEvent& event)
{
// Don't disable the cell if we're just starting to edit it
if ( m_inSetFocus )
{
event.Skip();
return;
}
// accept changes
m_grid->DisableCellEditControl();
// notice that we must not skip the event here because the call above may
// delete the control which received the kill focus event in the first
// place and if we pretend not having processed the event, the search for a
// handler for it will continue using the now deleted object resulting in a
// crash
}
void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event)
{
switch ( event.GetKeyCode() )
{
case WXK_ESCAPE:
m_editor->Reset();
m_grid->DisableCellEditControl();
break;
case WXK_TAB:
m_grid->GetEventHandler()->ProcessEvent( event );
break;
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
if (!m_grid->GetEventHandler()->ProcessEvent(event))
m_editor->HandleReturn(event);
break;
default:
event.Skip();
break;
}
}
void wxGridCellEditorEvtHandler::OnChar(wxKeyEvent& event)
{
int row = m_grid->GetGridCursorRow();
int col = m_grid->GetGridCursorCol();
wxRect rect = m_grid->CellToRect( row, col );
int cw, ch;
m_grid->GetGridWindow()->GetClientSize( &cw, &ch );
// if cell width is smaller than grid client area, cell is wholly visible
bool wholeCellVisible = (rect.GetWidth() < cw);
switch ( event.GetKeyCode() )
{
case WXK_ESCAPE:
case WXK_TAB:
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
break;
case WXK_HOME:
{
if ( wholeCellVisible )
{
// no special processing needed...
event.Skip();
break;
}
// do special processing for partly visible cell...
// get the widths of all cells previous to this one
int colXPos = 0;
for ( int i = 0; i < col; i++ )
{
colXPos += m_grid->GetColSize(i);
}
int xUnit = 1, yUnit = 1;
m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit);
if (col != 0)
{
m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL));
}
else
{
m_grid->Scroll(colXPos / xUnit, m_grid->GetScrollPos(wxVERTICAL));
}
event.Skip();
break;
}
case WXK_END:
{
if ( wholeCellVisible )
{
// no special processing needed...
event.Skip();
break;
}
// do special processing for partly visible cell...
int textWidth = 0;
wxString value = m_grid->GetCellValue(row, col);
if ( wxEmptyString != value )
{
// get width of cell CONTENTS (text)
int y;
wxFont font = m_grid->GetCellFont(row, col);
m_grid->GetTextExtent(value, &textWidth, &y, NULL, NULL, &font);
// try to RIGHT align the text by scrolling
int client_right = m_grid->GetGridWindow()->GetClientSize().GetWidth();
// (m_grid->GetScrollLineX()*2) is a factor for not scrolling to far,
// otherwise the last part of the cell content might be hidden below the scroll bar
// FIXME: maybe there is a more suitable correction?
textWidth -= (client_right - (m_grid->GetScrollLineX() * 2));
if ( textWidth < 0 )
{
textWidth = 0;
}
}
// get the widths of all cells previous to this one
int colXPos = 0;
for ( int i = 0; i < col; i++ )
{
colXPos += m_grid->GetColSize(i);
}
// and add the (modified) text width of the cell contents
// as we'd like to see the last part of the cell contents
colXPos += textWidth;
int xUnit = 1, yUnit = 1;
m_grid->GetScrollPixelsPerUnit(&xUnit, &yUnit);
m_grid->Scroll(colXPos / xUnit - 1, m_grid->GetScrollPos(wxVERTICAL));
event.Skip();
break;
}
default:
event.Skip();
break;
}
}
// ----------------------------------------------------------------------------
// wxGridCellEditor
// ----------------------------------------------------------------------------
wxGridCellEditor::wxGridCellEditor()
{
m_control = NULL;
m_attr = NULL;
}
wxGridCellEditor::~wxGridCellEditor()
{
Destroy();
}
void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent),
wxWindowID WXUNUSED(id),
wxEvtHandler* evtHandler)
{
if ( evtHandler )
m_control->PushEventHandler(evtHandler);
}
void wxGridCellEditor::PaintBackground(wxDC& dc,
const wxRect& rectCell,
const wxGridCellAttr& attr)
{
// erase the background because we might not fill the cell
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(attr.GetBackgroundColour()));
dc.DrawRectangle(rectCell);
}
void wxGridCellEditor::Destroy()
{
if (m_control)
{
m_control->PopEventHandler( true /* delete it*/ );
m_control->Destroy();
m_control = NULL;
}
}
void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr)
{
wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
m_control->Show(show);
if ( show )
{
// set the colours/fonts if we have any
if ( attr )
{
m_colFgOld = m_control->GetForegroundColour();
m_control->SetForegroundColour(attr->GetTextColour());
m_colBgOld = m_control->GetBackgroundColour();
m_control->SetBackgroundColour(attr->GetBackgroundColour());
// Workaround for GTK+1 font setting problem on some platforms
#if !defined(__WXGTK__) || defined(__WXGTK20__)
m_fontOld = m_control->GetFont();
m_control->SetFont(attr->GetFont());
#endif
// can't do anything more in the base class version, the other
// attributes may only be used by the derived classes
}
}
else
{
// restore the standard colours fonts
if ( m_colFgOld.IsOk() )
{
m_control->SetForegroundColour(m_colFgOld);
m_colFgOld = wxNullColour;
}
if ( m_colBgOld.IsOk() )
{
m_control->SetBackgroundColour(m_colBgOld);
m_colBgOld = wxNullColour;
}
// Workaround for GTK+1 font setting problem on some platforms
#if !defined(__WXGTK__) || defined(__WXGTK20__)
if ( m_fontOld.IsOk() )
{
m_control->SetFont(m_fontOld);
m_fontOld = wxNullFont;
}
#endif
}
}
void wxGridCellEditor::SetSize(const wxRect& rect)
{
wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
}
void wxGridCellEditor::HandleReturn(wxKeyEvent& event)
{
event.Skip();
}
bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event)
{
bool ctrl = event.ControlDown();
bool alt;
#ifdef __WXMAC__
// On the Mac the Alt key is more like shift and is used for entry of
// valid characters, so check for Ctrl and Meta instead.
alt = event.MetaDown();
#else // !__WXMAC__
alt = event.AltDown();
#endif // __WXMAC__/!__WXMAC__
// Assume it's not a valid char if ctrl or alt is down, but if both are
// down then it may be because of an AltGr key combination, so let them
// through in that case.
if ((ctrl || alt) && !(ctrl && alt))
return false;
#if wxUSE_UNICODE
if ( static_cast<int>(event.GetUnicodeKey()) == WXK_NONE )
return false;
#else
if ( event.GetKeyCode() > WXK_START )
return false;
#endif
return true;
}
void wxGridCellEditor::StartingKey(wxKeyEvent& event)
{
event.Skip();
}
void wxGridCellEditor::StartingClick()
{
}
#if wxUSE_TEXTCTRL
// ----------------------------------------------------------------------------
// wxGridCellTextEditor
// ----------------------------------------------------------------------------
wxGridCellTextEditor::wxGridCellTextEditor()
{
m_maxChars = 0;
}
void wxGridCellTextEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
DoCreate(parent, id, evtHandler);
}
void wxGridCellTextEditor::DoCreate(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler,
long style)
{
// Use of wxTE_RICH2 is a strange hack to work around the bug #11681: a
// plain text control seems to lose its caret somehow when we hide it and
// show it again for a different cell.
style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER | wxTE_RICH2;
m_control = new wxTextCtrl(parent, id, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
style);
#ifdef __WXOSX__
wxWidgetImpl* impl = m_control->GetPeer();
impl->SetNeedsFocusRect(false);
#endif
// set max length allowed in the textctrl, if the parameter was set
if ( m_maxChars != 0 )
{
Text()->SetMaxLength(m_maxChars);
}
wxGridCellEditor::Create(parent, id, evtHandler);
}
void wxGridCellTextEditor::PaintBackground(wxDC& WXUNUSED(dc),
const wxRect& WXUNUSED(rectCell),
const wxGridCellAttr& WXUNUSED(attr))
{
// as we fill the entire client area,
// don't do anything here to minimize flicker
}
void wxGridCellTextEditor::SetSize(const wxRect& rectOrig)
{
wxRect rect(rectOrig);
// Make the edit control large enough to allow for internal margins
//
// TODO: remove this if the text ctrl sizing is improved esp. for unix
//
#if defined(__WXGTK__)
if (rect.x != 0)
{
rect.x += 1;
rect.y += 1;
rect.width -= 1;
rect.height -= 1;
}
#elif defined(__WXMSW__)
if ( rect.x == 0 )
rect.x += 2;
else
rect.x += 3;
if ( rect.y == 0 )
rect.y += 2;
else
rect.y += 3;
rect.width -= 2;
rect.height -= 2;
#elif defined(__WXOSX__)
rect.x += 1;
rect.y += 1;
rect.width -= 1;
rect.height -= 1;
#else
int extra_x = ( rect.x > 2 ) ? 2 : 1;
int extra_y = ( rect.y > 2 ) ? 2 : 1;
#if defined(__WXMOTIF__)
extra_x *= 2;
extra_y *= 2;
#endif
rect.SetLeft( wxMax(0, rect.x - extra_x) );
rect.SetTop( wxMax(0, rect.y - extra_y) );
rect.SetRight( rect.GetRight() + 2 * extra_x );
rect.SetBottom( rect.GetBottom() + 2 * extra_y );
#endif
wxGridCellEditor::SetSize(rect);
}
void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid)
{
wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
m_value = grid->GetTable()->GetValue(row, col);
DoBeginEdit(m_value);
}
void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue)
{
Text()->SetValue(startValue);
Text()->SetInsertionPointEnd();
Text()->SelectAll();
Text()->SetFocus();
}
bool wxGridCellTextEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& WXUNUSED(oldval),
wxString *newval)
{
wxCHECK_MSG( m_control, false,
"wxGridCellTextEditor must be created first!" );
const wxString value = Text()->GetValue();
if ( value == m_value )
return false;
m_value = value;
if ( newval )
*newval = m_value;
return true;
}
void wxGridCellTextEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
grid->GetTable()->SetValue(row, col, m_value);
m_value.clear();
}
void wxGridCellTextEditor::Reset()
{
wxASSERT_MSG( m_control, "wxGridCellTextEditor must be created first!" );
DoReset(m_value);
}
void wxGridCellTextEditor::DoReset(const wxString& startValue)
{
Text()->SetValue(startValue);
Text()->SetInsertionPointEnd();
}
bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
{
switch ( event.GetKeyCode() )
{
case WXK_DELETE:
case WXK_BACK:
return true;
default:
return wxGridCellEditor::IsAcceptedKey(event);
}
}
void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
{
// Since this is now happening in the EVT_CHAR event EmulateKeyPress is no
// longer an appropriate way to get the character into the text control.
// Do it ourselves instead. We know that if we get this far that we have
// a valid character, so not a whole lot of testing needs to be done.
wxTextCtrl* tc = Text();
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.
tc->Remove(0, 1);
break;
case WXK_BACK:
// Delete the last character when starting to edit with BACKSPACE.
{
const long pos = tc->GetLastPosition();
tc->Remove(pos - 1, pos);
}
break;
default:
if ( isPrintable )
tc->WriteText(static_cast<wxChar>(ch));
break;
}
}
void wxGridCellTextEditor::HandleReturn( wxKeyEvent&
WXUNUSED_GTK(WXUNUSED_MOTIF(event)) )
{
#if defined(__WXMOTIF__) || defined(__WXGTK__)
// wxMotif needs a little extra help...
size_t pos = (size_t)( Text()->GetInsertionPoint() );
wxString s( Text()->GetValue() );
s = s.Left(pos) + wxT("\n") + s.Mid(pos);
Text()->SetValue(s);
Text()->SetInsertionPoint( pos );
#else
// the other ports can handle a Return key press
//
event.Skip();
#endif
}
void wxGridCellTextEditor::SetParameters(const wxString& params)
{
if ( !params )
{
// reset to default
m_maxChars = 0;
}
else
{
long tmp;
if ( params.ToLong(&tmp) )
{
m_maxChars = (size_t)tmp;
}
else
{
wxLogDebug( wxT("Invalid wxGridCellTextEditor parameter string '%s' ignored"), params.c_str() );
}
}
}
// return the value in the text control
wxString wxGridCellTextEditor::GetValue() const
{
return Text()->GetValue();
}
// ----------------------------------------------------------------------------
// wxGridCellNumberEditor
// ----------------------------------------------------------------------------
wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max)
{
m_min = min;
m_max = max;
}
void wxGridCellNumberEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
#if wxUSE_SPINCTRL
if ( HasRange() )
{
// create a spin ctrl
m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS,
m_min, m_max);
wxGridCellEditor::Create(parent, id, evtHandler);
}
else
#endif
{
// just a text control
wxGridCellTextEditor::Create(parent, id, evtHandler);
#if wxUSE_VALIDATORS
Text()->SetValidator(wxIntegerValidator<int>());
#endif
}
}
void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
{
// first get the value
wxGridTableBase *table = grid->GetTable();
if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
{
m_value = table->GetValueAsLong(row, col);
}
else
{
m_value = 0;
wxString sValue = table->GetValue(row, col);
if (! sValue.ToLong(&m_value) && ! sValue.empty())
{
wxFAIL_MSG( wxT("this cell doesn't have numeric value") );
return;
}
}
#if wxUSE_SPINCTRL
if ( HasRange() )
{
Spin()->SetValue((int)m_value);
Spin()->SetFocus();
}
else
#endif
{
DoBeginEdit(GetString());
}
}
bool wxGridCellNumberEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& oldval, wxString *newval)
{
long value = 0;
wxString text;
#if wxUSE_SPINCTRL
if ( HasRange() )
{
value = Spin()->GetValue();
if ( value == m_value )
return false;
text.Printf(wxT("%ld"), value);
}
else // using unconstrained input
#endif // wxUSE_SPINCTRL
{
text = Text()->GetValue();
if ( text.empty() )
{
if ( oldval.empty() )
return false;
}
else // non-empty text now (maybe 0)
{
if ( !text.ToLong(&value) )
return false;
// if value == m_value == 0 but old text was "" and new one is
// "0" something still did change
if ( value == m_value && (value || !oldval.empty()) )
return false;
}
}
m_value = value;
if ( newval )
*newval = text;
return true;
}
void wxGridCellNumberEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
table->SetValueAsLong(row, col, m_value);
else
table->SetValue(row, col, wxString::Format("%ld", m_value));
}
void wxGridCellNumberEditor::Reset()
{
#if wxUSE_SPINCTRL
if ( HasRange() )
{
Spin()->SetValue((int)m_value);
}
else
#endif
{
DoReset(GetString());
}
}
bool wxGridCellNumberEditor::IsAcceptedKey(wxKeyEvent& event)
{
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
int keycode = event.GetKeyCode();
if ( (keycode < 128) &&
(wxIsdigit(keycode) || keycode == '+' || keycode == '-'))
{
return true;
}
}
return false;
}
void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event)
{
int keycode = event.GetKeyCode();
if ( !HasRange() )
{
if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-')
{
wxGridCellTextEditor::StartingKey(event);
// skip Skip() below
return;
}
}
#if wxUSE_SPINCTRL
else
{
if ( wxIsdigit(keycode) )
{
wxSpinCtrl* spin = (wxSpinCtrl*)m_control;
spin->SetValue(keycode - '0');
spin->SetSelection(1,1);
return;
}
}
#endif
event.Skip();
}
void wxGridCellNumberEditor::SetParameters(const wxString& params)
{
if ( !params )
{
// reset to default
m_min =
m_max = -1;
}
else
{
long tmp;
if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
{
m_min = (int)tmp;
if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
{
m_max = (int)tmp;
// skip the error message below
return;
}
}
wxLogDebug(wxT("Invalid wxGridCellNumberEditor parameter string '%s' ignored"), params.c_str());
}
}
// return the value in the spin control if it is there (the text control otherwise)
wxString wxGridCellNumberEditor::GetValue() const
{
wxString s;
#if wxUSE_SPINCTRL
if ( HasRange() )
{
long value = Spin()->GetValue();
s.Printf(wxT("%ld"), value);
}
else
#endif
{
s = Text()->GetValue();
}
return s;
}
// ----------------------------------------------------------------------------
// wxGridCellFloatEditor
// ----------------------------------------------------------------------------
wxGridCellFloatEditor::wxGridCellFloatEditor(int width,
int precision,
int format)
{
m_width = width;
m_precision = precision;
m_style = format;
}
void wxGridCellFloatEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
wxGridCellTextEditor::Create(parent, id, evtHandler);
#if wxUSE_VALIDATORS
Text()->SetValidator(wxFloatingPointValidator<double>(m_precision));
#endif
}
void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid)
{
// first get the value
wxGridTableBase * const table = grid->GetTable();
if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) )
{
m_value = table->GetValueAsDouble(row, col);
}
else
{
m_value = 0.0;
const wxString value = table->GetValue(row, col);
if ( !value.empty() )
{
if ( !value.ToDouble(&m_value) )
{
wxFAIL_MSG( wxT("this cell doesn't have float value") );
return;
}
}
}
DoBeginEdit(GetString());
}
bool wxGridCellFloatEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& oldval, wxString *newval)
{
const wxString text(Text()->GetValue());
double value;
if ( !text.empty() )
{
if ( !text.ToDouble(&value) )
return false;
}
else // new value is empty string
{
if ( oldval.empty() )
return false; // nothing changed
value = 0.;
}
// the test for empty strings ensures that we don't skip the value setting
// when "" is replaced by "0" or vice versa as "" numeric value is also 0.
if ( wxIsSameDouble(value, m_value) && !text.empty() && !oldval.empty() )
return false; // nothing changed
m_value = value;
if ( newval )
*newval = text;
return true;
}
void wxGridCellFloatEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) )
table->SetValueAsDouble(row, col, m_value);
else
table->SetValue(row, col, Text()->GetValue());
}
void wxGridCellFloatEditor::Reset()
{
DoReset(GetString());
}
void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event)
{
int keycode = event.GetKeyCode();
char tmpbuf[2];
tmpbuf[0] = (char) keycode;
tmpbuf[1] = '\0';
wxString strbuf(tmpbuf, *wxConvCurrent);
#if wxUSE_INTL
bool is_decimal_point = ( strbuf ==
wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER) );
#else
bool is_decimal_point = ( strbuf == wxT(".") );
#endif
if ( wxIsdigit(keycode) || keycode == '+' || keycode == '-'
|| is_decimal_point )
{
wxGridCellTextEditor::StartingKey(event);
// skip Skip() below
return;
}
event.Skip();
}
void wxGridCellFloatEditor::SetParameters(const wxString& params)
{
if ( !params )
{
// reset to default
m_width =
m_precision = -1;
m_style = wxGRID_FLOAT_FORMAT_DEFAULT;
m_format.clear();
}
else
{
wxString rest;
wxString tmp = params.BeforeFirst(wxT(','), &rest);
if ( !tmp.empty() )
{
long width;
if ( tmp.ToLong(&width) )
{
m_width = (int)width;
}
else
{
wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
}
}
tmp = rest.BeforeFirst(wxT(','));
if ( !tmp.empty() )
{
long precision;
if ( tmp.ToLong(&precision) )
{
m_precision = (int)precision;
}
else
{
wxLogDebug(wxT("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str());
}
}
tmp = rest.AfterFirst(wxT(','));
if ( !tmp.empty() )
{
if ( tmp[0] == wxT('f') )
{
m_style = wxGRID_FLOAT_FORMAT_FIXED;
}
else if ( tmp[0] == wxT('e') )
{
m_style = wxGRID_FLOAT_FORMAT_SCIENTIFIC;
}
else if ( tmp[0] == wxT('g') )
{
m_style = wxGRID_FLOAT_FORMAT_COMPACT;
}
else if ( tmp[0] == wxT('E') )
{
m_style = wxGRID_FLOAT_FORMAT_SCIENTIFIC |
wxGRID_FLOAT_FORMAT_UPPER;
}
else if ( tmp[0] == wxT('F') )
{
m_style = wxGRID_FLOAT_FORMAT_FIXED |
wxGRID_FLOAT_FORMAT_UPPER;
}
else if ( tmp[0] == wxT('G') )
{
m_style = wxGRID_FLOAT_FORMAT_COMPACT |
wxGRID_FLOAT_FORMAT_UPPER;
}
else
{
wxLogDebug("Invalid wxGridCellFloatRenderer format "
"parameter string '%s ignored", params);
}
}
}
}
wxString wxGridCellFloatEditor::GetString()
{
if ( !m_format )
{
if ( m_precision == -1 && m_width != -1)
{
// default precision
m_format.Printf(wxT("%%%d."), m_width);
}
else if ( m_precision != -1 && m_width == -1)
{
// default width
m_format.Printf(wxT("%%.%d"), m_precision);
}
else if ( m_precision != -1 && m_width != -1 )
{
m_format.Printf(wxT("%%%d.%d"), m_width, m_precision);
}
else
{
// default width/precision
m_format = wxT("%");
}
bool isUpper = (m_style & wxGRID_FLOAT_FORMAT_UPPER) != 0;
if ( m_style & wxGRID_FLOAT_FORMAT_SCIENTIFIC )
m_format += isUpper ? wxT('E') : wxT('e');
else if ( m_style & wxGRID_FLOAT_FORMAT_COMPACT )
m_format += isUpper ? wxT('G') : wxT('g');
else
m_format += wxT('f');
}
return wxString::Format(m_format, m_value);
}
bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
{
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
const int keycode = event.GetKeyCode();
if ( wxIsascii(keycode) )
{
#if wxUSE_INTL
const wxString decimalPoint =
wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
#else
const wxString decimalPoint(wxT('.'));
#endif
// accept digits, 'e' as in '1e+6', also '-', '+', and '.'
if ( wxIsdigit(keycode) ||
tolower(keycode) == 'e' ||
keycode == decimalPoint ||
keycode == '+' ||
keycode == '-' )
{
return true;
}
}
}
return false;
}
#endif // wxUSE_TEXTCTRL
#if wxUSE_CHECKBOX
// ----------------------------------------------------------------------------
// wxGridCellBoolEditor
// ----------------------------------------------------------------------------
// the default values for GetValue()
wxString wxGridCellBoolEditor::ms_stringValues[2] = { wxT(""), wxT("1") };
void wxGridCellBoolEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
m_control = new wxCheckBox(parent, id, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxNO_BORDER);
wxGridCellEditor::Create(parent, id, evtHandler);
}
void wxGridCellBoolEditor::SetSize(const wxRect& r)
{
bool resize = false;
wxSize size = m_control->GetSize();
wxCoord minSize = wxMin(r.width, r.height);
// check if the checkbox is not too big/small for this cell
wxSize sizeBest = m_control->GetBestSize();
if ( !(size == sizeBest) )
{
// reset to default size if it had been made smaller
size = sizeBest;
resize = true;
}
if ( size.x >= minSize || size.y >= minSize )
{
// leave 1 pixel margin
size.x = size.y = minSize - 2;
resize = true;
}
if ( resize )
{
m_control->SetSize(size);
}
// position it in the centre of the rectangle (TODO: support alignment?)
#if defined(__WXGTK__) || defined (__WXMOTIF__)
// the checkbox without label still has some space to the right in wxGTK,
// so shift it to the right
size.x -= 8;
#elif defined(__WXMSW__)
// here too, but in other way
size.x += 1;
size.y -= 2;
#endif
int hAlign = wxALIGN_CENTRE;
int vAlign = wxALIGN_CENTRE;
if (GetCellAttr())
GetCellAttr()->GetAlignment(& hAlign, & vAlign);
int x = 0, y = 0;
if (hAlign == wxALIGN_LEFT)
{
x = r.x + 2;
#ifdef __WXMSW__
x += 2;
#endif
y = r.y + r.height / 2 - size.y / 2;
}
else if (hAlign == wxALIGN_RIGHT)
{
x = r.x + r.width - size.x - 2;
y = r.y + r.height / 2 - size.y / 2;
}
else if (hAlign == wxALIGN_CENTRE)
{
x = r.x + r.width / 2 - size.x / 2;
y = r.y + r.height / 2 - size.y / 2;
}
m_control->Move(x, y);
}
void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr)
{
m_control->Show(show);
if ( show )
{
wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY;
CBox()->SetBackgroundColour(colBg);
}
}
void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be created first!"));
if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL))
{
m_value = grid->GetTable()->GetValueAsBool(row, col);
}
else
{
wxString cellval( grid->GetTable()->GetValue(row, col) );
if ( cellval == ms_stringValues[false] )
m_value = false;
else if ( cellval == ms_stringValues[true] )
m_value = true;
else
{
// do not try to be smart here and convert it to true or false
// because we'll still overwrite it with something different and
// this risks to be very surprising for the user code, let them
// know about it
wxFAIL_MSG( wxT("invalid value for a cell with bool editor!") );
}
}
CBox()->SetValue(m_value);
CBox()->SetFocus();
}
bool wxGridCellBoolEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& WXUNUSED(oldval),
wxString *newval)
{
bool value = CBox()->GetValue();
if ( value == m_value )
return false;
m_value = value;
if ( newval )
*newval = GetValue();
return true;
}
void wxGridCellBoolEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_BOOL) )
table->SetValueAsBool(row, col, m_value);
else
table->SetValue(row, col, GetValue());
}
void wxGridCellBoolEditor::Reset()
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be created first!"));
CBox()->SetValue(m_value);
}
void wxGridCellBoolEditor::StartingClick()
{
CBox()->SetValue(!CBox()->GetValue());
}
bool wxGridCellBoolEditor::IsAcceptedKey(wxKeyEvent& event)
{
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
int keycode = event.GetKeyCode();
switch ( keycode )
{
case WXK_SPACE:
case '+':
case '-':
return true;
}
}
return false;
}
void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event)
{
int keycode = event.GetKeyCode();
switch ( keycode )
{
case WXK_SPACE:
CBox()->SetValue(!CBox()->GetValue());
break;
case '+':
CBox()->SetValue(true);
break;
case '-':
CBox()->SetValue(false);
break;
}
}
wxString wxGridCellBoolEditor::GetValue() const
{
return ms_stringValues[CBox()->GetValue()];
}
/* static */ void
wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue,
const wxString& valueFalse)
{
ms_stringValues[false] = valueFalse;
ms_stringValues[true] = valueTrue;
}
/* static */ bool
wxGridCellBoolEditor::IsTrueValue(const wxString& value)
{
return value == ms_stringValues[true];
}
#endif // wxUSE_CHECKBOX
#if wxUSE_COMBOBOX
// ----------------------------------------------------------------------------
// wxGridCellChoiceEditor
// ----------------------------------------------------------------------------
wxGridCellChoiceEditor::wxGridCellChoiceEditor(const wxArrayString& choices,
bool allowOthers)
: m_choices(choices),
m_allowOthers(allowOthers) { }
wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count,
const wxString choices[],
bool allowOthers)
: m_allowOthers(allowOthers)
{
if ( count )
{
m_choices.Alloc(count);
for ( size_t n = 0; n < count; n++ )
{
m_choices.Add(choices[n]);
}
}
}
wxGridCellEditor *wxGridCellChoiceEditor::Clone() const
{
wxGridCellChoiceEditor *editor = new wxGridCellChoiceEditor;
editor->m_allowOthers = m_allowOthers;
editor->m_choices = m_choices;
return editor;
}
void wxGridCellChoiceEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
int style = wxTE_PROCESS_ENTER |
wxTE_PROCESS_TAB |
wxBORDER_NONE;
if ( !m_allowOthers )
style |= wxCB_READONLY;
m_control = new wxComboBox(parent, id, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
m_choices,
style);
wxGridCellEditor::Create(parent, id, evtHandler);
}
void wxGridCellChoiceEditor::SetSize(const wxRect& rect)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellChoiceEditor must be created first!"));
// Check that the height is not too small to fit the combobox.
wxRect rectTallEnough = rect;
const wxSize bestSize = m_control->GetBestSize();
const wxCoord diffY = bestSize.GetHeight() - rectTallEnough.GetHeight();
if ( diffY > 0 )
{
// Do make it tall enough.
rectTallEnough.height += diffY;
// Also centre the effective rectangle vertically with respect to the
// original one.
rectTallEnough.y -= diffY/2;
}
//else: The rectangle provided is already tall enough.
wxGridCellEditor::SetSize(rectTallEnough);
}
void wxGridCellChoiceEditor::PaintBackground(wxDC& dc,
const wxRect& rectCell,
const wxGridCellAttr& attr)
{
// as we fill the entire client area, don't do anything here to minimize
// flicker
// TODO: It doesn't actually fill the client area since the height of a
// combo always defaults to the standard. Until someone has time to
// figure out the right rectangle to paint, just do it the normal way.
wxGridCellEditor::PaintBackground(dc, rectCell, attr);
}
void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEditor must be created first!"));
wxGridCellEditorEvtHandler* evtHandler = NULL;
if (m_control)
evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);
// Don't immediately end if we get a kill focus event within BeginEdit
if (evtHandler)
evtHandler->SetInSetFocus(true);
m_value = grid->GetTable()->GetValue(row, col);
Reset(); // this updates combo box to correspond to m_value
Combo()->SetFocus();
#ifdef __WXOSX_COCOA__
// This is a work around for the combobox being simply dismissed when a
// choice is made in it under OS X. The bug is almost certainly due to a
// problem in focus events generation logic but it's not obvious to fix and
// for now this at least allows to use wxGrid.
Combo()->Popup();
#endif
if (evtHandler)
{
// When dropping down the menu, a kill focus event
// happens after this point, so we can't reset the flag yet.
#if !defined(__WXGTK20__)
evtHandler->SetInSetFocus(false);
#endif
}
}
bool wxGridCellChoiceEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& WXUNUSED(oldval),
wxString *newval)
{
const wxString value = Combo()->GetValue();
if ( value == m_value )
return false;
m_value = value;
if ( newval )
*newval = value;
return true;
}
void wxGridCellChoiceEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
grid->GetTable()->SetValue(row, col, m_value);
}
void wxGridCellChoiceEditor::Reset()
{
if (m_allowOthers)
{
Combo()->SetValue(m_value);
Combo()->SetInsertionPointEnd();
}
else // the combobox is read-only
{
// find the right position, or default to the first if not found
int pos = Combo()->FindString(m_value);
if (pos == wxNOT_FOUND)
pos = 0;
Combo()->SetSelection(pos);
}
}
void wxGridCellChoiceEditor::SetParameters(const wxString& params)
{
if ( !params )
{
// what can we do?
return;
}
m_choices.Empty();
wxStringTokenizer tk(params, wxT(','));
while ( tk.HasMoreTokens() )
{
m_choices.Add(tk.GetNextToken());
}
}
// return the value in the text control
wxString wxGridCellChoiceEditor::GetValue() const
{
return Combo()->GetValue();
}
#endif // wxUSE_COMBOBOX
#if wxUSE_COMBOBOX
// ----------------------------------------------------------------------------
// wxGridCellEnumEditor
// ----------------------------------------------------------------------------
// A cell editor which displays an enum number as a textual equivalent. eg
// data in cell is 0,1,2 ... n the cell could be displayed as
// "John","Fred"..."Bob" in the combo choice box
wxGridCellEnumEditor::wxGridCellEnumEditor(const wxString& choices)
:wxGridCellChoiceEditor()
{
m_index = -1;
if (!choices.empty())
SetParameters(choices);
}
wxGridCellEditor *wxGridCellEnumEditor::Clone() const
{
wxGridCellEnumEditor *editor = new wxGridCellEnumEditor();
editor->m_index = m_index;
return editor;
}
void wxGridCellEnumEditor::BeginEdit(int row, int col, wxGrid* grid)
{
wxASSERT_MSG(m_control,
wxT("The wxGridCellEnumEditor must be Created first!"));
wxGridCellEditorEvtHandler* evtHandler = NULL;
if (m_control)
evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);
// Don't immediately end if we get a kill focus event within BeginEdit
if (evtHandler)
evtHandler->SetInSetFocus(true);
wxGridTableBase *table = grid->GetTable();
if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) )
{
m_index = table->GetValueAsLong(row, col);
}
else
{
wxString startValue = table->GetValue(row, col);
if (startValue.IsNumber() && !startValue.empty())
{
startValue.ToLong(&m_index);
}
else
{
m_index = -1;
}
}
Combo()->SetSelection(m_index);
Combo()->SetFocus();
#ifdef __WXOSX_COCOA__
// This is a work around for the combobox being simply dismissed when a
// choice is made in it under OS X. The bug is almost certainly due to a
// problem in focus events generation logic but it's not obvious to fix and
// for now this at least allows to use wxGrid.
Combo()->Popup();
#endif
if (evtHandler)
{
// When dropping down the menu, a kill focus event
// happens after this point, so we can't reset the flag yet.
#if !defined(__WXGTK20__)
evtHandler->SetInSetFocus(false);
#endif
}
}
bool wxGridCellEnumEditor::EndEdit(int WXUNUSED(row),
int WXUNUSED(col),
const wxGrid* WXUNUSED(grid),
const wxString& WXUNUSED(oldval),
wxString *newval)
{
long idx = Combo()->GetSelection();
if ( idx == m_index )
return false;
m_index = idx;
if ( newval )
newval->Printf("%ld", m_index);
return true;
}
void wxGridCellEnumEditor::ApplyEdit(int row, int col, wxGrid* grid)
{
wxGridTableBase * const table = grid->GetTable();
if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) )
table->SetValueAsLong(row, col, m_index);
else
table->SetValue(row, col, wxString::Format("%ld", m_index));
}
#endif // wxUSE_COMBOBOX
// ----------------------------------------------------------------------------
// wxGridCellAutoWrapStringEditor
// ----------------------------------------------------------------------------
void
wxGridCellAutoWrapStringEditor::Create(wxWindow* parent,
wxWindowID id,
wxEvtHandler* evtHandler)
{
wxGridCellTextEditor::DoCreate(parent, id, evtHandler,
wxTE_MULTILINE | wxTE_RICH);
}
#endif // wxUSE_GRID