From: Robert Roebling Date: Mon, 16 Apr 2007 12:45:25 +0000 (+0000) Subject: Rewritten in-place editing for generic wxDataViewCtrl X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/99d471a5db7e75fadd2c855ee02e5b2c38529763 Rewritten in-place editing for generic wxDataViewCtrl moving the code (partly) to the renderer and away from the wxDataViewCtrl class. Adding new in-plae editors (wxChoice) should be much simpler now. Also implemented using Validate() before committing a change to the data model. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@45497 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/dataview.h b/include/wx/dataview.h index c495318526..52176d2319 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -280,7 +280,7 @@ public: // to combine alignment flags (e.g. wxALIGN_LEFT|wxALIGN_BOTTOM) virtual void SetAlignment( int align ) = 0; virtual int GetAlignment() const = 0; - + protected: wxString m_variantType; wxDataViewColumn *m_owner; diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index 9f2d6f009f..b62bc9eee6 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -25,6 +25,30 @@ class WXDLLIMPEXP_ADV wxDataViewCtrl; class WXDLLIMPEXP_ADV wxDataViewMainWindow; class WXDLLIMPEXP_ADV wxDataViewHeaderWindow; +//----------------------------------------------------------------------------- +// wxDataViewEditorCtrlEvtHandler +//----------------------------------------------------------------------------- + +class wxDataViewEditorCtrlEvtHandler: public wxEvtHandler +{ +public: + wxDataViewEditorCtrlEvtHandler( wxControl *editor, wxDataViewRenderer *owner ); + + void AcceptChangesAndFinish(); + +protected: + void OnChar( wxKeyEvent &event ); + void OnKillFocus( wxFocusEvent &event ); + +private: + wxDataViewRenderer *m_owner; + wxControl *m_editorCtrl; + bool m_finished; + +private: + DECLARE_EVENT_TABLE() +}; + // --------------------------------------------------------- // wxDataViewRenderer // --------------------------------------------------------- @@ -78,10 +102,24 @@ public: // Create DC on request virtual wxDC *GetDC(); + // in-place editing + virtual bool HasEditorCtrl() + { return false; } + virtual wxControl* CreateEditorCtrl( wxWindow *parent, wxRect labelRect, const wxVariant &value ) + { return NULL; } + virtual bool GetValueFromEditorCtrl( wxControl* editor, wxVariant &value ) + { return false; } + + virtual bool StartEditing( unsigned int row, wxRect labelRect ); + virtual void CancelEditing(); + virtual bool FinishEditing(); + private: - wxDC *m_dc; - int m_align; - wxDataViewCellMode m_mode; + wxDC *m_dc; + int m_align; + wxDataViewCellMode m_mode; + wxControl *m_editorCtrl; + unsigned int m_row; // for m_editorCtrl protected: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewRenderer) @@ -102,6 +140,7 @@ protected: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewCustomRenderer) }; + // --------------------------------------------------------- // wxDataViewTextRenderer // --------------------------------------------------------- @@ -119,8 +158,13 @@ public: bool Render( wxRect cell, wxDC *dc, int state ); wxSize GetSize() const; + // in-place editing + virtual bool HasEditorCtrl(); + virtual wxControl* CreateEditorCtrl( wxWindow *parent, wxRect labelRect, const wxVariant &value ); + virtual bool GetValueFromEditorCtrl( wxControl* editor, wxVariant &value ); + private: - wxString m_text; + wxString m_text; protected: DECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewTextRenderer) @@ -362,6 +406,8 @@ public: // utility functions not part of the API // updates the header window after a change in a column setting void OnColumnChange(); + wxDataViewMainWindow* GetMainWindow() { return m_clientArea; } + private: wxDataViewListModelNotifier *m_notifier; wxDataViewMainWindow *m_clientArea; diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index e8da85b5c4..579c2a9db6 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -33,6 +33,7 @@ #include "wx/timer.h" #include "wx/settings.h" #include "wx/msgdlg.h" + #include "wx/dcscreen.h" #endif #include "wx/stockitem.h" @@ -243,45 +244,6 @@ public: void Notify(); }; -//----------------------------------------------------------------------------- -// wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing -//----------------------------------------------------------------------------- - -class wxDataViewTextCtrlWrapper : public wxEvtHandler -{ -public: - // NB: text must be a valid object but not Create()d yet - wxDataViewTextCtrlWrapper( wxDataViewMainWindow *owner, - wxTextCtrl *text, - wxDataViewListModel *model, - unsigned int col, unsigned int row, - wxRect cellLabel ); - - wxTextCtrl *GetText() const { return m_text; } - - void AcceptChangesAndFinish(); - -protected: - void OnChar( wxKeyEvent &event ); - void OnKeyUp( wxKeyEvent &event ); - void OnKillFocus( wxFocusEvent &event ); - - bool AcceptChanges(); - void Finish(); - -private: - wxDataViewMainWindow *m_owner; - wxTextCtrl *m_text; - wxString m_startValue; - wxDataViewListModel *m_model; - unsigned int m_col; - unsigned int m_row; - bool m_finished; - bool m_aboutToFinish; - - DECLARE_EVENT_TABLE() -}; - //----------------------------------------------------------------------------- // wxDataViewMainWindow //----------------------------------------------------------------------------- @@ -325,7 +287,6 @@ public: void OnInternalIdle(); void OnRenameTimer(); - void FinishEditing( wxTextCtrl *text ); void ScrollWindow( int dx, int dy, const wxRect *rect = NULL ); @@ -367,11 +328,10 @@ private: bool m_dirty; wxDataViewColumn *m_currentCol; - unsigned int m_currentRow; + unsigned int m_currentRow; wxDataViewSelection m_selection; wxDataViewRenameTimer *m_renameTimer; - wxDataViewTextCtrlWrapper *m_textctrlWrapper; bool m_lastOnSame; bool m_hasFocus; @@ -458,6 +418,105 @@ wxDC *wxDataViewRenderer::GetDC() return m_dc; } +bool wxDataViewRenderer::StartEditing( unsigned int row, wxRect labelRect ) +{ + GetView()->CalcScrolledPosition( labelRect.x, labelRect.y, + &labelRect.x, &labelRect.y); + + m_row = row; // remember for later + + unsigned int col = GetOwner()->GetModelColumn(); + wxVariant value; + GetOwner()->GetOwner()->GetModel()->GetValue( value, col, row ); + + m_editorCtrl = CreateEditorCtrl( GetOwner()->GetOwner()->GetMainWindow(), labelRect, value ); + + m_editorCtrl->PushEventHandler( new wxDataViewEditorCtrlEvtHandler( m_editorCtrl, this ) ); + + m_editorCtrl->SetFocus(); + + return true; +} + +void wxDataViewRenderer::CancelEditing() +{ + // m_editorCtrl->PopEventHandler( true ); + + delete m_editorCtrl; + + GetOwner()->GetOwner()->GetMainWindow()->SetFocus(); +} + +bool wxDataViewRenderer::FinishEditing() +{ + // m_editorCtrl->PopEventHandler( true ); + + wxVariant value; + GetValueFromEditorCtrl( m_editorCtrl, value ); + + delete m_editorCtrl; + + GetOwner()->GetOwner()->GetMainWindow()->SetFocus(); + + if (!Validate(value)) + return false; + + unsigned int col = GetOwner()->GetModelColumn(); + GetOwner()->GetOwner()->GetModel()->SetValue( value, col, m_row ); + GetOwner()->GetOwner()->GetModel()->ValueChanged( col, m_row ); + + return true; +} + +//----------------------------------------------------------------------------- +// wxDataViewEditorCtrlEvtHandler +//----------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE(wxDataViewEditorCtrlEvtHandler, wxEvtHandler) + EVT_CHAR (wxDataViewEditorCtrlEvtHandler::OnChar) + EVT_KILL_FOCUS (wxDataViewEditorCtrlEvtHandler::OnKillFocus) +END_EVENT_TABLE() + +wxDataViewEditorCtrlEvtHandler::wxDataViewEditorCtrlEvtHandler( + wxControl *editorCtrl, + wxDataViewRenderer *owner ) +{ + m_owner = owner; + m_editorCtrl = editorCtrl; + + m_finished = false; +} + +void wxDataViewEditorCtrlEvtHandler::OnChar( wxKeyEvent &event ) +{ + switch ( event.m_keyCode ) + { + case WXK_RETURN: + m_finished = true; + m_owner->FinishEditing(); + break; + + case WXK_ESCAPE: + m_finished = true; + m_owner->CancelEditing(); + break; + + default: + event.Skip(); + } +} + +void wxDataViewEditorCtrlEvtHandler::OnKillFocus( wxFocusEvent &event ) +{ + if (!m_finished) + { + m_finished = true; + m_owner->FinishEditing(); + } + + // We must let the native text control handle focus + event.Skip(); +} // --------------------------------------------------------- // wxDataViewCustomRenderer @@ -495,6 +554,26 @@ bool wxDataViewTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const return false; } +bool wxDataViewTextRenderer::HasEditorCtrl() +{ + return true; +} + +wxControl* wxDataViewTextRenderer::CreateEditorCtrl( wxWindow *parent, + wxRect labelRect, const wxVariant &value ) +{ + return new wxTextCtrl( parent, wxID_ANY, value, + wxPoint(labelRect.x,labelRect.y), + wxSize(labelRect.width,labelRect.height) ); +} + +bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVariant &value ) +{ + wxTextCtrl *text = (wxTextCtrl*) editor; + value = text->GetValue(); + return true; +} + bool wxDataViewTextRenderer::Render( wxRect cell, wxDC *dc, int state ) { wxDataViewCtrl *view = GetOwner()->GetOwner(); @@ -1512,149 +1591,6 @@ void wxDataViewRenameTimer::Notify() m_owner->OnRenameTimer(); } -//----------------------------------------------------------------------------- -// wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing -//----------------------------------------------------------------------------- - -BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler) - EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar) - EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp) - EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus) -END_EVENT_TABLE() - -wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper( - wxDataViewMainWindow *owner, - wxTextCtrl *text, - wxDataViewListModel *model, - unsigned int col, unsigned int row, - wxRect rectLabel ) -{ - m_owner = owner; - m_model = model; - m_row = row; - m_col = col; - m_text = text; - - m_finished = false; - m_aboutToFinish = false; - - wxVariant value; - model->GetValue( value, col, row ); - m_startValue = value.GetString(); - - m_owner->GetOwner()->CalcScrolledPosition( - rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y ); - - m_text->Create( owner, wxID_ANY, m_startValue, - wxPoint(rectLabel.x-2,rectLabel.y-2), - wxSize(rectLabel.width+7,rectLabel.height+4) ); - m_text->SetFocus(); - - m_text->PushEventHandler(this); -} - -void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish() -{ - m_aboutToFinish = true; - - // Notify the owner about the changes - AcceptChanges(); - - // Even if vetoed, close the control (consistent with MSW) - Finish(); -} - -void wxDataViewTextCtrlWrapper::OnChar( wxKeyEvent &event ) -{ - switch ( event.m_keyCode ) - { - case WXK_RETURN: - AcceptChangesAndFinish(); - break; - - case WXK_ESCAPE: - // m_owner->OnRenameCancelled( m_itemEdited ); - Finish(); - break; - - default: - event.Skip(); - } -} - -void wxDataViewTextCtrlWrapper::OnKeyUp( wxKeyEvent &event ) -{ - if (m_finished) - { - event.Skip(); - return; - } - - // auto-grow the textctrl - wxSize parentSize = m_owner->GetSize(); - wxPoint myPos = m_text->GetPosition(); - wxSize mySize = m_text->GetSize(); - int sx, sy; - m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy); - if (myPos.x + sx > parentSize.x) - sx = parentSize.x - myPos.x; - if (mySize.x > sx) - sx = mySize.x; - m_text->SetSize(sx, wxDefaultCoord); - - event.Skip(); -} - -void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event ) -{ - if ( !m_finished && !m_aboutToFinish ) - { - AcceptChanges(); - //if ( !AcceptChanges() ) - // m_owner->OnRenameCancelled( m_itemEdited ); - - Finish(); - } - - // We must let the native text control handle focus - event.Skip(); -} - -bool wxDataViewTextCtrlWrapper::AcceptChanges() -{ - const wxString value = m_text->GetValue(); - - if ( value == m_startValue ) - // nothing changed, always accept - return true; - -// if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) - // vetoed by the user -// return false; - - // accepted, do rename the item - wxVariant variant; - variant = value; - m_model->SetValue( variant, m_col, m_row ); - m_model->ValueChanged( m_col, m_row ); - - return true; -} - -void wxDataViewTextCtrlWrapper::Finish() -{ - if ( !m_finished ) - { - m_finished = true; - - m_text->RemoveEventHandler(this); - m_owner->FinishEditing(m_text); - - // delete later - wxPendingDelete.Append( this ); - } -} - //----------------------------------------------------------------------------- // wxDataViewMainWindow //----------------------------------------------------------------------------- @@ -1687,7 +1623,6 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i m_lastOnSame = false; m_renameTimer = new wxDataViewRenameTimer( this ); - m_textctrlWrapper = NULL; // TODO: user better initial values/nothing selected m_currentCol = NULL; @@ -1747,44 +1682,37 @@ void wxDataViewMainWindow::OnRenameTimer() wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight ); - wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl); - - wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject(); - m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(), - m_currentCol->GetModelColumn(), m_currentRow, labelRect ); -} - -void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text ) -{ - delete text; - m_textctrlWrapper = NULL; - SetFocus(); - // SetFocusIgnoringChildren(); + m_currentCol->GetRenderer()->StartEditing( m_currentRow, labelRect ); } bool wxDataViewMainWindow::RowAppended() { - return false; + UpdateDisplay(); + return true; } bool wxDataViewMainWindow::RowPrepended() { - return false; + UpdateDisplay(); + return true; } bool wxDataViewMainWindow::RowInserted( unsigned int WXUNUSED(before) ) { - return false; + UpdateDisplay(); + return true; } bool wxDataViewMainWindow::RowDeleted( unsigned int WXUNUSED(row) ) { - return false; + UpdateDisplay(); + return true; } bool wxDataViewMainWindow::RowChanged( unsigned int WXUNUSED(row) ) { - return false; + UpdateDisplay(); + return true; } bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned int row ) @@ -1801,14 +1729,14 @@ bool wxDataViewMainWindow::ValueChanged( unsigned int WXUNUSED(col), unsigned in bool wxDataViewMainWindow::RowsReordered( unsigned int *WXUNUSED(new_order) ) { - Refresh(); - + UpdateDisplay(); return true; } bool wxDataViewMainWindow::Cleared() { - return false; + UpdateDisplay(); + return true; } void wxDataViewMainWindow::UpdateDisplay()