// 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
/////////////////////////////////////////////////////////////////////////////
#include "wx/textctrl.h"
#include "wx/checkbox.h"
#include "wx/combobox.h"
- #include "wx/valtext.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"
#define WXUNUSED_GTK(identifier) identifier
#endif
+#ifdef __WXOSX__
+#include "wx/osx/private.h"
+#endif
+
// Required for wxIs... functions
#include <ctype.h>
// implementation
// ============================================================================
+wxDEFINE_EVENT( wxEVT_GRID_HIDE_EDITOR, wxCommandEvent );
+
// ----------------------------------------------------------------------------
// wxGridCellEditorEvtHandler
// ----------------------------------------------------------------------------
void wxGridCellEditorEvtHandler::OnKillFocus(wxFocusEvent& event)
{
+ // We must let the native control have this event so in any case don't mark
+ // it as handled, otherwise various weird problems can happen (see #11681).
+ event.Skip();
+
// Don't disable the cell if we're just starting to edit it
- if ( m_inSetFocus )
- {
- event.Skip();
+ if (m_inSetFocus)
return;
- }
- // accept changes
- m_grid->DisableCellEditControl();
+ // Tell the grid to dismiss the control but don't do it immediately as it
+ // could result in the editor being destroyed right now and a crash in the
+ // code searching for the next event handler, so post an event asking the
+ // grid to do it slightly later instead.
- // 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
+ // FIXME-VC6: Once we drop support for VC6, we should use a simpler
+ // m_grid->CallAfter(&wxGrid::DisableCellEditControl) and get
+ // rid of wxEVT_GRID_HIDE_EDITOR entirely.
+ m_grid->GetEventHandler()->
+ AddPendingEvent(wxCommandEvent(wxEVT_GRID_HIDE_EDITOR));
}
void wxGridCellEditorEvtHandler::OnKeyDown(wxKeyEvent& event)
m_control->PushEventHandler(evtHandler);
}
-void wxGridCellEditor::PaintBackground(const wxRect& rectCell,
- wxGridCellAttr *attr)
+void wxGridCellEditor::PaintBackground(wxDC& dc,
+ const wxRect& rectCell,
+ const wxGridCellAttr& attr)
{
// erase the background because we might not fill the cell
- wxClientDC dc(m_control->GetParent());
- wxGridWindow* gridWindow = wxDynamicCast(m_control->GetParent(), wxGridWindow);
- if (gridWindow)
- gridWindow->GetOwner()->PrepareDC(dc);
-
dc.SetPen(*wxTRANSPARENT_PEN);
- dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
+ dc.SetBrush(wxBrush(attr.GetBackgroundColour()));
dc.DrawRectangle(rectCell);
-
- // redraw the control we just painted over
- m_control->Refresh();
}
void wxGridCellEditor::Destroy()
else
{
// restore the standard colours fonts
- if ( m_colFgOld.Ok() )
+ if ( m_colFgOld.IsOk() )
{
m_control->SetForegroundColour(m_colFgOld);
m_colFgOld = wxNullColour;
}
- if ( m_colBgOld.Ok() )
+ 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.Ok() )
+ if ( m_fontOld.IsOk() )
{
m_control->SetFont(m_fontOld);
m_fontOld = wxNullFont;
bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event)
{
bool ctrl = event.ControlDown();
- bool alt = event.AltDown();
+ 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();
-#endif
+#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
return false;
#if wxUSE_UNICODE
- // if the unicode key code is not really a unicode character (it may
- // be a function key or etc., the platforms appear to always give us a
- // small value in this case) then fallback to the ASCII key code but
- // don't do anything for function keys or etc.
- if ( event.GetUnicodeKey() > 127 && event.GetKeyCode() > 127 )
+ if ( static_cast<int>(event.GetUnicodeKey()) == WXK_NONE )
return false;
#else
- if ( event.GetKeyCode() > 255 )
+ if ( event.GetKeyCode() > WXK_START )
return false;
#endif
// wxGridCellTextEditor
// ----------------------------------------------------------------------------
-wxGridCellTextEditor::wxGridCellTextEditor()
+wxGridCellTextEditor::wxGridCellTextEditor(size_t maxChars)
{
- m_maxChars = 0;
+ m_maxChars = maxChars;
}
void wxGridCellTextEditor::Create(wxWindow* parent,
{
style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER;
- m_control = new wxTextCtrl(parent, id, wxEmptyString,
- wxDefaultPosition, wxDefaultSize,
- style);
+ wxTextCtrl* const text = new wxTextCtrl(parent, id, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ style);
+ text->SetMargins(0, 0);
+ m_control = text;
+#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);
}
+ // validate text in textctrl, if validator is set
+ if ( m_validator )
+ {
+ Text()->SetValidator(*m_validator);
+ }
wxGridCellEditor::Create(parent, id, evtHandler);
}
-void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell),
- wxGridCellAttr * WXUNUSED(attr))
+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
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;
{
Text()->SetValue(startValue);
Text()->SetInsertionPointEnd();
- Text()->SetSelection(-1, -1);
+ Text()->SelectAll();
Text()->SetFocus();
}
bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event)
{
- return wxGridCellEditor::IsAcceptedKey(event);
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_DELETE:
+ case WXK_BACK:
+ return true;
+
+ default:
+ return wxGridCellEditor::IsAcceptedKey(event);
+ }
}
void wxGridCellTextEditor::StartingKey(wxKeyEvent& event)
// a valid character, so not a whole lot of testing needs to be done.
wxTextCtrl* tc = Text();
- wxChar ch;
- long pos;
+ int ch;
+
+ bool isPrintable;
#if wxUSE_UNICODE
ch = event.GetUnicodeKey();
- if (ch <= 127)
- ch = (wxChar)event.GetKeyCode();
-#else
- ch = (wxChar)event.GetKeyCode();
-#endif
+ 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 character at the cursor
- pos = tc->GetInsertionPoint();
- if (pos < tc->GetLastPosition())
- tc->Remove(pos, pos + 1);
+ // Delete the initial character when starting to edit with DELETE.
+ tc->Remove(0, 1);
break;
case WXK_BACK:
- // delete the character before the cursor
- pos = tc->GetInsertionPoint();
- if (pos > 0)
+ // Delete the last character when starting to edit with BACKSPACE.
+ {
+ const long pos = tc->GetLastPosition();
tc->Remove(pos - 1, pos);
+ }
break;
default:
- tc->WriteText(ch);
+ if ( isPrintable )
+ tc->WriteText(static_cast<wxChar>(ch));
break;
}
}
}
}
+void wxGridCellTextEditor::SetValidator(const wxValidator& validator)
+{
+ m_validator.reset(static_cast<wxValidator*>(validator.Clone()));
+}
+
+wxGridCellEditor *wxGridCellTextEditor::Clone() const
+{
+ wxGridCellTextEditor* editor = new wxGridCellTextEditor(m_maxChars);
+ if ( m_validator )
+ {
+ editor->SetValidator(*m_validator);
+ }
+ return editor;
+}
+
// return the value in the text control
wxString wxGridCellTextEditor::GetValue() const
{
wxGridCellTextEditor::Create(parent, id, evtHandler);
#if wxUSE_VALIDATORS
- Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+ Text()->SetValidator(wxIntegerValidator<int>());
#endif
}
}
// wxGridCellFloatEditor
// ----------------------------------------------------------------------------
-wxGridCellFloatEditor::wxGridCellFloatEditor(int width, int precision)
+wxGridCellFloatEditor::wxGridCellFloatEditor(int width,
+ int precision,
+ int format)
{
m_width = width;
m_precision = precision;
+ m_style = format;
}
void wxGridCellFloatEditor::Create(wxWindow* parent,
wxGridCellTextEditor::Create(parent, id, evtHandler);
#if wxUSE_VALIDATORS
- Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
+ Text()->SetValidator(wxFloatingPointValidator<double>(m_precision));
#endif
}
// reset to default
m_width =
m_precision = -1;
+ m_style = wxGRID_FLOAT_FORMAT_DEFAULT;
+ m_format.clear();
}
else
{
- long tmp;
- if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
+ wxString rest;
+ wxString tmp = params.BeforeFirst(wxT(','), &rest);
+ if ( !tmp.empty() )
{
- m_width = (int)tmp;
-
- if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
+ long width;
+ if ( tmp.ToLong(&width) )
+ {
+ m_width = (int)width;
+ }
+ else
{
- m_precision = (int)tmp;
+ wxLogDebug(wxT("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str());
+ }
+ }
- // skip the error message below
- return;
+ 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());
}
}
- wxLogDebug(wxT("Invalid wxGridCellFloatEditor 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() const
+wxString wxGridCellFloatEditor::GetString()
{
- wxString fmt;
- if ( m_precision == -1 && m_width != -1)
- {
- // default precision
- fmt.Printf(wxT("%%%d.f"), m_width);
- }
- else if ( m_precision != -1 && m_width == -1)
- {
- // default width
- fmt.Printf(wxT("%%.%df"), m_precision);
- }
- else if ( m_precision != -1 && m_width != -1 )
- {
- fmt.Printf(wxT("%%%d.%df"), m_width, m_precision);
- }
- else
+ if ( !m_format )
{
- // default width/precision
- fmt = wxT("%f");
+ 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(fmt, m_value);
+ return wxString::Format(m_format, m_value);
}
bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event)
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
const int keycode = event.GetKeyCode();
- if ( isascii(keycode) )
+ if ( wxIsascii(keycode) )
{
- char tmpbuf[2];
- tmpbuf[0] = (char) keycode;
- tmpbuf[1] = '\0';
- wxString strbuf(tmpbuf, *wxConvCurrent);
-
#if wxUSE_INTL
const wxString decimalPoint =
wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
wxGridCellEditor::Create(parent, id, evtHandler);
}
-void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell,
- wxGridCellAttr * attr)
+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(rectCell, attr);
+ wxGridCellEditor::PaintBackground(dc, rectCell, attr);
}
void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
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
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) )
}
Combo()->SetSelection(m_index);
- Combo()->SetInsertionPointEnd();
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),