X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7a0079d5a4d1fe6e80cc978d84648fa7bf6fde4a..bf2c43c76e2819be443ab1d830ab68d9569d66b1:/src/generic/grideditors.cpp diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 6ec2505ac8..aef0f36453 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -4,7 +4,6 @@ // 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 ///////////////////////////////////////////////////////////////////////////// @@ -28,12 +27,12 @@ #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" @@ -56,6 +55,10 @@ #define WXUNUSED_GTK(identifier) identifier #endif +#ifdef __WXOSX__ +#include "wx/osx/private.h" +#endif + // Required for wxIs... functions #include @@ -63,27 +66,32 @@ // 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) @@ -242,21 +250,14 @@ void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent), 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() @@ -300,13 +301,13 @@ void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) 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; @@ -314,7 +315,7 @@ void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) // 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; @@ -338,13 +339,15 @@ void wxGridCellEditor::HandleReturn(wxKeyEvent& event) 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 @@ -353,14 +356,10 @@ bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event) 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(event.GetUnicodeKey()) == WXK_NONE ) return false; #else - if ( event.GetKeyCode() > 255 ) + if ( event.GetKeyCode() > WXK_START ) return false; #endif @@ -382,9 +381,9 @@ void wxGridCellEditor::StartingClick() // wxGridCellTextEditor // ---------------------------------------------------------------------------- -wxGridCellTextEditor::wxGridCellTextEditor() +wxGridCellTextEditor::wxGridCellTextEditor(size_t maxChars) { - m_maxChars = 0; + m_maxChars = maxChars; } void wxGridCellTextEditor::Create(wxWindow* parent, @@ -401,21 +400,33 @@ void wxGridCellTextEditor::DoCreate(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 @@ -450,6 +461,12 @@ void wxGridCellTextEditor::SetSize(const wxRect& rectOrig) 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; @@ -481,7 +498,7 @@ void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue) { Text()->SetValue(startValue); Text()->SetInsertionPointEnd(); - Text()->SetSelection(-1, -1); + Text()->SelectAll(); Text()->SetFocus(); } @@ -527,7 +544,15 @@ void wxGridCellTextEditor::DoReset(const wxString& startValue) 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) @@ -538,35 +563,39 @@ 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(ch)); break; } } @@ -609,6 +638,21 @@ void wxGridCellTextEditor::SetParameters(const wxString& params) } } +void wxGridCellTextEditor::SetValidator(const wxValidator& validator) +{ + m_validator.reset(static_cast(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 { @@ -647,7 +691,7 @@ void wxGridCellNumberEditor::Create(wxWindow* parent, wxGridCellTextEditor::Create(parent, id, evtHandler); #if wxUSE_VALIDATORS - Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + Text()->SetValidator(wxIntegerValidator()); #endif } } @@ -849,10 +893,13 @@ wxString wxGridCellNumberEditor::GetValue() const // 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, @@ -862,7 +909,7 @@ void wxGridCellFloatEditor::Create(wxWindow* parent, wxGridCellTextEditor::Create(parent, id, evtHandler); #if wxUSE_VALIDATORS - Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + Text()->SetValidator(wxFloatingPointValidator(m_precision)); #endif } @@ -975,51 +1022,113 @@ void wxGridCellFloatEditor::SetParameters(const wxString& params) // 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) + if ( !m_format ) { - // 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 - { - // 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) @@ -1336,8 +1445,32 @@ void wxGridCellChoiceEditor::Create(wxWindow* parent, 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 @@ -1345,7 +1478,7 @@ void wxGridCellChoiceEditor::PaintBackground(const wxRect& rectCell, // 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) @@ -1367,6 +1500,14 @@ 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 @@ -1473,6 +1614,14 @@ 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) ) @@ -1493,9 +1642,24 @@ void wxGridCellEnumEditor::BeginEdit(int row, int col, wxGrid* grid) } 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),