X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/71cf399ff0f1ddb966b48e512b0fb4e690f5c440..0dd48109b4d2983fe600b3faf260b31cb82353c4:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 0fba2b3829..418b9c901a 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -9,6 +9,15 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +/* + TODO: + + - Replace use of wxINVERT with wxOverlay + - Make Begin/EndBatch() the same as the generic Freeze/Thaw() + - Review the column reordering code, it's a mess. + - Implement row reordering after dealing with the columns. + */ + // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" @@ -38,10 +47,14 @@ #include "wx/spinctrl.h" #include "wx/tokenzr.h" #include "wx/renderer.h" +#include "wx/headerctrl.h" #include "wx/generic/gridsel.h" +#include "wx/generic/gridctrl.h" +#include "wx/generic/grideditors.h" +#include "wx/generic/private/grid.h" -const wxChar wxGridNameStr[] = wxT("grid"); +const char wxGridNameStr[] = "grid"; #if defined(__WXMOTIF__) #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier) @@ -58,348 +71,6 @@ const wxChar wxGridNameStr[] = wxT("grid"); // Required for wxIs... functions #include -// ---------------------------------------------------------------------------- -// array classes -// ---------------------------------------------------------------------------- - -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridCellAttr *, wxArrayAttrs, - class WXDLLIMPEXP_ADV); - -struct wxGridCellWithAttr -{ - wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_) - : coords(row, col), attr(attr_) - { - } - - ~wxGridCellWithAttr() - { - attr->DecRef(); - } - - wxGridCellCoords coords; - wxGridCellAttr *attr; - -// Cannot do this: -// DECLARE_NO_COPY_CLASS(wxGridCellWithAttr) -// without rewriting the macros, which require a public copy constructor. -}; - -WX_DECLARE_OBJARRAY_WITH_DECL(wxGridCellWithAttr, wxGridCellWithAttrArray, - class WXDLLIMPEXP_ADV); - -#include "wx/arrimpl.cpp" - -WX_DEFINE_OBJARRAY(wxGridCellCoordsArray) -WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray) - -// ---------------------------------------------------------------------------- -// events -// ---------------------------------------------------------------------------- - -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_LEFT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_RIGHT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_BEGIN_DRAG) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_CLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_LEFT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_LABEL_RIGHT_DCLICK) -DEFINE_EVENT_TYPE(wxEVT_GRID_ROW_SIZE) -DEFINE_EVENT_TYPE(wxEVT_GRID_COL_SIZE) -DEFINE_EVENT_TYPE(wxEVT_GRID_COL_MOVE) -DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT) -DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE) -DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_SHOWN) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_HIDDEN) -DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_CREATED) - -// ---------------------------------------------------------------------------- -// private classes -// ---------------------------------------------------------------------------- - -// common base class for various grid subwindows -class WXDLLIMPEXP_ADV wxGridSubwindow : public wxWindow -{ -public: - wxGridSubwindow() { m_owner = NULL; } - wxGridSubwindow(wxGrid *owner, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - int additionalStyle = 0, - const wxString& name = wxPanelNameStr) - : wxWindow(owner, id, pos, size, - wxWANTS_CHARS | wxBORDER_NONE | additionalStyle, - name) - { - m_owner = owner; - } - - wxGrid *GetOwner() { return m_owner; } - -protected: - void OnMouseCaptureLost(wxMouseCaptureLostEvent& event); - - wxGrid *m_owner; - - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridSubwindow) -}; - -class WXDLLIMPEXP_ADV wxGridRowLabelWindow : public wxGridSubwindow -{ -public: - wxGridRowLabelWindow() { } - wxGridRowLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); - -private: - void OnPaint( wxPaintEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); - - DECLARE_DYNAMIC_CLASS(wxGridRowLabelWindow) - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridRowLabelWindow) -}; - - -class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxGridSubwindow -{ -public: - wxGridColLabelWindow() { } - wxGridColLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); - -private: - void OnPaint( wxPaintEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); - - DECLARE_DYNAMIC_CLASS(wxGridColLabelWindow) - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridColLabelWindow) -}; - - -class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxGridSubwindow -{ -public: - wxGridCornerLabelWindow() { } - wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); - -private: - void OnMouseEvent( wxMouseEvent& event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); - void OnPaint( wxPaintEvent& event ); - - DECLARE_DYNAMIC_CLASS(wxGridCornerLabelWindow) - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridCornerLabelWindow) -}; - -class WXDLLIMPEXP_ADV wxGridWindow : public wxGridSubwindow -{ -public: - wxGridWindow() - { - m_rowLabelWin = NULL; - m_colLabelWin = NULL; - } - - wxGridWindow( wxGrid *parent, - wxGridRowLabelWindow *rowLblWin, - wxGridColLabelWindow *colLblWin, - wxWindowID id, const wxPoint &pos, const wxSize &size ); - - void ScrollWindow( int dx, int dy, const wxRect *rect ); - -private: - wxGridRowLabelWindow *m_rowLabelWin; - wxGridColLabelWindow *m_colLabelWin; - - void OnPaint( wxPaintEvent &event ); - void OnMouseWheel( wxMouseEvent& event ); - void OnMouseEvent( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); - void OnEraseBackground( wxEraseEvent& ); - void OnFocus( wxFocusEvent& ); - - DECLARE_DYNAMIC_CLASS(wxGridWindow) - DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxGridWindow) -}; - - -class wxGridCellEditorEvtHandler : public wxEvtHandler -{ -public: - wxGridCellEditorEvtHandler(wxGrid* grid, wxGridCellEditor* editor) - : m_grid(grid), - m_editor(editor), - m_inSetFocus(false) - { - } - - void OnKillFocus(wxFocusEvent& event); - void OnKeyDown(wxKeyEvent& event); - void OnChar(wxKeyEvent& event); - - void SetInSetFocus(bool inSetFocus) { m_inSetFocus = inSetFocus; } - -private: - wxGrid *m_grid; - wxGridCellEditor *m_editor; - - // Work around the fact that a focus kill event can be sent to - // a combobox within a set focus event. - bool m_inSetFocus; - - DECLARE_EVENT_TABLE() - DECLARE_DYNAMIC_CLASS(wxGridCellEditorEvtHandler) - DECLARE_NO_COPY_CLASS(wxGridCellEditorEvtHandler) -}; - - -IMPLEMENT_ABSTRACT_CLASS(wxGridCellEditorEvtHandler, wxEvtHandler) - -BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler ) - EVT_KILL_FOCUS( wxGridCellEditorEvtHandler::OnKillFocus ) - EVT_KEY_DOWN( wxGridCellEditorEvtHandler::OnKeyDown ) - EVT_CHAR( wxGridCellEditorEvtHandler::OnChar ) -END_EVENT_TABLE() - - -// ---------------------------------------------------------------------------- -// the internal data representation used by wxGridCellAttrProvider -// ---------------------------------------------------------------------------- - -// this class stores attributes set for cells -class WXDLLIMPEXP_ADV wxGridCellAttrData -{ -public: - void SetAttr(wxGridCellAttr *attr, int row, int col); - wxGridCellAttr *GetAttr(int row, int col) const; - void UpdateAttrRows( size_t pos, int numRows ); - void UpdateAttrCols( size_t pos, int numCols ); - -private: - // searches for the attr for given cell, returns wxNOT_FOUND if not found - int FindIndex(int row, int col) const; - - wxGridCellWithAttrArray m_attrs; -}; - -// this class stores attributes set for rows or columns -class WXDLLIMPEXP_ADV wxGridRowOrColAttrData -{ -public: - // empty ctor to suppress warnings - wxGridRowOrColAttrData() {} - ~wxGridRowOrColAttrData(); - - void SetAttr(wxGridCellAttr *attr, int rowOrCol); - wxGridCellAttr *GetAttr(int rowOrCol) const; - void UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols ); - -private: - wxArrayInt m_rowsOrCols; - wxArrayAttrs m_attrs; -}; - -// NB: this is just a wrapper around 3 objects: one which stores cell -// attributes, and 2 others for row/col ones -class WXDLLIMPEXP_ADV wxGridCellAttrProviderData -{ -public: - wxGridCellAttrData m_cellAttrs; - wxGridRowOrColAttrData m_rowAttrs, - m_colAttrs; -}; - - -// ---------------------------------------------------------------------------- -// data structures used for the data type registry -// ---------------------------------------------------------------------------- - -struct wxGridDataTypeInfo -{ - wxGridDataTypeInfo(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor) - : m_typeName(typeName), m_renderer(renderer), m_editor(editor) - {} - - ~wxGridDataTypeInfo() - { - wxSafeDecRef(m_renderer); - wxSafeDecRef(m_editor); - } - - wxString m_typeName; - wxGridCellRenderer* m_renderer; - wxGridCellEditor* m_editor; - - DECLARE_NO_COPY_CLASS(wxGridDataTypeInfo) -}; - - -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray, - class WXDLLIMPEXP_ADV); - - -class WXDLLIMPEXP_ADV wxGridTypeRegistry -{ -public: - wxGridTypeRegistry() {} - ~wxGridTypeRegistry(); - - void RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor); - - // find one of already registered data types - int FindRegisteredDataType(const wxString& typeName); - - // try to FindRegisteredDataType(), if this fails and typeName is one of - // standard typenames, register it and return its index - int FindDataType(const wxString& typeName); - - // try to FindDataType(), if it fails see if it is not one of already - // registered data types with some params in which case clone the - // registered data type and set params for it - int FindOrCloneDataType(const wxString& typeName); - - wxGridCellRenderer* GetRenderer(int index); - wxGridCellEditor* GetEditor(int index); - -private: - wxGridDataTypeInfoArray m_typeinfo; -}; - - -// ---------------------------------------------------------------------------- -// conditional compilation -// ---------------------------------------------------------------------------- - -#ifndef WXGRID_DRAW_LINES -#define WXGRID_DRAW_LINES 1 -#endif // ---------------------------------------------------------------------------- // globals @@ -412,1875 +83,108 @@ private: #endif // ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- - -wxGridCellCoords wxGridNoCellCoords( -1, -1 ); -wxRect wxGridNoCellRect( -1, -1, -1, -1 ); - -// scroll line size -// TODO: this doesn't work at all, grid cells have different sizes and approx -// calculations don't work as because of the size mismatch scrollbars -// sometimes fail to be shown when they should be or vice versa -// -// The scroll bars may be a little flakey once in a while, but that is -// surely much less horrible than having scroll lines of only 1!!! -// -- Robin -// -// Well, it's still seriously broken so it might be better but needs -// fixing anyhow -// -- Vadim -static const size_t GRID_SCROLL_LINE_X = 15; // 1; -static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; - -// the size of hash tables used a bit everywhere (the max number of elements -// in these hash tables is the number of rows/columns) -static const int GRID_HASH_SIZE = 100; - -#if 0 -// ---------------------------------------------------------------------------- -// private functions -// ---------------------------------------------------------------------------- - -static inline int GetScrollX(int x) -{ - return (x + GRID_SCROLL_LINE_X - 1) / GRID_SCROLL_LINE_X; -} - -static inline int GetScrollY(int y) -{ - return (y + GRID_SCROLL_LINE_Y - 1) / GRID_SCROLL_LINE_Y; -} -#endif - -// ============================================================================ -// implementation -// ============================================================================ - -// ---------------------------------------------------------------------------- -// 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(const wxRect& rectCell, - 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(), wxSOLID)); - dc.DrawRectangle(rectCell); - - // redraw the control we just painted over - m_control->Refresh(); -} - -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.Ok() ) - { - m_control->SetForegroundColour(m_colFgOld); - m_colFgOld = wxNullColour; - } - - if ( m_colBgOld.Ok() ) - { - 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() ) - { - 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 = event.AltDown(); - -#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 - - // 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; - - int key = 0; - bool keyOk = true; - -#ifdef __WXGTK20__ - // If it's a F-Key or other special key then it shouldn't start the - // editor. - if (event.GetKeyCode() >= WXK_START) - return false; -#endif -#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. - key = event.GetUnicodeKey(); - if (key <= 127) - { - key = event.GetKeyCode(); - keyOk = (key <= 127); - } -#else - key = event.GetKeyCode(); - keyOk = (key <= 255); -#endif - - return keyOk; -} - -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) -{ - style |= wxTE_PROCESS_ENTER | - wxTE_PROCESS_TAB | - wxTE_AUTO_SCROLL | - wxNO_BORDER; - - m_control = new wxTextCtrl(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - style); - - // 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(const wxRect& WXUNUSED(rectCell), - 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; -#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_startValue = grid->GetTable()->GetValue(row, col); - - DoBeginEdit(m_startValue); -} - -void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue) -{ - Text()->SetValue(startValue); - Text()->SetInsertionPointEnd(); - Text()->SetSelection(-1, -1); - Text()->SetFocus(); -} - -bool wxGridCellTextEditor::EndEdit(int row, int col, wxGrid* grid) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - bool changed = false; - wxString value = Text()->GetValue(); - if (value != m_startValue) - changed = true; - - if (changed) - grid->GetTable()->SetValue(row, col, value); - - m_startValue = wxEmptyString; - - // No point in setting the text of the hidden control - //Text()->SetValue(m_startValue); - - return changed; -} - -void wxGridCellTextEditor::Reset() -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - DoReset(m_startValue); -} - -void wxGridCellTextEditor::DoReset(const wxString& startValue) -{ - Text()->SetValue(startValue); - Text()->SetInsertionPointEnd(); -} - -bool wxGridCellTextEditor::IsAcceptedKey(wxKeyEvent& event) -{ - 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(); - wxChar ch; - long pos; - -#if wxUSE_UNICODE - ch = event.GetUnicodeKey(); - if (ch <= 127) - ch = (wxChar)event.GetKeyCode(); -#else - ch = (wxChar)event.GetKeyCode(); -#endif - - switch (ch) - { - case WXK_DELETE: - // delete the character at the cursor - pos = tc->GetInsertionPoint(); - if (pos < tc->GetLastPosition()) - tc->Remove(pos, pos + 1); - break; - - case WXK_BACK: - // delete the character before the cursor - pos = tc->GetInsertionPoint(); - if (pos > 0) - tc->Remove(pos - 1, pos); - break; - - default: - tc->WriteText(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( _T("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(wxTextValidator(wxFILTER_NUMERIC)); -#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_valueOld = table->GetValueAsLong(row, col); - } - else - { - m_valueOld = 0; - wxString sValue = table->GetValue(row, col); - if (! sValue.ToLong(&m_valueOld) && ! sValue.empty()) - { - wxFAIL_MSG( _T("this cell doesn't have numeric value") ); - return; - } - } - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - Spin()->SetValue((int)m_valueOld); - Spin()->SetFocus(); - } - else -#endif - { - DoBeginEdit(GetString()); - } -} - -bool wxGridCellNumberEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - bool changed; - long value = 0; - wxString text; - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - value = Spin()->GetValue(); - changed = value != m_valueOld; - if (changed) - text = wxString::Format(wxT("%ld"), value); - } - else -#endif - { - text = Text()->GetValue(); - changed = (text.empty() || text.ToLong(&value)) && (value != m_valueOld); - } - - if ( changed ) - { - if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER)) - grid->GetTable()->SetValueAsLong(row, col, value); - else - grid->GetTable()->SetValue(row, col, text); - } - - return changed; -} - -void wxGridCellNumberEditor::Reset() -{ -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - Spin()->SetValue((int)m_valueOld); - } - 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(_T(',')).ToLong(&tmp) ) - { - m_min = (int)tmp; - - if ( params.AfterFirst(_T(',')).ToLong(&tmp) ) - { - m_max = (int)tmp; - - // skip the error message below - return; - } - } - - wxLogDebug(_T("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) -{ - m_width = width; - m_precision = precision; -} - -void wxGridCellFloatEditor::Create(wxWindow* parent, - wxWindowID id, - wxEvtHandler* evtHandler) -{ - wxGridCellTextEditor::Create(parent, id, evtHandler); - -#if wxUSE_VALIDATORS - Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); -#endif -} - -void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid) -{ - // first get the value - wxGridTableBase *table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - { - m_valueOld = table->GetValueAsDouble(row, col); - } - else - { - m_valueOld = 0.0; - wxString sValue = table->GetValue(row, col); - if (! sValue.ToDouble(&m_valueOld) && ! sValue.empty()) - { - wxFAIL_MSG( _T("this cell doesn't have float value") ); - return; - } - } - - DoBeginEdit(GetString()); -} - -bool wxGridCellFloatEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - double value = 0.0; - wxString text(Text()->GetValue()); - - if ( (text.empty() || text.ToDouble(&value)) && - !wxIsSameDouble(value, m_valueOld) ) - { - if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT)) - grid->GetTable()->SetValueAsDouble(row, col, value); - else - grid->GetTable()->SetValue(row, col, text); - - return true; - } - - return false; -} - -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 == _T(".") ); -#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; - } - else - { - long tmp; - if ( params.BeforeFirst(_T(',')).ToLong(&tmp) ) - { - m_width = (int)tmp; - - if ( params.AfterFirst(_T(',')).ToLong(&tmp) ) - { - m_precision = (int)tmp; - - // skip the error message below - return; - } - } - - wxLogDebug(_T("Invalid wxGridCellFloatEditor parameter string '%s' ignored"), params.c_str()); - } -} - -wxString wxGridCellFloatEditor::GetString() const -{ - wxString fmt; - if ( m_precision == -1 && m_width != -1) - { - // default precision - fmt.Printf(_T("%%%d.f"), m_width); - } - else if ( m_precision != -1 && m_width == -1) - { - // default width - fmt.Printf(_T("%%.%df"), m_precision); - } - else if ( m_precision != -1 && m_width != -1 ) - { - fmt.Printf(_T("%%%d.%df"), m_width, m_precision); - } - else - { - // default width/precision - fmt = _T("%f"); - } - - return wxString::Format(fmt, m_valueOld); -} - -bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event) -{ - if ( wxGridCellEditor::IsAcceptedKey(event) ) - { - const int keycode = event.GetKeyCode(); - if ( isascii(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); -#else - const wxString decimalPoint(_T('.')); -#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] = { _T(""), _T("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_startValue = grid->GetTable()->GetValueAsBool(row, col); - } - else - { - wxString cellval( grid->GetTable()->GetValue(row, col) ); - - if ( cellval == ms_stringValues[false] ) - m_startValue = false; - else if ( cellval == ms_stringValues[true] ) - m_startValue = 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( _T("invalid value for a cell with bool editor!") ); - } - } - - CBox()->SetValue(m_startValue); - CBox()->SetFocus(); -} - -bool wxGridCellBoolEditor::EndEdit(int row, int col, - wxGrid* grid) -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - bool changed = false; - bool value = CBox()->GetValue(); - if ( value != m_startValue ) - changed = true; - - if ( changed ) - { - wxGridTableBase * const table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) - table->SetValueAsBool(row, col, value); - else - table->SetValue(row, col, GetValue()); - } - - return changed; -} - -void wxGridCellBoolEditor::Reset() -{ - wxASSERT_MSG(m_control, - wxT("The wxGridCellEditor must be created first!")); - - CBox()->SetValue(m_startValue); -} - -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 = 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::PaintBackground(const wxRect& rectCell, - 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); -} - -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_startValue = grid->GetTable()->GetValue(row, col); - - if (m_allowOthers) - { - Combo()->SetValue(m_startValue); - 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_startValue); - if (pos == wxNOT_FOUND) - pos = 0; - Combo()->SetSelection(pos); - } - - Combo()->SetFocus(); - - 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 row, int col, - wxGrid* grid) -{ - wxString value = Combo()->GetValue(); - if ( value == m_startValue ) - return false; - - grid->GetTable()->SetValue(row, col, value); - - return true; -} - -void wxGridCellChoiceEditor::Reset() -{ - Combo()->SetValue(m_startValue); - Combo()->SetInsertionPointEnd(); -} - -void wxGridCellChoiceEditor::SetParameters(const wxString& params) -{ - if ( !params ) - { - // what can we do? - return; - } - - m_choices.Empty(); - - wxStringTokenizer tk(params, _T(',')); - 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 - -// ---------------------------------------------------------------------------- -// wxGridCellEditorEvtHandler -// ---------------------------------------------------------------------------- - -void wxGridCellEditorEvtHandler::OnKillFocus(wxFocusEvent& event) -{ - // Don't disable the cell if we're just starting to edit it - if (m_inSetFocus) - return; - - // accept changes - m_grid->DisableCellEditControl(); - - event.Skip(); -} - -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; - } -} - -// ---------------------------------------------------------------------------- -// wxGridCellWorker is an (almost) empty common base class for -// wxGridCellRenderer and wxGridCellEditor managing ref counting -// ---------------------------------------------------------------------------- - -void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params)) -{ - // nothing to do -} - -wxGridCellWorker::~wxGridCellWorker() -{ -} - -// ============================================================================ -// renderer classes -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxGridCellRenderer -// ---------------------------------------------------------------------------- - -void wxGridCellRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int WXUNUSED(row), int WXUNUSED(col), - bool isSelected) -{ - dc.SetBackgroundMode( wxSOLID ); - - // grey out fields if the grid is disabled - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - dc.SetBrush( wxBrush(grid.GetSelectionBackground(), wxSOLID) ); - } - else - { - dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxSOLID) ); - } - } - else - { - dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxSOLID)); - } - - dc.SetPen( *wxTRANSPARENT_PEN ); - dc.DrawRectangle(rect); -} - -// ---------------------------------------------------------------------------- -// wxGridCellStringRenderer -// ---------------------------------------------------------------------------- - -void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, - const wxGridCellAttr& attr, - wxDC& dc, - bool isSelected) -{ - dc.SetBackgroundMode( wxTRANSPARENT ); - - // TODO some special colours for attr.IsReadOnly() case? - - // different coloured text when the grid is disabled - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - dc.SetTextBackground( grid.GetSelectionBackground() ); - dc.SetTextForeground( grid.GetSelectionForeground() ); - } - else - { - dc.SetTextBackground( attr.GetBackgroundColour() ); - dc.SetTextForeground( attr.GetTextColour() ); - } - } - else - { - dc.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - } - - dc.SetFont( attr.GetFont() ); -} - -wxSize wxGridCellStringRenderer::DoGetBestSize(const wxGridCellAttr& attr, - wxDC& dc, - const wxString& text) -{ - wxCoord x = 0, y = 0, max_x = 0; - dc.SetFont(attr.GetFont()); - wxStringTokenizer tk(text, _T('\n')); - while ( tk.HasMoreTokens() ) - { - dc.GetTextExtent(tk.GetNextToken(), &x, &y); - max_x = wxMax(max_x, x); - } - - y *= 1 + text.Freq(wxT('\n')); // multiply by the number of lines. - - return wxSize(max_x, y); -} - -wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) -{ - return DoGetBestSize(attr, dc, grid.GetCellValue(row, col)); -} - -void wxGridCellStringRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) -{ - wxRect rect = rectCell; - rect.Inflate(-1); - - // erase only this cells background, overflow cells should have been erased - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - - int overflowCols = 0; - - if (attr.GetOverflow()) - { - int cols = grid.GetNumberCols(); - int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth(); - int cell_rows, cell_cols; - attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <= 0 - if ((best_width > rectCell.width) && (col < cols) && grid.GetTable()) - { - int i, c_cols, c_rows; - for (i = col+cell_cols; i < cols; i++) - { - bool is_empty = true; - for (int j=row; j < row + cell_rows; j++) - { - // check w/ anchor cell for multicell block - grid.GetCellSize(j, i, &c_rows, &c_cols); - if (c_rows > 0) - c_rows = 0; - if (!grid.GetTable()->IsEmptyCell(j + c_rows, i)) - { - is_empty = false; - break; - } - } - - if (is_empty) - { - rect.width += grid.GetColSize(i); - } - else - { - i--; - break; - } - - if (rect.width >= best_width) - break; - } - - overflowCols = i - col - cell_cols + 1; - if (overflowCols >= cols) - overflowCols = cols - 1; - } - - if (overflowCols > 0) // redraw overflow cells w/ proper hilight - { - hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned - wxRect clip = rect; - clip.x += rectCell.width; - // draw each overflow cell individually - int col_end = col + cell_cols + overflowCols; - if (col_end >= grid.GetNumberCols()) - col_end = grid.GetNumberCols() - 1; - for (int i = col + cell_cols; i <= col_end; i++) - { - clip.width = grid.GetColSize(i) - 1; - dc.DestroyClippingRegion(); - dc.SetClippingRegion(clip); - - SetTextColoursAndFont(grid, attr, dc, - grid.IsInSelection(row,i)); - - grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), - rect, hAlign, vAlign); - clip.x += grid.GetColSize(i) - 1; - } - - rect = rectCell; - rect.Inflate(-1); - rect.width++; - dc.DestroyClippingRegion(); - } - } - - // now we only have to draw the text - SetTextColoursAndFont(grid, attr, dc, isSelected); - - grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), - rect, hAlign, vAlign); -} - -// ---------------------------------------------------------------------------- -// wxGridCellNumberRenderer -// ---------------------------------------------------------------------------- - -wxString wxGridCellNumberRenderer::GetString(const wxGrid& grid, int row, int col) -{ - wxGridTableBase *table = grid.GetTable(); - wxString text; - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - { - text.Printf(_T("%ld"), table->GetValueAsLong(row, col)); - } - else - { - text = table->GetValue(row, col); - } - - return text; -} - -void wxGridCellNumberRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) -{ - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - SetTextColoursAndFont(grid, attr, dc, isSelected); - - // draw the text right aligned by default - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - hAlign = wxALIGN_RIGHT; - - wxRect rect = rectCell; - rect.Inflate(-1); - - grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); -} - -wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) -{ - return DoGetBestSize(attr, dc, GetString(grid, row, col)); -} - -// ---------------------------------------------------------------------------- -// wxGridCellFloatRenderer -// ---------------------------------------------------------------------------- - -wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, int precision) -{ - SetWidth(width); - SetPrecision(precision); -} - -wxGridCellRenderer *wxGridCellFloatRenderer::Clone() const -{ - wxGridCellFloatRenderer *renderer = new wxGridCellFloatRenderer; - renderer->m_width = m_width; - renderer->m_precision = m_precision; - renderer->m_format = m_format; - - return renderer; -} - -wxString wxGridCellFloatRenderer::GetString(const wxGrid& grid, int row, int col) -{ - wxGridTableBase *table = grid.GetTable(); - - bool hasDouble; - double val; - wxString text; - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - { - val = table->GetValueAsDouble(row, col); - hasDouble = true; - } - else - { - text = table->GetValue(row, col); - hasDouble = text.ToDouble(&val); - } - - if ( hasDouble ) - { - if ( !m_format ) - { - if ( m_width == -1 ) - { - if ( m_precision == -1 ) - { - // default width/precision - m_format = _T("%f"); - } - else - { - m_format.Printf(_T("%%.%df"), m_precision); - } - } - else if ( m_precision == -1 ) - { - // default precision - m_format.Printf(_T("%%%d.f"), m_width); - } - else - { - m_format.Printf(_T("%%%d.%df"), m_width, m_precision); - } - } - - text.Printf(m_format, val); - - } - //else: text already contains the string - - return text; -} - -void wxGridCellFloatRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) -{ - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - SetTextColoursAndFont(grid, attr, dc, isSelected); - - // draw the text right aligned by default - int hAlign, vAlign; - attr.GetAlignment(&hAlign, &vAlign); - hAlign = wxALIGN_RIGHT; - - wxRect rect = rectCell; - rect.Inflate(-1); - - grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); -} - -wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - int row, int col) -{ - return DoGetBestSize(attr, dc, GetString(grid, row, col)); -} - -void wxGridCellFloatRenderer::SetParameters(const wxString& params) -{ - if ( !params ) - { - // reset to defaults - SetWidth(-1); - SetPrecision(-1); - } - else - { - wxString tmp = params.BeforeFirst(_T(',')); - if ( !tmp.empty() ) - { - long width; - if ( tmp.ToLong(&width) ) - { - SetWidth((int)width); - } - else - { - wxLogDebug(_T("Invalid wxGridCellFloatRenderer width parameter string '%s ignored"), params.c_str()); - } - } - - tmp = params.AfterFirst(_T(',')); - if ( !tmp.empty() ) - { - long precision; - if ( tmp.ToLong(&precision) ) - { - SetPrecision((int)precision); - } - else - { - wxLogDebug(_T("Invalid wxGridCellFloatRenderer precision parameter string '%s ignored"), params.c_str()); - } - } - } -} - -// ---------------------------------------------------------------------------- -// wxGridCellBoolRenderer +// constants // ---------------------------------------------------------------------------- -wxSize wxGridCellBoolRenderer::ms_sizeCheckMark; +wxGridCellCoords wxGridNoCellCoords( -1, -1 ); +wxRect wxGridNoCellRect( -1, -1, -1, -1 ); -// FIXME these checkbox size calculations are really ugly... +namespace +{ -// between checkmark and box -static const wxCoord wxGRID_CHECKMARK_MARGIN = 2; +// scroll line size +const size_t GRID_SCROLL_LINE_X = 15; +const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; -wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, - wxGridCellAttr& WXUNUSED(attr), - wxDC& WXUNUSED(dc), - int WXUNUSED(row), - int WXUNUSED(col)) -{ - // compute it only once (no locks for MT safeness in GUI thread...) - if ( !ms_sizeCheckMark.x ) - { - // get checkbox size - wxCheckBox *checkbox = new wxCheckBox(&grid, wxID_ANY, wxEmptyString); - wxSize size = checkbox->GetBestSize(); - wxCoord checkSize = size.y + 2 * wxGRID_CHECKMARK_MARGIN; +// the size of hash tables used a bit everywhere (the max number of elements +// in these hash tables is the number of rows/columns) +const int GRID_HASH_SIZE = 100; -#if defined(__WXMOTIF__) - checkSize -= size.y / 2; -#endif +// the minimal distance in pixels the mouse needs to move to start a drag +// operation +const int DRAG_SENSITIVITY = 3; - delete checkbox; +} // anonymous namespace - ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize; - } +#include "wx/arrimpl.cpp" - return ms_sizeCheckMark; -} +WX_DEFINE_OBJARRAY(wxGridCellCoordsArray) +WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray) + +// ---------------------------------------------------------------------------- +// events +// ---------------------------------------------------------------------------- + +wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_DCLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_BEGIN_DRAG, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_CLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_CLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_DCLICK, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_ROW_SIZE, wxGridSizeEvent ) +wxDEFINE_EVENT( wxEVT_GRID_COL_SIZE, wxGridSizeEvent ) +wxDEFINE_EVENT( wxEVT_GRID_COL_MOVE, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_COL_SORT, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGING, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGED, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_SELECT_CELL, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent ) +wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent ) + +// ============================================================================ +// implementation +// ============================================================================ + +IMPLEMENT_ABSTRACT_CLASS(wxGridCellEditorEvtHandler, wxEvtHandler) + +BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler ) + EVT_KILL_FOCUS( wxGridCellEditorEvtHandler::OnKillFocus ) + EVT_KEY_DOWN( wxGridCellEditorEvtHandler::OnKeyDown ) + EVT_CHAR( wxGridCellEditorEvtHandler::OnChar ) +END_EVENT_TABLE() + +BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick) + + EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize) + EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing) + EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize) + + EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder) + EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder) +END_EVENT_TABLE() -void wxGridCellBoolRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) +wxGridOperations& wxGridRowOperations::Dual() const { - wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + static wxGridColumnOperations s_colOper; - // draw a check mark in the centre (ignoring alignment - TODO) - wxSize size = GetBestSize(grid, attr, dc, row, col); + return s_colOper; +} - // don't draw outside the cell - wxCoord minSize = wxMin(rect.width, rect.height); - if ( size.x >= minSize || size.y >= minSize ) - { - // and even leave (at least) 1 pixel margin - size.x = size.y = minSize; - } +wxGridOperations& wxGridColumnOperations::Dual() const +{ + static wxGridRowOperations s_rowOper; - // draw a border around checkmark - int vAlign, hAlign; - attr.GetAlignment(&hAlign, &vAlign); + return s_rowOper; +} - wxRect rectBorder; - if (hAlign == wxALIGN_CENTRE) - { - rectBorder.x = rect.x + rect.width / 2 - size.x / 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_LEFT) - { - rectBorder.x = rect.x + 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } - else if (hAlign == wxALIGN_RIGHT) - { - rectBorder.x = rect.x + rect.width - size.x - 2; - rectBorder.y = rect.y + rect.height / 2 - size.y / 2; - rectBorder.width = size.x; - rectBorder.height = size.y; - } +// ---------------------------------------------------------------------------- +// wxGridCellWorker is an (almost) empty common base class for +// wxGridCellRenderer and wxGridCellEditor managing ref counting +// ---------------------------------------------------------------------------- - bool value; - if ( grid.GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) - { - value = grid.GetTable()->GetValueAsBool(row, col); - } - else - { - wxString cellval( grid.GetTable()->GetValue(row, col) ); - value = wxGridCellBoolEditor::IsTrueValue(cellval); - } +void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params)) +{ + // nothing to do +} - int flags = 0; - if (value) - flags |= wxCONTROL_CHECKED; - - wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); +wxGridCellWorker::~wxGridCellWorker() +{ } // ---------------------------------------------------------------------------- @@ -2578,21 +482,25 @@ wxGridCellEditor* wxGridCellAttr::GetEditor(const wxGrid* grid, int row, int col void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col) { + // Note: contrary to wxGridRowOrColAttrData::SetAttr, we must not + // touch attribute's reference counting explicitly, since this + // is managed by class wxGridCellWithAttr int n = FindIndex(row, col); if ( n == wxNOT_FOUND ) { - // add the attribute - m_attrs.Add(new wxGridCellWithAttr(row, col, attr)); + if ( attr ) + { + // add the attribute + m_attrs.Add(new wxGridCellWithAttr(row, col, attr)); + } + //else: nothing to do } - else + else // we already have an attribute for this cell { - // free the old attribute - m_attrs[(size_t)n].attr->DecRef(); - if ( attr ) { // change the attribute - m_attrs[(size_t)n].attr = attr; + m_attrs[(size_t)n].ChangeAttr(attr); } else { @@ -2604,7 +512,7 @@ void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col) wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = FindIndex(row, col); if ( n != wxNOT_FOUND ) @@ -2641,8 +549,6 @@ void wxGridCellAttrData::UpdateAttrRows( size_t pos, int numRows ) else { // ...or remove the attribute - // No need to DecRef the attribute itself since this is - // done be wxGridCellWithAttr's destructor! m_attrs.RemoveAt(n); n--; count--; @@ -2677,8 +583,6 @@ void wxGridCellAttrData::UpdateAttrCols( size_t pos, int numCols ) else { // ...or remove the attribute - // No need to DecRef the attribute itself since this is - // done be wxGridCellWithAttr's destructor! m_attrs.RemoveAt(n); n--; count--; @@ -2718,7 +622,7 @@ wxGridRowOrColAttrData::~wxGridRowOrColAttrData() wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = m_rowsOrCols.Index(rowOrCol); if ( n != wxNOT_FOUND ) @@ -2737,7 +641,8 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) { if ( attr ) { - // add the attribute + // add the attribute - no need to do anything to reference count + // since we take ownership of the attribute. m_rowsOrCols.Add(rowOrCol); m_attrs.Add(attr); } @@ -2746,15 +651,19 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) else { size_t n = (size_t)i; + if ( m_attrs[n] == attr ) + // nothing to do + return; if ( attr ) { - // change the attribute + // change the attribute, handling reference count manually, + // taking ownership of the new attribute. m_attrs[n]->DecRef(); m_attrs[n] = attr; } else { - // remove this attribute + // remove this attribute, handling reference count manually m_attrs[n]->DecRef(); m_rowsOrCols.RemoveAt(n); m_attrs.RemoveAt(n); @@ -2799,7 +708,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols wxGridCellAttrProvider::wxGridCellAttrProvider() { - m_data = (wxGridCellAttrProviderData *)NULL; + m_data = NULL; } wxGridCellAttrProvider::~wxGridCellAttrProvider() @@ -2815,7 +724,7 @@ void wxGridCellAttrProvider::InitData() wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind ) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; if ( m_data ) { switch (kind) @@ -2948,167 +857,6 @@ void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) } } -// ---------------------------------------------------------------------------- -// wxGridTypeRegistry -// ---------------------------------------------------------------------------- - -wxGridTypeRegistry::~wxGridTypeRegistry() -{ - size_t count = m_typeinfo.GetCount(); - for ( size_t i = 0; i < count; i++ ) - delete m_typeinfo[i]; -} - -void wxGridTypeRegistry::RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor) -{ - wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor); - - // is it already registered? - int loc = FindRegisteredDataType(typeName); - if ( loc != wxNOT_FOUND ) - { - delete m_typeinfo[loc]; - m_typeinfo[loc] = info; - } - else - { - m_typeinfo.Add(info); - } -} - -int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName) -{ - size_t count = m_typeinfo.GetCount(); - for ( size_t i = 0; i < count; i++ ) - { - if ( typeName == m_typeinfo[i]->m_typeName ) - { - return i; - } - } - - return wxNOT_FOUND; -} - -int wxGridTypeRegistry::FindDataType(const wxString& typeName) -{ - int index = FindRegisteredDataType(typeName); - if ( index == wxNOT_FOUND ) - { - // check whether this is one of the standard ones, in which case - // register it "on the fly" -#if wxUSE_TEXTCTRL - if ( typeName == wxGRID_VALUE_STRING ) - { - RegisterDataType(wxGRID_VALUE_STRING, - new wxGridCellStringRenderer, - new wxGridCellTextEditor); - } - else -#endif // wxUSE_TEXTCTRL -#if wxUSE_CHECKBOX - if ( typeName == wxGRID_VALUE_BOOL ) - { - RegisterDataType(wxGRID_VALUE_BOOL, - new wxGridCellBoolRenderer, - new wxGridCellBoolEditor); - } - else -#endif // wxUSE_CHECKBOX -#if wxUSE_TEXTCTRL - if ( typeName == wxGRID_VALUE_NUMBER ) - { - RegisterDataType(wxGRID_VALUE_NUMBER, - new wxGridCellNumberRenderer, - new wxGridCellNumberEditor); - } - else if ( typeName == wxGRID_VALUE_FLOAT ) - { - RegisterDataType(wxGRID_VALUE_FLOAT, - new wxGridCellFloatRenderer, - new wxGridCellFloatEditor); - } - else -#endif // wxUSE_TEXTCTRL -#if wxUSE_COMBOBOX - if ( typeName == wxGRID_VALUE_CHOICE ) - { - RegisterDataType(wxGRID_VALUE_CHOICE, - new wxGridCellStringRenderer, - new wxGridCellChoiceEditor); - } - else -#endif // wxUSE_COMBOBOX - { - return wxNOT_FOUND; - } - - // we get here only if just added the entry for this type, so return - // the last index - index = m_typeinfo.GetCount() - 1; - } - - return index; -} - -int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName) -{ - int index = FindDataType(typeName); - if ( index == wxNOT_FOUND ) - { - // the first part of the typename is the "real" type, anything after ':' - // are the parameters for the renderer - index = FindDataType(typeName.BeforeFirst(_T(':'))); - if ( index == wxNOT_FOUND ) - { - return wxNOT_FOUND; - } - - wxGridCellRenderer *renderer = GetRenderer(index); - wxGridCellRenderer *rendererOld = renderer; - renderer = renderer->Clone(); - rendererOld->DecRef(); - - wxGridCellEditor *editor = GetEditor(index); - wxGridCellEditor *editorOld = editor; - editor = editor->Clone(); - editorOld->DecRef(); - - // do it even if there are no parameters to reset them to defaults - wxString params = typeName.AfterFirst(_T(':')); - renderer->SetParameters(params); - editor->SetParameters(params); - - // register the new typename - RegisterDataType(typeName, renderer, editor); - - // we just registered it, it's the last one - index = m_typeinfo.GetCount() - 1; - } - - return index; -} - -wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) -{ - wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; - if (renderer) - renderer->IncRef(); - - return renderer; -} - -wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) -{ - wxGridCellEditor* editor = m_typeinfo[index]->m_editor; - if (editor) - editor->IncRef(); - - return editor; -} - // ---------------------------------------------------------------------------- // wxGridTableBase // ---------------------------------------------------------------------------- @@ -3117,8 +865,8 @@ IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject ) wxGridTableBase::wxGridTableBase() { - m_view = (wxGrid *) NULL; - m_attrProvider = (wxGridCellAttrProvider *) NULL; + m_view = NULL; + m_attrProvider = NULL; } wxGridTableBase::~wxGridTableBase() @@ -3148,14 +896,15 @@ wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAtt if ( m_attrProvider ) return m_attrProvider->GetAttr(row, col, kind); else - return (wxGridCellAttr *)NULL; + return NULL; } void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col) { if ( m_attrProvider ) { - attr->SetKind(wxGridCellAttr::Cell); + if ( attr ) + attr->SetKind(wxGridCellAttr::Cell); m_attrProvider->SetAttr(attr, row, col); } else @@ -3346,7 +1095,7 @@ void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col), wxGridTableMessage::wxGridTableMessage() { - m_table = (wxGridTableBase *) NULL; + m_table = NULL; m_id = -1; m_comInt1 = -1; m_comInt2 = -1; @@ -3422,15 +1171,6 @@ void wxGridStringTable::SetValue( int row, int col, const wxString& value ) m_data[row][col] = value; } -bool wxGridStringTable::IsEmptyCell( int row, int col ) -{ - wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()), - true, - _T("invalid row or column index in wxGridStringTable") ); - - return (m_data[row][col] == wxEmptyString); -} - void wxGridStringTable::Clear() { int row, col; @@ -3602,16 +1342,6 @@ bool wxGridStringTable::AppendCols( size_t numCols ) size_t curNumRows = m_data.GetCount(); -#if 0 - if ( !curNumRows ) - { - // TODO: something better than this ? - // - wxFAIL_MSG( wxT("Unable to append cols to a grid table with no rows.\nCall AppendRows() first") ); - return false; - } -#endif - for ( row = 0; row < curNumRows; row++ ) { m_data[row].Add( wxEmptyString, numCols ); @@ -3768,24 +1498,11 @@ void wxGridSubwindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event m_owner->CancelMouseCapture(); } -IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridRowLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent ) - EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridRowLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridRowLabelWindow::OnChar ) -END_EVENT_TABLE() - -wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} +END_EVENT_TABLE() void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { @@ -3813,51 +1530,18 @@ void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); -} - -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridRowLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridRowLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) + if (!m_owner->GetEventHandler()->ProcessEvent( event )) event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridColLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent ) - EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridColLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridColLabelWindow::OnChar ) END_EVENT_TABLE() -wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); @@ -3887,80 +1571,23 @@ void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); -} - -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridColLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridColLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) + if (!m_owner->GetEventHandler()->ProcessEvent( event )) event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow ) EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent ) EVT_PAINT( wxGridCornerLabelWindow::OnPaint ) - EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridCornerLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridCornerLabelWindow::OnChar ) END_EVENT_TABLE() -wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); - int client_height = 0; - int client_width = 0; - GetClientSize( &client_width, &client_height ); - - // VZ: any reason for this ifdef? (FIXME) -#if 0 -def __WXGTK__ - wxRect rect; - rect.SetX( 1 ); - rect.SetY( 1 ); - rect.SetWidth( client_width - 2 ); - rect.SetHeight( client_height - 2 ); - - wxRendererNative::Get().DrawHeaderButton( this, dc, rect, 0 ); -#else // !__WXGTK__ - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); - dc.DrawLine( client_width - 1, client_height - 1, client_width - 1, 0 ); - dc.DrawLine( client_width - 1, client_height - 1, 0, client_height - 1 ); - dc.DrawLine( 0, 0, client_width, 0 ); - dc.DrawLine( 0, 0, 0, client_height ); - - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 1, 1, client_width - 1, 1 ); - dc.DrawLine( 1, 1, 1, client_height - 1 ); -#endif + m_owner->DrawCornerLabel(dc); } void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) @@ -3970,34 +1597,12 @@ void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent(event); -} - -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridCornerLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridCornerLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) + if (!m_owner->GetEventHandler()->ProcessEvent(event)) event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) EVT_PAINT( wxGridWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel ) @@ -4010,20 +1615,6 @@ BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground ) END_EVENT_TABLE() -wxGridWindow::wxGridWindow( wxGrid *parent, - wxGridRowLabelWindow *rowLblWin, - wxGridColLabelWindow *colLblWin, - wxWindowID id, - const wxPoint &pos, - const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size, - wxCLIP_CHILDREN, wxT("grid window") ) -{ - m_owner = parent; - m_rowLabelWin = rowLblWin; - m_colLabelWin = colLblWin; -} - void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPaintDC dc( this ); @@ -4032,19 +1623,18 @@ void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); m_owner->DrawGridCellArea( dc, dirtyCells ); -#if WXGRID_DRAW_LINES + m_owner->DrawGridSpace( dc ); + m_owner->DrawAllGridLines( dc, reg ); -#endif - m_owner->DrawGridSpace( dc ); m_owner->DrawHighlight( dc, dirtyCells ); } void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect ) { wxWindow::ScrollWindow( dx, dy, rect ); - m_rowLabelWin->ScrollWindow( 0, dy, rect ); - m_colLabelWin->ScrollWindow( dx, 0, rect ); + m_owner->GetGridRowLabelWindow()->ScrollWindow( 0, dy, rect ); + m_owner->GetGridColLabelWindow()->ScrollWindow( dx, 0, rect ); } void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) @@ -4057,7 +1647,8 @@ void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); + if (!m_owner->GetEventHandler()->ProcessEvent( event )) + event.Skip(); } // This seems to be required for wxMotif/wxGTK otherwise the mouse @@ -4087,27 +1678,35 @@ void wxGridWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) ) void wxGridWindow::OnFocus(wxFocusEvent& event) { + // and if we have any selection, it has to be repainted, because it + // uses different colour when the grid is not focused: + if ( m_owner->IsSelection() ) + { + Refresh(); + } + else + { + // NB: Note that this code is in "else" branch only because the other + // branch refreshes everything and so there's no point in calling + // Refresh() again, *not* because it should only be done if + // !IsSelection(). If the above code is ever optimized to refresh + // only selected area, this needs to be moved out of the "else" + // branch so that it's always executed. + + // current cell cursor {dis,re}appears on focus change: + const wxGridCellCoords cursorCoords(m_owner->GetGridCursorRow(), + m_owner->GetGridCursorCol()); + const wxRect cursor = + m_owner->BlockToDeviceRect(cursorCoords, cursorCoords); + Refresh(true, &cursor); + } + if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip(); } -////////////////////////////////////////////////////////////////////// - -// Internal Helper function for computing row or column from some -// (unscrolled) coordinate value, using either -// m_defaultRowHeight/m_defaultColWidth or binary search on array -// of m_rowBottoms/m_ColRights to speed up the search! - -// Internal helper macros for simpler use of that function - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax); - #define internalXToCol(x) XToCol(x, true) -#define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \ - m_minAcceptableRowHeight, \ - m_rowBottoms, m_numRows, true) +#define internalYToRow(y) YToRow(y, true) ///////////////////////////////////////////////////////////////////// @@ -4172,30 +1771,6 @@ BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow ) EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground ) END_EVENT_TABLE() -wxGrid::wxGrid() -{ - // in order to make sure that a size event is not - // trigerred in a unfinished state - m_cornerLabelWin = NULL; - m_rowLabelWin = NULL; - m_colLabelWin = NULL; - m_gridWin = NULL; -} - -wxGrid::wxGrid( wxWindow *parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name ) - : wxScrolledWindow( parent, id, pos, size, (style | wxWANTS_CHARS), name ), - m_colMinWidths(GRID_HASH_SIZE), - m_rowMinHeights(GRID_HASH_SIZE) -{ - Create(); - SetInitialSize(size); -} - bool wxGrid::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) @@ -4209,6 +1784,7 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, Create(); SetInitialSize(size); + SetScrollRate(m_scrollLineX, m_scrollLineY); CalcDimensions(); return true; @@ -4216,6 +1792,14 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, wxGrid::~wxGrid() { + if ( m_winCapture ) + m_winCapture->ReleaseMouse(); + + // Ensure that the editor control is destroyed before the grid is, + // otherwise we crash later when the editor tries to do something with the + // half destroyed grid + HideCellEditControl(); + // Must do this or ~wxScrollHelper will pop the wrong event handler SetTargetWindow(this); ClearAttrCache(); @@ -4250,15 +1834,8 @@ wxGrid::~wxGrid() void wxGrid::Create() { - // set to true by CreateGrid - m_created = false; - // create the type registry m_typeRegistry = new wxGridTypeRegistry; - m_selection = NULL; - - m_table = (wxGridTableBase *) NULL; - m_ownTable = false; m_cellEditCtrlEnabled = false; @@ -4290,31 +1867,11 @@ void wxGrid::Create() m_numCols = 0; m_currentCellCoords = wxGridNoCellCoords; - m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; - m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; - // subwindow components that make up the wxGrid - m_rowLabelWin = new wxGridRowLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_colLabelWin = new wxGridColLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_cornerLabelWin = new wxGridCornerLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_gridWin = new wxGridWindow( this, - m_rowLabelWin, - m_colLabelWin, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); + m_rowLabelWin = new wxGridRowLabelWindow(this); + CreateColumnWindow(); + m_cornerLabelWin = new wxGridCornerLabelWindow(this); + m_gridWin = new wxGridWindow( this ); SetTargetWindow( m_gridWin ); @@ -4334,38 +1891,51 @@ void wxGrid::Create() m_cornerLabelWin->SetOwnBackgroundColour(lbg); m_rowLabelWin->SetOwnForegroundColour(lfg); m_rowLabelWin->SetOwnBackgroundColour(lbg); - m_colLabelWin->SetOwnForegroundColour(lfg); - m_colLabelWin->SetOwnBackgroundColour(lbg); + m_colWindow->SetOwnForegroundColour(lfg); + m_colWindow->SetOwnBackgroundColour(lbg); m_gridWin->SetOwnForegroundColour(gfg); m_gridWin->SetOwnBackgroundColour(gbg); - Init(); + m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); + m_labelTextColour = m_rowLabelWin->GetForegroundColour(); + + // now that we have the grid window, use its font to compute the default + // row height + m_defaultRowHeight = m_gridWin->GetCharHeight(); +#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl() + m_defaultRowHeight += 8; +#else + m_defaultRowHeight += 4; +#endif + +} + +void wxGrid::CreateColumnWindow() +{ + if ( m_useNativeHeader ) + { + m_colWindow = new wxGridHeaderCtrl(this); + m_colLabelHeight = m_colWindow->GetBestSize().y; + } + else // draw labels ourselves + { + m_colWindow = new wxGridColLabelWindow(this); + m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + } } bool wxGrid::CreateGrid( int numRows, int numCols, - wxGrid::wxGridSelectionModes selmode ) + wxGridSelectionModes selmode ) { wxCHECK_MSG( !m_created, false, wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") ); - m_numRows = numRows; - m_numCols = numCols; - - m_table = new wxGridStringTable( m_numRows, m_numCols ); - m_table->SetView( this ); - m_ownTable = true; - m_selection = new wxGridSelection( this, selmode ); - - CalcDimensions(); - - m_created = true; - - return m_created; + return SetTable(new wxGridStringTable(numRows, numCols), true, selmode); } -void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) +void wxGrid::SetSelectionMode(wxGridSelectionModes selmode) { wxCHECK_RET( m_created, wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") ); @@ -4375,14 +1945,16 @@ void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const { - wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells, + wxCHECK_MSG( m_created, wxGridSelectCells, wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") ); return m_selection->GetSelectionMode(); } -bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, - wxGrid::wxGridSelectionModes selmode ) +bool +wxGrid::SetTable(wxGridTableBase *table, + bool takeOwnership, + wxGrid::wxGridSelectionModes selmode ) { bool checkSelection = false; if ( m_created ) @@ -4418,6 +1990,9 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, m_numRows = table->GetNumberRows(); m_numCols = table->GetNumberCols(); + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + m_table = table; m_table->SetView( this ); m_ownTable = takeOwnership; @@ -4427,22 +2002,22 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, // If the newly set table is smaller than the // original one current cell and selection regions // might be invalid, - m_selectingKeyboard = wxGridNoCellCoords; + m_selectedBlockCorner = wxGridNoCellCoords; m_currentCellCoords = wxGridCellCoords(wxMin(m_numRows, m_currentCellCoords.GetRow()), wxMin(m_numCols, m_currentCellCoords.GetCol())); - if (m_selectingTopLeft.GetRow() >= m_numRows || - m_selectingTopLeft.GetCol() >= m_numCols) + if (m_selectedBlockTopLeft.GetRow() >= m_numRows || + m_selectedBlockTopLeft.GetCol() >= m_numCols) { - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; } else - m_selectingBottomRight = + m_selectedBlockBottomRight = wxGridCellCoords(wxMin(m_numRows, - m_selectingBottomRight.GetRow()), + m_selectedBlockBottomRight.GetRow()), wxMin(m_numCols, - m_selectingBottomRight.GetCol())); + m_selectedBlockBottomRight.GetCol())); } CalcDimensions(); @@ -4454,28 +2029,30 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, void wxGrid::Init() { - m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; - m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + m_created = false; - if ( m_rowLabelWin ) - { - m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); - } - else - { - m_labelBackgroundColour = *wxWHITE; - } + m_cornerLabelWin = NULL; + m_rowLabelWin = NULL; + m_colWindow = NULL; + m_gridWin = NULL; + + m_table = NULL; + m_ownTable = false; + + m_selection = NULL; + m_defaultCellAttr = NULL; + m_typeRegistry = NULL; + m_winCapture = NULL; - m_labelTextColour = *wxBLACK; + m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; + m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; // init attr cache m_attrCache.row = -1; m_attrCache.col = -1; m_attrCache.attr = NULL; - // TODO: something better than this ? - // - m_labelFont = this->GetFont(); + m_labelFont = GetFont(); m_labelFont.SetWeight( wxBOLD ); m_rowLabelHorizAlign = wxALIGN_CENTRE; @@ -4486,19 +2063,15 @@ void wxGrid::Init() m_colLabelTextOrientation = wxHORIZONTAL; m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH; - m_defaultRowHeight = m_gridWin->GetCharHeight(); + m_defaultRowHeight = 0; // this will be initialized after creation m_minAcceptableColWidth = WXGRID_MIN_COL_WIDTH; m_minAcceptableRowHeight = WXGRID_MIN_ROW_HEIGHT; -#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl() - m_defaultRowHeight += 8; -#else - m_defaultRowHeight += 4; -#endif - m_gridLineColour = wxColour( 192,192,192 ); m_gridLinesEnabled = true; + m_gridLinesClipHorz = + m_gridLinesClipVert = true; m_cellHighlightColour = *wxBLACK; m_cellHighlightPenWidth = 2; m_cellHighlightROPenWidth = 1; @@ -4506,7 +2079,7 @@ void wxGrid::Init() m_canDragColMove = false; m_cursorMode = WXGRID_CURSOR_SELECT_CELL; - m_winCapture = (wxWindow *)NULL; + m_winCapture = NULL; m_canDragRowSize = true; m_canDragColSize = true; m_canDragGridSize = true; @@ -4515,6 +2088,11 @@ void wxGrid::Init() m_dragRowOrCol = -1; m_isDragging = false; m_startDragPos = wxDefaultPosition; + + m_sortCol = wxNOT_FOUND; + m_sortIsAscending = true; + + m_useNativeHeader = m_nativeColumnLabels = false; m_waitForSlowClick = false; @@ -4524,7 +2102,9 @@ void wxGrid::Init() m_currentCellCoords = wxGridNoCellCoords; - ClearSelection(); + m_selectedBlockTopLeft = + m_selectedBlockBottomRight = + m_selectedBlockCorner = wxGridNoCellCoords; m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); @@ -4580,10 +2160,9 @@ void wxGrid::InitColWidths() m_colWidths.Add( m_defaultColWidth, m_numCols ); - int colRight = 0; for ( int i = 0; i < m_numCols; i++ ) { - colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth; + int colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth; m_colRights.Add( colRight ); } } @@ -4664,17 +2243,25 @@ void wxGrid::CalcDimensions() if ( y >= h ) y = wxMax( h - 1, 0 ); - // do set scrollbar parameters - SetScrollbars( m_scrollLineX, m_scrollLineY, - GetScrollX(w), GetScrollY(h), - x, y, - GetBatchCount() != 0); + // update the virtual size and refresh the scrollbars to reflect it + m_gridWin->SetVirtualSize(w, h); + Scroll(x, y); + AdjustScrollbars(); // if our OnSize() hadn't been called (it would if we have scrollbars), we // still must reposition the children CalcWindowSizes(); } +wxSize wxGrid::GetSizeAvailableForScrollTarget(const wxSize& size) +{ + wxSize sizeGridWin(size); + sizeGridWin.x -= m_rowLabelWidth; + sizeGridWin.y -= m_colLabelHeight; + + return sizeGridWin; +} + void wxGrid::CalcWindowSizes() { // escape if the window is has not been fully created yet @@ -4685,44 +2272,26 @@ void wxGrid::CalcWindowSizes() int cw, ch; GetClientSize( &cw, &ch ); - // this block of code tries to work around the following problem: the grid - // could have been just resized to have enough space to show the full grid - // window contents without the scrollbars, but its client size could be - // not big enough because the grid has the scrollbars right now and so the - // scrollbars would remain even though we don't need them any more - // - // to prevent this from happening, check if we have enough space for - // everything without the scrollbars and explicitly disable them then - wxSize size = GetSize() - GetWindowBorderSize(); - if ( size != wxSize(cw, ch) ) - { - // check if we have enough space for grid window after accounting for - // the fixed size elements - size.x -= m_rowLabelWidth; - size.y -= m_colLabelHeight; - - const wxSize vsize = m_gridWin->GetVirtualSize(); - - if ( size.x >= vsize.x && size.y >= vsize.y ) - { - // yes, we do, so remove the scrollbars and use the new client size - // (which should be the same as full window size - borders now) - SetScrollbars(0, 0, 0, 0); - GetClientSize(&cw, &ch); - } - } + // the grid may be too small to have enough space for the labels yet, don't + // size the windows to negative sizes in this case + int gw = cw - m_rowLabelWidth; + int gh = ch - m_colLabelHeight; + if (gw < 0) + gw = 0; + if (gh < 0) + gh = 0; if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() ) m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight ); - if ( m_colLabelWin && m_colLabelWin->IsShown() ) - m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw - m_rowLabelWidth, m_colLabelHeight ); + if ( m_colWindow && m_colWindow->IsShown() ) + m_colWindow->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); if ( m_rowLabelWin && m_rowLabelWin->IsShown() ) - m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch - m_colLabelHeight ); + m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh ); if ( m_gridWin && m_gridWin->IsShown() ) - m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, cw - m_rowLabelWidth, ch - m_colLabelHeight ); + m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, gw, gh ); } // this is called when the grid table sends a message @@ -4744,20 +2313,6 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) // cell it might want to save that stuff to might no longer exist. HideCellEditControl(); -#if 0 - // if we were using the default widths/heights so far, we must change them - // now - if ( m_colWidths.IsEmpty() ) - { - InitColWidths(); - } - - if ( m_rowHeights.IsEmpty() ) - { - InitRowHeights(); - } -#endif - switch ( msg.GetId() ) { case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: @@ -4908,6 +2463,9 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int numCols = msg.GetCommandInt2(); m_numCols += numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + if ( !m_colAt.IsEmpty() ) { //Shift the column IDs @@ -4962,7 +2520,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4973,6 +2531,8 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int numCols = msg.GetCommandInt(); int oldNumCols = m_numCols; m_numCols += numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5015,7 +2575,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -5026,6 +2586,8 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) size_t pos = msg.GetCommandInt(); int numCols = msg.GetCommandInt2(); m_numCols -= numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5090,7 +2652,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -5240,9 +2802,8 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); // find the cells within these bounds - // - int row, col; - for ( row = internalYToRow(top); row < m_numRows; row++ ) + wxArrayInt cols; + for ( int row = internalYToRow(top); row < m_numRows; row++ ) { if ( GetRowBottom(row) <= top ) continue; @@ -5250,19 +2811,23 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const if ( GetRowTop(row) > bottom ) break; - int colPos; - for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ ) + // add all dirty cells in this row: notice that the columns which + // are dirty don't depend on the row so we compute them only once + // for the first dirty row and then reuse for all the next ones + if ( cols.empty() ) { - col = GetColAt( colPos ); + // do determine the dirty columns + for ( int pos = XToPos(left); pos <= XToPos(right); pos++ ) + cols.push_back(GetColAt(pos)); - if ( GetColRight(col) <= left ) - continue; - - if ( GetColLeft(col) > right ) + // if there are no dirty columns at all, nothing to do + if ( cols.empty() ) break; - - cellsExposed.Add( wxGridCellCoords( row, col ) ); } + + const size_t count = cols.size(); + for ( size_t n = 0; n < count; n++ ) + cellsExposed.Add(wxGridCellCoords(row, cols[n])); } ++iter; @@ -5316,13 +2881,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) if ( (row = YToRow( y )) >= 0 ) { if ( m_selection ) - { - m_selection->SelectRow( row, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->SelectRow(row, event); } } break; @@ -5373,22 +2932,16 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) { if ( event.ShiftDown() ) { - m_selection->SelectBlock( m_currentCellCoords.GetRow(), - 0, - row, - GetNumberCols() - 1, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectBlock + ( + m_currentCellCoords.GetRow(), 0, + row, GetNumberCols() - 1, + event + ); } else { - m_selection->SelectRow( row, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectRow(row, event); } } @@ -5422,7 +2975,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // adjust row height depending on label text AutoSizeRowLabelSize( row ); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } } @@ -5490,21 +3043,107 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) } } +void wxGrid::UpdateColumnSortingIndicator(int col) +{ + wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" ); + + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + else if ( m_nativeColumnLabels ) + m_colWindow->Refresh(); + //else: sorting indicator display not yet implemented in grid version +} + +void wxGrid::SetSortingColumn(int col, bool ascending) +{ + if ( col == m_sortCol ) + { + // we are already using this column for sorting (or not sorting at all) + // but we might still change the sorting order, check for it + if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending ) + { + m_sortIsAscending = ascending; + + UpdateColumnSortingIndicator(m_sortCol); + } + } + else // we're changing the column used for sorting + { + const int sortColOld = m_sortCol; + + // change it before updating the column as we want GetSortingColumn() + // to return the correct new value + m_sortCol = col; + + if ( sortColOld != wxNOT_FOUND ) + UpdateColumnSortingIndicator(sortColOld); + + if ( m_sortCol != wxNOT_FOUND ) + { + m_sortIsAscending = ascending; + UpdateColumnSortingIndicator(m_sortCol); + } + } +} + +void wxGrid::DoColHeaderClick(int col) +{ + // we consider that the grid was resorted if this event is processed and + // not vetoed + if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 ) + { + SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true); + Refresh(); + } +} + +void wxGrid::DoStartResizeCol(int col) +{ + m_dragRowOrCol = col; + m_dragLastPos = -1; + DoUpdateResizeColWidth(GetColWidth(m_dragRowOrCol)); +} + +void wxGrid::DoUpdateResizeCol(int x) +{ + int cw, ch, dummy, top; + m_gridWin->GetClientSize( &cw, &ch ); + CalcUnscrolledPosition( 0, 0, &dummy, &top ); + + wxClientDC dc( m_gridWin ); + PrepareDC( dc ); + + x = wxMax( x, GetColLeft(m_dragRowOrCol) + GetColMinimalWidth(m_dragRowOrCol)); + dc.SetLogicalFunction(wxINVERT); + if ( m_dragLastPos >= 0 ) + { + dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); + } + dc.DrawLine( x, top, x, top + ch ); + m_dragLastPos = x; +} + +void wxGrid::DoUpdateResizeColWidth(int w) +{ + DoUpdateResizeCol(GetColLeft(m_dragRowOrCol) + w); +} + void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { - int x, y, col; + int x, y; wxPoint pos( event.GetPosition() ); CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); + int col = XToCol(x); if ( event.Dragging() ) { if (!m_isDragging) { m_isDragging = true; - m_colLabelWin->CaptureMouse(); + GetColLabelWindow()->CaptureMouse(); - if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL ) - m_dragRowOrCol = XToCol( x ); + if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 ) + DoStartMoveCol(col); } if ( event.LeftIsDown() ) @@ -5512,69 +3151,45 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - { - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); - - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - - x = wxMax( x, GetColLeft(m_dragRowOrCol) + - GetColMinimalWidth(m_dragRowOrCol)); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - } - dc.DrawLine( x, top, x, top + ch ); - m_dragLastPos = x; - } + DoUpdateResizeCol(x); break; case WXGRID_CURSOR_SELECT_COL: { - if ( (col = XToCol( x )) >= 0 ) + if ( col != -1 ) { if ( m_selection ) - { - m_selection->SelectCol( col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->SelectCol(col, event); } } break; case WXGRID_CURSOR_MOVE_COL: { - if ( x < 0 ) - m_moveToCol = GetColAt( 0 ); - else - m_moveToCol = XToCol( x ); + int posNew = XToPos(x); + int colNew = GetColAt(posNew); + // determine the position of the drop marker int markerX; - - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); + if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) ) + markerX = GetColRight(colNew); else - markerX = GetColLeft( m_moveToCol ); + markerX = GetColLeft(colNew); if ( markerX != m_dragLastPos ) { - wxClientDC dc( m_colLabelWin ); + wxClientDC dc( GetColLabelWindow() ); + DoPrepareDC(dc); int cw, ch; - m_colLabelWin->GetClientSize( &cw, &ch ); + GetColLabelWindow()->GetClientSize( &cw, &ch ); markerX++; //Clean up the last indicator if ( m_dragLastPos >= 0 ) { - wxPen pen( m_colLabelWin->GetBackgroundColour(), 2 ); + wxPen pen( GetColLabelWindow()->GetBackgroundColour(), 2 ); dc.SetPen(pen); dc.DrawLine( m_dragLastPos + 1, 0, m_dragLastPos + 1, ch ); dc.SetPen(wxNullPen); @@ -5583,17 +3198,15 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) DrawColLabel( dc, XToCol( m_dragLastPos ) ); } + const wxColour *color; //Moving to the same place? Don't draw a marker - if ( (m_moveToCol == m_dragRowOrCol) - || (GetColPos( m_moveToCol ) == GetColPos( m_dragRowOrCol ) + 1) - || (m_moveToCol < 0 && m_dragRowOrCol == GetColAt( m_numCols - 1 ))) - { - m_dragLastPos = -1; - return; - } + if ( colNew == m_dragRowOrCol ) + color = wxLIGHT_GREY; + else + color = wxBLUE; //Draw the marker - wxPen pen( *wxBLUE, 2 ); + wxPen pen( *color, 2 ); dc.SetPen(pen); dc.DrawLine( markerX, 0, markerX, ch ); @@ -5619,8 +3232,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) if (m_isDragging) { - if (m_colLabelWin->HasCapture()) - m_colLabelWin->ReleaseMouse(); + if (GetColLabelWindow()->HasCapture()) + GetColLabelWindow()->ReleaseMouse(); m_isDragging = false; } @@ -5628,7 +3241,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( event.Entering() || event.Leaving() ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); } // ------------ Left button pressed @@ -5641,21 +3254,20 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( XToEdgeOfCol(x) < 0 ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) ) { if ( m_canDragColMove ) { //Show button as pressed - wxClientDC dc( m_colLabelWin ); + wxClientDC dc( GetColLabelWindow() ); int colLeft = GetColLeft( col ); int colRight = GetColRight( col ) - 1; - dc.SetPen( wxPen( m_colLabelWin->GetBackgroundColour(), 1 ) ); + dc.SetPen( wxPen( GetColLabelWindow()->GetBackgroundColour(), 1 ) ); dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 ); dc.DrawLine( colLeft, 1, colRight, 1 ); - ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, GetColLabelWindow()); } else { @@ -5665,25 +3277,20 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { if ( event.ShiftDown() ) { - m_selection->SelectBlock( 0, - m_currentCellCoords.GetCol(), - GetNumberRows() - 1, col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectBlock + ( + 0, m_currentCellCoords.GetCol(), + GetNumberRows() - 1, col, + event + ); } else { - m_selection->SelectCol( col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectCol(col, event); } } - ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, GetColLabelWindow()); } } } @@ -5692,7 +3299,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // starting to drag-resize a col // if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); } } @@ -5700,10 +3307,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( event.LeftDClick() ) { - col = XToEdgeOfCol(x); - if ( col < 0 ) + const int colEdge = XToEdgeOfCol(x); + if ( colEdge == -1 ) { - col = XToCol(x); if ( col >= 0 && ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) ) { @@ -5713,9 +3319,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) else { // adjust column width depending on label text - AutoSizeColLabelSize( col ); + AutoSizeColLabelSize( colEdge ); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } } @@ -5728,28 +3334,32 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { case WXGRID_CURSOR_RESIZE_COL: DoEndDragResizeCol(); - - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event ); break; case WXGRID_CURSOR_MOVE_COL: - DoEndDragMoveCol(); - - SendEvent( wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol, event ); + if ( m_dragLastPos == -1 || col == m_dragRowOrCol ) + { + // the column didn't actually move anywhere + if ( col != -1 ) + DoColHeaderClick(col); + m_colWindow->Refresh(); // "unpress" the column + } + else + { + DoEndMoveCol(XToPos(x)); + } break; case WXGRID_CURSOR_SELECT_COL: case WXGRID_CURSOR_SELECT_CELL: case WXGRID_CURSOR_RESIZE_ROW: case WXGRID_CURSOR_SELECT_ROW: - // nothing to do (?) + if ( col != -1 ) + DoColHeaderClick(col); break; } - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } @@ -5757,7 +3367,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDown() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) ) { @@ -5769,7 +3378,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDClick() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) ) { @@ -5788,12 +3396,12 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { // don't capture the cursor yet if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow(), false); } } } @@ -5836,6 +3444,8 @@ void wxGrid::CancelMouseCapture() if ( m_winCapture ) { m_isDragging = false; + m_startDragPos = wxDefaultPosition; + m_cursorMode = WXGRID_CURSOR_SELECT_CELL; m_winCapture->SetCursor( *wxSTANDARD_CURSOR ); m_winCapture = NULL; @@ -5862,9 +3472,9 @@ void wxGrid::ChangeCursorMode(CursorMode mode, wxLogTrace(_T("grid"), _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"), - win == m_colLabelWin ? _T("colLabelWin") - : win ? _T("rowLabelWin") - : _T("gridWin"), + win == m_colWindow ? _T("colLabelWin") + : win ? _T("rowLabelWin") + : _T("gridWin"), cursorModes[m_cursorMode], cursorModes[mode]); #endif @@ -5881,9 +3491,8 @@ void wxGrid::ChangeCursorMode(CursorMode mode, if ( m_winCapture ) { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = (wxWindow *)NULL; + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; } m_cursorMode = mode; @@ -5918,617 +3527,584 @@ void wxGrid::ChangeCursorMode(CursorMode mode, } } -void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) -{ - int x, y; - wxPoint pos( event.GetPosition() ); - CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); +// ---------------------------------------------------------------------------- +// grid mouse event processing +// ---------------------------------------------------------------------------- - wxGridCellCoords coords; - XYToCell( x, y, coords ); +void +wxGrid::DoGridCellDrag(wxMouseEvent& event, + const wxGridCellCoords& coords, + bool isFirstDrag) +{ + if ( coords == wxGridNoCellCoords ) + return; // we're outside any valid cell - int cell_rows, cell_cols; - bool isFirstDrag = !m_isDragging; - GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); - if ((cell_rows < 0) || (cell_cols < 0)) + // Hide the edit control, so it won't interfere with drag-shrinking. + if ( IsCellEditControlShown() ) { - coords.SetRow(coords.GetRow() + cell_rows); - coords.SetCol(coords.GetCol() + cell_cols); + HideCellEditControl(); + SaveEditControlValue(); } - if ( event.Dragging() ) + switch ( event.GetModifiers() ) { - //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol()); - - // Don't start doing anything until the mouse has been dragged at - // least 3 pixels in any direction... - if (! m_isDragging) - { - if (m_startDragPos == wxDefaultPosition) - { - m_startDragPos = pos; - return; - } - if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4) - return; - } - - m_isDragging = true; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - // Hide the edit control, so it - // won't interfere with drag-shrinking. - if ( IsCellEditControlShown() ) - { - HideCellEditControl(); - SaveEditControlValue(); - } + case wxMOD_CMD: + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; + UpdateBlockBeingSelected(m_selectedBlockCorner, coords); + break; - if ( coords != wxGridNoCellCoords ) + case wxMOD_NONE: + if ( CanDragCell() ) { - if ( event.CmdDown() ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - HighlightBlock( m_selectingKeyboard, coords ); - } - else if ( CanDragCell() ) - { - if ( isFirstDrag ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - - SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG, - coords.GetRow(), - coords.GetCol(), - event ); - return; - } - } - else + if ( isFirstDrag ) { - if ( !IsSelection() ) - { - HighlightBlock( coords, coords ); - } - else - { - HighlightBlock( m_currentCellCoords, coords ); - } - } + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; - if (! IsVisible(coords)) - { - MakeCellVisible(coords); - // TODO: need to introduce a delay or something here. The - // scrolling is way to fast, at least on MSW - also on GTK. + SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event); + return; } } - // Have we captured the mouse yet? - if (! m_winCapture) - { - m_winCapture = m_gridWin; - m_winCapture->CaptureMouse(); - } + UpdateBlockBeingSelected(m_currentCellCoords, coords); + break; - } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) - { - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); + default: + // we don't handle the other key modifiers + event.Skip(); + } +} - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - y = wxMax( y, GetRowTop(m_dragRowOrCol) + - GetRowMinimalHeight(m_dragRowOrCol) ); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos ); - } - dc.DrawLine( left, y, left+cw, y ); - m_dragLastPos = y; - } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) - { - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); +void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper) +{ + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - x = wxMax( x, GetColLeft(m_dragRowOrCol) + - GetColMinimalWidth(m_dragRowOrCol) ); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - } - dc.DrawLine( x, top, x, top + ch ); - m_dragLastPos = x; + const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)), + m_gridWin->GetClientSize()); + + // erase the previously drawn line, if any + if ( m_dragLastPos >= 0 ) + oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); + + // we need the vertical position for rows and horizontal for columns here + m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition())); + + // don't allow resizing beneath the minimal size + const int posMin = oper.GetLineStartPos(this, m_dragRowOrCol) + + oper.GetMinimalLineSize(this, m_dragRowOrCol); + if ( m_dragLastPos < posMin ) + m_dragLastPos = posMin; + + // and draw it at the new position + oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); +} + +void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords) +{ + if ( !m_isDragging ) + { + // Don't start doing anything until the mouse has been dragged far + // enough + const wxPoint& pt = event.GetPosition(); + if ( m_startDragPos == wxDefaultPosition ) + { + m_startDragPos = pt; + return; } - return; + if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY && + abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY ) + return; } - m_isDragging = false; - m_startDragPos = wxDefaultPosition; + const bool isFirstDrag = !m_isDragging; + m_isDragging = true; - // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL - // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under - // wxGTK -#if 0 - if ( event.Entering() || event.Leaving() ) + switch ( m_cursorMode ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - m_gridWin->SetCursor( *wxSTANDARD_CURSOR ); + case WXGRID_CURSOR_SELECT_CELL: + DoGridCellDrag(event, coords, isFirstDrag); + break; + + case WXGRID_CURSOR_RESIZE_ROW: + DoGridLineDrag(event, wxGridRowOperations()); + break; + + case WXGRID_CURSOR_RESIZE_COL: + DoGridLineDrag(event, wxGridColumnOperations()); + break; + + default: + event.Skip(); } - else -#endif // 0 - // ------------ Left button pressed - // - if ( event.LeftDown() && coords != wxGridNoCellCoords ) + if ( isFirstDrag ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) - { - if ( !event.CmdDown() ) - ClearSelection(); - if ( event.ShiftDown() ) - { - if ( m_selection ) - { - m_selection->SelectBlock( m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol(), - coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - } - else if ( XToEdgeOfCol(x) < 0 && - YToEdgeOfRow(y) < 0 ) - { - DisableCellEditControl(); - MakeCellVisible( coords ); + m_winCapture = m_gridWin; + m_winCapture->CaptureMouse(); + } +} - if ( event.CmdDown() ) - { - if ( m_selection ) - { - m_selection->ToggleCellSelection( coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; - m_selectingKeyboard = coords; - } - else - { - m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords; - SetCurrentCell( coords ); - if ( m_selection ) - { - if ( m_selection->GetSelectionMode() != - wxGrid::wxGridSelectCells ) - { - HighlightBlock( coords, coords ); - } - } - } - } - } +void +wxGrid::DoGridCellLeftDown(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) ) + { + // event handled by user code, no need to do anything here + return; } - // ------------ Left double click - // - else if ( event.LeftDClick() && coords != wxGridNoCellCoords ) + if ( !event.CmdDown() ) + ClearSelection(); + + if ( event.ShiftDown() ) + { + if ( m_selection ) + { + m_selection->SelectBlock(m_currentCellCoords, coords, event); + m_selectedBlockCorner = coords; + } + } + else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) { DisableCellEditControl(); + MakeCellVisible( coords ); - if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 ) + if ( event.CmdDown() ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + if ( m_selection ) { - // we want double click to select a cell and start editing - // (i.e. to behave in same way as sequence of two slow clicks): - m_waitForSlowClick = true; + m_selection->ToggleCellSelection(coords, event); } + + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = coords; + } + else + { + m_waitForSlowClick = m_currentCellCoords == coords && + coords != wxGridNoCellCoords; + SetCurrentCell( coords ); } } +} - // ------------ Left button released - // - else if ( event.LeftUp() ) +void +wxGrid::DoGridCellLeftDClick(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) ) { - if (m_winCapture) - { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - } - - if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) - { - ClearSelection(); - EnableCellEditControl(); + // we want double click to select a cell and start editing + // (i.e. to behave in same way as sequence of two slow clicks): + m_waitForSlowClick = true; + } + } +} - wxGridCellAttr *attr = GetCellAttr(coords); - wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); - editor->StartingClick(); - editor->DecRef(); - attr->DecRef(); +void +wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) +{ + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + { + if (m_winCapture) + { + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + } - m_waitForSlowClick = false; - } - else if ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords ) - { - if ( m_selection ) - { - m_selection->SelectBlock( m_selectingTopLeft.GetRow(), - m_selectingTopLeft.GetCol(), - m_selectingBottomRight.GetRow(), - m_selectingBottomRight.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) + { + ClearSelection(); + EnableCellEditControl(); - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + wxGridCellAttr *attr = GetCellAttr(coords); + wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); + editor->StartingClick(); + editor->DecRef(); + attr->DecRef(); - // Show the edit control, if it has been hidden for - // drag-shrinking. - ShowCellEditControl(); - } + m_waitForSlowClick = false; } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) + else if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeRow(); + if ( m_selection ) + { + m_selection->SelectBlock( m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event ); + } - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); - } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeCol(); + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event ); + // Show the edit control, if it has been hidden for + // drag-shrinking. + ShowCellEditControl(); } + } + else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + DoEndDragResizeRow(); - m_dragLastPos = -1; + // Note: we are ending the event *after* doing + // default processing in this case + // + SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); + } + else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + DoEndDragResizeCol(); } - // ------------ Right button down + m_dragLastPos = -1; +} + +void +wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) + { + // out of grid cell area + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + return; + } + + int dragRow = YToEdgeOfRow( pos.y ); + int dragCol = XToEdgeOfCol( pos.x ); + + // Dragging on the corner of a cell to resize in both + // directions is not implemented yet... // - else if ( event.RightDown() && coords != wxGridNoCellCoords ) + if ( dragRow >= 0 && dragCol >= 0 ) { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + return; + } + + if ( dragRow >= 0 ) + { + m_dragRowOrCol = dragRow; + + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // no default action at the moment + if ( CanDragRowSize() && CanDragGridSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); } } + // When using the native header window we can only resize the columns by + // dragging the dividers in it because we can't make it enter into the + // column resizing mode programmatically + else if ( dragCol >= 0 && !m_useNativeHeader ) + { + m_dragRowOrCol = dragCol; - // ------------ Right double click - // - else if ( event.RightDClick() && coords != wxGridNoCellCoords ) + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + { + if ( CanDragColSize() && CanDragGridSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); + } + } + else // Neither on a row or col edge { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - // no default action at the moment + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); } } +} + +void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) +{ + const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); + + // coordinates of the cell under mouse + wxGridCellCoords coords = XYToCell(pos); + + int cell_rows, cell_cols; + GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); + if ( (cell_rows < 0) || (cell_cols < 0) ) + { + coords.SetRow(coords.GetRow() + cell_rows); + coords.SetCol(coords.GetCol() + cell_cols); + } + + if ( event.Dragging() ) + { + if ( event.LeftIsDown() ) + DoGridDragEvent(event, coords); + else + event.Skip(); + return; + } - // ------------ Moving and no button action - // - else if ( event.Moving() && !event.IsButton() ) - { - if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) - { - // out of grid cell area - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - return; - } - - int dragRow = YToEdgeOfRow( y ); - int dragCol = XToEdgeOfCol( x ); + m_isDragging = false; + m_startDragPos = wxDefaultPosition; - // Dragging on the corner of a cell to resize in both - // directions is not implemented yet... - // - if ( dragRow >= 0 && dragCol >= 0 ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - return; - } + // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL + // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under + // wxGTK +#if 0 + if ( event.Entering() || event.Leaving() ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + m_gridWin->SetCursor( *wxSTANDARD_CURSOR ); + } +#endif // 0 - if ( dragRow >= 0 ) + // deal with various button presses + if ( event.IsButton() ) + { + if ( coords != wxGridNoCellCoords ) { - m_dragRowOrCol = dragRow; + DisableCellEditControl(); - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); - } + if ( event.LeftDown() ) + DoGridCellLeftDown(event, coords, pos); + else if ( event.LeftDClick() ) + DoGridCellLeftDClick(event, coords, pos); + else if ( event.RightDown() ) + SendEvent(wxEVT_GRID_CELL_RIGHT_CLICK, coords, event); + else if ( event.RightDClick() ) + SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event); } - else if ( dragCol >= 0 ) - { - m_dragRowOrCol = dragCol; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragColSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); - } - } - else // Neither on a row or col edge + // this one should be called even if we're not over any cell + if ( event.LeftUp() ) { - if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - } + DoGridCellLeftUp(event, coords); } } + else if ( event.Moving() ) + { + DoGridMouseMoveEvent(event, coords, pos); + } + else // unknown mouse event? + { + event.Skip(); + } } -void wxGrid::DoEndDragResizeRow() +void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) { - if ( m_dragLastPos >= 0 ) - { - // erase the last line and resize the row - // - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); + if ( m_dragLastPos == -1 ) + return; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( left, m_dragLastPos, left + cw, m_dragLastPos ); - HideCellEditControl(); - SaveEditControlValue(); + const wxGridOperations& doper = oper.Dual(); - int rowTop = GetRowTop(m_dragRowOrCol); - SetRowSize( m_dragRowOrCol, - wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) ); + const wxSize size = m_gridWin->GetClientSize(); - if ( !GetBatchCount() ) - { - // Only needed to get the correct rect.y: - wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) ); - rect.x = 0; - CalcScrolledPosition(0, rect.y, &dummy, &rect.y); - rect.width = m_rowLabelWidth; - rect.height = ch - rect.y; - m_rowLabelWin->Refresh( true, &rect ); - rect.width = cw; + const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0)); - // if there is a multicell block, paint all of it - if (m_table) - { - int i, cell_rows, cell_cols, subtract_rows = 0; - int leftCol = XToCol(left); - int rightCol = internalXToCol(left + cw); - if (leftCol >= 0) - { - for (i=leftCol; iRefresh( false, &rect ); - } + // erase the last line we drew + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); - ShowCellEditControl(); - } -} + const int posLineStart = oper.Select(ptOrigin); + const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size); + oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos); -void wxGrid::DoEndDragResizeCol() -{ - if ( m_dragLastPos >= 0 ) + // temporarily hide the edit control before resizing + HideCellEditControl(); + SaveEditControlValue(); + + // do resize the line + const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol); + oper.SetLineSize(this, m_dragRowOrCol, + wxMax(m_dragLastPos - lineStart, + oper.GetMinimalLineSize(this, m_dragRowOrCol))); + + m_dragLastPos = -1; + + // refresh now if we're not frozen + if ( !GetBatchCount() ) { - // erase the last line and resize the col - // - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); + // we need to refresh everything beyond the resized line in the header + // window - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - HideCellEditControl(); - SaveEditControlValue(); + // get the position from which to refresh in the other direction + wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0))); + rect.SetPosition(CalcScrolledPosition(rect.GetPosition())); - int colLeft = GetColLeft(m_dragRowOrCol); - SetColSize( m_dragRowOrCol, - wxMax( m_dragLastPos - colLeft, - GetColMinimalWidth(m_dragRowOrCol) ) ); + // we only need the ordinate (for rows) or abscissa (for columns) here, + // and need to cover the entire window in the other direction + oper.Select(rect) = 0; - if ( !GetBatchCount() ) + wxRect rectHeader(rect.GetPosition(), + oper.MakeSize + ( + oper.GetHeaderWindowSize(this), + doper.Select(size) - doper.Select(rect) + )); + + oper.GetHeaderWindow(this)->Refresh(true, &rectHeader); + + + // also refresh the grid window: extend the rectangle + if ( m_table ) { - // Only needed to get the correct rect.x: - wxRect rect ( CellToRect( 0, m_dragRowOrCol ) ); - rect.y = 0; - CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); - rect.width = cw - rect.x; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); - rect.height = ch; - - // if there is a multicell block, paint all of it - if (m_table) + oper.SelectSize(rect) = oper.Select(size); + + int subtractLines = 0; + const int lineStart = oper.PosToLine(this, posLineStart); + if ( lineStart >= 0 ) { - int i, cell_rows, cell_cols, subtract_cols = 0; - int topRow = YToRow(top); - int bottomRow = internalYToRow(top + cw); - if (topRow >= 0) + // ensure that if we have a multi-cell block we redraw all of + // it by increasing the refresh area to cover it entirely if a + // part of it is affected + const int lineEnd = oper.PosToLine(this, posLineEnd, true); + for ( int line = lineStart; line < lineEnd; line++ ) { - for (i=topRow; iRefresh( false, &rect ); - } + int startPos = + oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines); + startPos = doper.CalcScrolledPosition(this, startPos); - ShowCellEditControl(); + doper.Select(rect) = startPos; + doper.SelectSize(rect) = doper.Select(size) - startPos; + + m_gridWin->Refresh(false, &rect); + } } + + // show the edit control back again + ShowCellEditControl(); } -void wxGrid::DoEndDragMoveCol() +void wxGrid::DoEndDragResizeRow() { - //The user clicked on the column but didn't actually drag - if ( m_dragLastPos < 0 ) - { - m_colLabelWin->Refresh(); //Do this to "unpress" the column - return; - } + DoEndDragResizeLine(wxGridRowOperations()); +} - int newPos; - if ( m_moveToCol == -1 ) - newPos = m_numCols - 1; +void wxGrid::DoEndDragResizeCol(wxMouseEvent *event) +{ + DoEndDragResizeLine(wxGridColumnOperations()); + + // Note: we are ending the event *after* doing + // default processing in this case + // + if ( event ) + SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, *event ); else - { - newPos = GetColPos( m_moveToCol ); - if ( newPos > GetColPos( m_dragRowOrCol ) ) - newPos--; - } + SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol ); +} - SetColPos( m_dragRowOrCol, newPos ); +void wxGrid::DoStartMoveCol(int col) +{ + m_dragRowOrCol = col; } -void wxGrid::SetColPos( int colID, int newPos ) +void wxGrid::DoEndMoveCol(int pos) { - if ( m_colAt.IsEmpty() ) - { - m_colAt.Alloc( m_numCols ); + wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" ); - int i; - for ( i = 0; i < m_numCols; i++ ) - { - m_colAt.Add( i ); - } - } + if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 ) + SetColPos(m_dragRowOrCol, pos); + //else: vetoed by user - int oldPos = GetColPos( colID ); + m_dragRowOrCol = -1; +} - //Reshuffle the m_colAt array - if ( newPos > oldPos ) +void wxGrid::RefreshAfterColPosChange() +{ + // recalculate the column rights as the column positions have changed, + // unless we calculate them dynamically because all columns widths are the + // same and it's easy to do + if ( !m_colWidths.empty() ) { - int i; - for ( i = oldPos; i < newPos; i++ ) + int colRight = 0; + for ( int colPos = 0; colPos < m_numCols; colPos++ ) { - m_colAt[i] = m_colAt[i+1]; + int colID = GetColAt( colPos ); + + colRight += m_colWidths[colID]; + m_colRights[colID] = colRight; } } + + // and make the changes visible + if ( m_useNativeHeader ) + { + if ( m_colAt.empty() ) + GetGridColHeader()->ResetColumnsOrder(); + else + GetGridColHeader()->SetColumnsOrder(m_colAt); + } else { - int i; - for ( i = oldPos; i > newPos; i-- ) - { - m_colAt[i] = m_colAt[i-1]; - } + m_colWindow->Refresh(); } + m_gridWin->Refresh(); +} - m_colAt[newPos] = colID; +void wxGrid::SetColumnsOrder(const wxArrayInt& order) +{ + m_colAt = order; - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) - { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - int colID = GetColAt( colPos ); + RefreshAfterColPosChange(); +} - colRight += m_colWidths[colID]; - m_colRights[colID] = colRight; - } +void wxGrid::SetColPos(int idx, int pos) +{ + // we're going to need m_colAt now, initialize it if needed + if ( m_colAt.empty() ) + { + m_colAt.reserve(m_numCols); + for ( int i = 0; i < m_numCols; i++ ) + m_colAt.push_back(i); } - m_colLabelWin->Refresh(); - m_gridWin->Refresh(); + wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos); + + RefreshAfterColPosChange(); } +void wxGrid::ResetColPos() +{ + m_colAt.clear(); + RefreshAfterColPosChange(); +} void wxGrid::EnableDragColMove( bool enable ) { if ( m_canDragColMove == enable ) return; - m_canDragColMove = enable; - - if ( !m_canDragColMove ) + if ( m_useNativeHeader ) { - m_colAt.Clear(); + // update all columns to make them [not] reorderable + GetGridColHeader()->SetColumnCount(m_numCols); + } - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) - { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - colRight += m_colWidths[colPos]; - m_colRights[colPos] = colRight; - } - } + m_canDragColMove = enable; - m_colLabelWin->Refresh(); - m_gridWin->Refresh(); - } + // we use to call ResetColPos() from here if !enable but this doesn't seem + // right as it would mean there would be no way to "freeze" the current + // columns order by disabling moving them after putting them in the desired + // order, whereas now you can always call ResetColPos() manually if needed } @@ -6560,7 +4136,7 @@ bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg ) // The behaviour of this function depends on the grid table class // Clear() function. For the default wxGridStringTable class the -// behavious is to replace all cell contents with wxEmptyString but +// behaviour is to replace all cell contents with wxEmptyString but // not to change the number of rows or cols. // void wxGrid::ClearGrid() @@ -6576,156 +4152,48 @@ void wxGrid::ClearGrid() } } -bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->InsertRows( pos, numRows ); - return done; - - // the table will have sent the results of the insert row - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - bool done = m_table && m_table->AppendRows( numRows ); - return done; - - // the table will have sent the results of the append row - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->DeleteRows( pos, numRows ); - return done; - // the table will have sent the results of the delete row - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t), + int pos, int num, bool WXUNUSED(updateLabels) ) { - // TODO: something with updateLabels flag + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") ); + if ( !m_table ) return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->InsertCols( pos, numCols ); - return done; - // the table will have sent the results of the insert col - // operation to this view object as a grid table message - } - - return false; -} -bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") ); - return false; - } + if ( IsCellEditControlEnabled() ) + DisableCellEditControl(); - if ( m_table ) - { - bool done = m_table->AppendCols( numCols ); - return done; - // the table will have sent the results of the append col - // operation to this view object as a grid table message - } + return (m_table->*funcModify)(pos, num); - return false; + // the table will have sent the results of the insert row + // operation to this view object as a grid table message } -bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), + int num, bool WXUNUSED(updateLabels)) { - // TODO: something with updateLabels flag + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") ); + if ( !m_table ) return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->DeleteCols( pos, numCols ); - return done; - // the table will have sent the results of the delete col - // operation to this view object as a grid table message - } - return false; + return (m_table->*funcAppend)(num); } // // ----- event handlers // -// Generate a grid event based on a mouse event and -// return the result of ProcessEvent() -// -int wxGrid::SendEvent( const wxEventType type, - int row, int col, - wxMouseEvent& mouseEv ) +// Generate a grid event based on a mouse event and return: +// -1 if the event was vetoed +// +1 if the event was processed (but not vetoed) +// 0 if the event wasn't handled +int +wxGrid::SendEvent(const wxEventType type, + int row, int col, + wxMouseEvent& mouseEv) { bool claimed, vetoed; @@ -6739,10 +4207,7 @@ int wxGrid::SendEvent( const wxEventType type, rowOrCol, mouseEv.GetX() + GetRowLabelSize(), mouseEv.GetY() + GetColLabelSize(), - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); @@ -6753,13 +4218,10 @@ int wxGrid::SendEvent( const wxEventType type, wxGridRangeSelectEvent gridEvt( GetId(), type, this, - m_selectingTopLeft, - m_selectingBottomRight, + m_selectedBlockTopLeft, + m_selectedBlockBottomRight, true, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); @@ -6783,10 +4245,7 @@ int wxGrid::SendEvent( const wxEventType type, pos.x, pos.y, false, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); } @@ -6799,10 +4258,7 @@ int wxGrid::SendEvent( const wxEventType type, mouseEv.GetX() + GetRowLabelSize(), mouseEv.GetY() + GetColLabelSize(), false, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); } @@ -6814,11 +4270,10 @@ int wxGrid::SendEvent( const wxEventType type, return claimed ? 1 : 0; } -// Generate a grid event of specified type and return the result -// of ProcessEvent(). +// Generate a grid event of specified type, return value same as above // -int wxGrid::SendEvent( const wxEventType type, - int row, int col ) +int +wxGrid::SendEvent(const wxEventType type, int row, int col, const wxString& s) { bool claimed, vetoed; @@ -6834,6 +4289,7 @@ int wxGrid::SendEvent( const wxEventType type, else { wxGridEvent gridEvt( GetId(), type, this, row, col ); + gridEvt.SetString(s); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); @@ -6856,7 +4312,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) { // Don't do anything if between Begin/EndBatch... // EndBatch() will do all this on the last nested one anyway. - if (! GetBatchCount()) + if ( m_created && !GetBatchCount() ) { // Refresh to get correct scrolled position: wxScrolledWindow::Refresh(eraseb, rect); @@ -6914,7 +4370,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) if ( width_cell > 0 && height_label > 0 ) { wxRect anotherrect(x, rect_y, width_cell, height_label); - m_colLabelWin->Refresh(eraseb, &anotherrect); + m_colWindow->Refresh(eraseb, &anotherrect); } // Paint row labels part intersecting rect. @@ -6934,7 +4390,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) else { m_cornerLabelWin->Refresh(eraseb, NULL); - m_colLabelWin->Refresh(eraseb, NULL); + m_colWindow->Refresh(eraseb, NULL); m_rowLabelWin->Refresh(eraseb, NULL); m_gridWin->Refresh(eraseb, NULL); } @@ -6945,8 +4401,8 @@ void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event)) { if (m_targetWindow != this) // check whether initialisation has been done { - // update our children window positions and scrollbars - CalcDimensions(); + // reposition our children windows + CalcWindowSizes(); } } @@ -7061,8 +4517,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) case WXK_HOME: if ( event.ControlDown() ) { - MakeCellVisible( 0, 0 ); - SetCurrentCell( 0, 0 ); + GoToCell(0, 0); } else { @@ -7073,8 +4528,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) case WXK_END: if ( event.ControlDown() ) { - MakeCellVisible( m_numRows - 1, m_numCols - 1 ); - SetCurrentCell( m_numRows - 1, m_numCols - 1 ); + GoToCell(m_numRows - 1, m_numCols - 1); } else { @@ -7091,25 +4545,34 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) break; case WXK_SPACE: - if ( event.ControlDown() ) + // Ctrl-Space selects the current column, Shift-Space -- the + // current row and Ctrl-Shift-Space -- everything + switch ( m_selection ? event.GetModifiers() : wxMOD_NONE ) { - if ( m_selection ) - { - m_selection->ToggleCellSelection( - m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - break; - } + case wxMOD_CONTROL: + m_selection->SelectCol(m_currentCellCoords.GetCol()); + break; + + case wxMOD_SHIFT: + m_selection->SelectRow(m_currentCellCoords.GetRow()); + break; + + case wxMOD_CONTROL | wxMOD_SHIFT: + m_selection->SelectBlock(0, 0, + m_numRows - 1, m_numCols - 1); + break; - if ( !IsEditable() ) - MoveCursorRight( false ); - else - event.Skip(); + case wxMOD_NONE: + if ( !IsEditable() ) + { + MoveCursorRight(false); + break; + } + //else: fall through + + default: + event.Skip(); + } break; default: @@ -7127,26 +4590,21 @@ void wxGrid::OnKeyUp( wxKeyEvent& event ) // if ( event.GetKeyCode() == WXK_SHIFT ) { - if ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords ) + if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) { if ( m_selection ) { m_selection->SelectBlock( - m_selectingTopLeft.GetRow(), - m_selectingTopLeft.GetCol(), - m_selectingBottomRight.GetRow(), - m_selectingBottomRight.GetCol(), - event.ControlDown(), - true, - event.AltDown(), - event.MetaDown() ); + m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event); } } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; - m_selectingKeyboard = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = wxGridNoCellCoords; } } @@ -7195,12 +4653,12 @@ void wxGrid::OnEraseBackground(wxEraseEvent&) { } -void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) +bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) { - if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) ) + if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 ) { - // the event has been intercepted - do nothing - return; + // the event has been vetoed - do nothing + return false; } #if !defined(__WXMAC__) @@ -7241,51 +4699,66 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) m_currentCellCoords = coords; wxGridCellAttr *attr = GetCellAttr( coords ); -#if !defined(__WXMAC__) +#if !defined(__WXMAC__) DrawCellHighlight( dc, attr ); #endif attr->DecRef(); + + return true; } -void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol ) +void +wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, + int bottomRow, int rightCol) { - int temp; - wxGridCellCoords updateTopLeft, updateBottomRight; - if ( m_selection ) { - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows ) - { - leftCol = 0; - rightCol = GetNumberCols() - 1; - } - else if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns ) + switch ( m_selection->GetSelectionMode() ) { - topRow = 0; - bottomRow = GetNumberRows() - 1; + default: + wxFAIL_MSG( "unknown selection mode" ); + // fall through + + case wxGridSelectCells: + // arbitrary blocks selection allowed so just use the cell + // coordinates as is + break; + + case wxGridSelectRows: + // only full rows selection allowd, ensure that we do select + // full rows + leftCol = 0; + rightCol = GetNumberCols() - 1; + break; + + case wxGridSelectColumns: + // same as above but for columns + topRow = 0; + bottomRow = GetNumberRows() - 1; + break; + + case wxGridSelectRowsOrColumns: + // in this mode we can select only full rows or full columns so + // it doesn't make sense to select blocks at all (and we can't + // extend the block because there is no preferred direction, we + // could only extend it to cover the entire grid but this is + // not useful) + return; } } - if ( topRow > bottomRow ) - { - temp = topRow; - topRow = bottomRow; - bottomRow = temp; - } + m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); + MakeCellVisible(m_selectedBlockCorner); - if ( leftCol > rightCol ) - { - temp = leftCol; - leftCol = rightCol; - rightCol = temp; - } + EnsureFirstLessThanSecond(topRow, bottomRow); + EnsureFirstLessThanSecond(leftCol, rightCol); - updateTopLeft = wxGridCellCoords( topRow, leftCol ); - updateBottomRight = wxGridCellCoords( bottomRow, rightCol ); + wxGridCellCoords updateTopLeft = wxGridCellCoords(topRow, leftCol), + updateBottomRight = wxGridCellCoords(bottomRow, rightCol); // First the case that we selected a completely new area - if ( m_selectingTopLeft == wxGridNoCellCoords || - m_selectingBottomRight == wxGridNoCellCoords ) + if ( m_selectedBlockTopLeft == wxGridNoCellCoords || + m_selectedBlockBottomRight == wxGridNoCellCoords ) { wxRect rect; rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ), @@ -7294,8 +4767,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo } // Now handle changing an existing selection area. - else if ( m_selectingTopLeft != updateTopLeft || - m_selectingBottomRight != updateBottomRight ) + else if ( m_selectedBlockTopLeft != updateTopLeft || + m_selectedBlockBottomRight != updateBottomRight ) { // Compute two optimal update rectangles: // Either one rectangle is a real subset of the @@ -7309,36 +4782,16 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo int i; // Store intermediate values - wxCoord oldLeft = m_selectingTopLeft.GetCol(); - wxCoord oldTop = m_selectingTopLeft.GetRow(); - wxCoord oldRight = m_selectingBottomRight.GetCol(); - wxCoord oldBottom = m_selectingBottomRight.GetRow(); + wxCoord oldLeft = m_selectedBlockTopLeft.GetCol(); + wxCoord oldTop = m_selectedBlockTopLeft.GetRow(); + wxCoord oldRight = m_selectedBlockBottomRight.GetCol(); + wxCoord oldBottom = m_selectedBlockBottomRight.GetRow(); // Determine the outer/inner coordinates. - if (oldLeft > leftCol) - { - temp = oldLeft; - oldLeft = leftCol; - leftCol = temp; - } - if (oldTop > topRow ) - { - temp = oldTop; - oldTop = topRow; - topRow = temp; - } - if (oldRight < rightCol ) - { - temp = oldRight; - oldRight = rightCol; - rightCol = temp; - } - if (oldBottom < bottomRow) - { - temp = oldBottom; - oldBottom = bottomRow; - bottomRow = temp; - } + EnsureFirstLessThanSecond(oldLeft, leftCol); + EnsureFirstLessThanSecond(oldTop, topRow); + EnsureFirstLessThanSecond(rightCol, oldRight); + EnsureFirstLessThanSecond(bottomRow, oldBottom); // Now, either the stuff marked old is the outer // rectangle or we don't have a situation where one @@ -7391,8 +4844,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo } // change selection - m_selectingTopLeft = updateTopLeft; - m_selectingBottomRight = updateBottomRight; + m_selectedBlockTopLeft = updateTopLeft; + m_selectedBlockBottomRight = updateBottomRight; } // @@ -7576,7 +5029,7 @@ void wxGrid::DrawGridSpace( wxDC& dc ) int left, top; CalcUnscrolledPosition( 0, 0, &left, &top ); - dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxSOLID) ); + dc.SetBrush(GetDefaultCellBackgroundColour()); dc.SetPen( *wxTRANSPARENT_PEN ); if ( right > rightCol ) @@ -7600,11 +5053,6 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) return; // we draw the cell border ourselves -#if !WXGRID_DRAW_LINES - if ( m_gridLinesEnabled ) - DrawCellBorder( dc, coords ); -#endif - wxGridCellAttr* attr = GetCellAttr(row, col); bool isCurrent = coords == m_currentCellCoords; @@ -7638,6 +5086,10 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) { + // don't show highlight when the grid doesn't have focus + if ( !HasFocus() ) + return; + int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); @@ -7667,36 +5119,17 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) // Now draw the rectangle // use the cellHighlightColour if the cell is inside a selection, this // will ensure the cell is always visible. - dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground : m_cellHighlightColour, penWidth, wxSOLID)); + dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground + : m_cellHighlightColour, + penWidth)); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect); } - -#if 0 - // VZ: my experiments with 3D borders... - - // how to properly set colours for arbitrary bg? - wxCoord x1 = rect.x, - y1 = rect.y, - x2 = rect.x + rect.width - 1, - y2 = rect.y + rect.height - 1; - - dc.SetPen(*wxWHITE_PEN); - dc.DrawLine(x1, y1, x2, y1); - dc.DrawLine(x1, y1, x1, y2); - - dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1); - dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2); - - dc.SetPen(*wxBLACK_PEN); - dc.DrawLine(x1, y2, x2, y2); - dc.DrawLine(x2, y1, x2, y2 + 1); -#endif } wxPen wxGrid::GetDefaultGridLinePen() { - return wxPen(GetGridLineColour(), 1, wxSOLID); + return wxPen(GetGridLineColour()); } wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row)) @@ -7752,7 +5185,27 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) size_t count = cells.GetCount(); for ( size_t n = 0; n < count; n++ ) { - if ( cells[n] == m_currentCellCoords ) + wxGridCellCoords cell = cells[n]; + + // If we are using attributes, then we may have just exposed another + // cell in a partially-visible merged cluster of cells. If the "anchor" + // (upper left) cell of this merged cluster is the cell indicated by + // m_currentCellCoords, then we need to refresh the cell highlight even + // though the "anchor" itself is not part of our update segment. + if ( CanHaveAttributes() ) + { + int rows = 0, + cols = 0; + GetCellSize(cell.GetRow(), cell.GetCol(), &rows, &cols); + + if ( rows < 0 ) + cell.SetRow(cell.GetRow() + rows); + + if ( cols < 0 ) + cell.SetCol(cell.GetCol() + cols); + } + + if ( cell == m_currentCellCoords ) { wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords); DrawCellHighlight(dc, attr); @@ -7763,50 +5216,41 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) } } -// TODO: remove this ??? // This is used to redraw all grid lines e.g. when the grid line colour // has been changed // void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) { -#if !WXGRID_DRAW_LINES - return; -#endif - - if ( !m_gridLinesEnabled || !m_numRows || !m_numCols ) + if ( !m_gridLinesEnabled ) return; int top, bottom, left, right; -#if 0 //#ifndef __WXGTK__ - if (reg.IsEmpty()) + int cw, ch; + m_gridWin->GetClientSize(&cw, &ch); + CalcUnscrolledPosition( 0, 0, &left, &top ); + CalcUnscrolledPosition( cw, ch, &right, &bottom ); + + // avoid drawing grid lines past the last row and col + if ( m_gridLinesClipHorz ) { - int cw, ch; - m_gridWin->GetClientSize(&cw, &ch); + if ( !m_numCols ) + return; - // virtual coords of visible area - // - CalcUnscrolledPosition( 0, 0, &left, &top ); - CalcUnscrolledPosition( cw, ch, &right, &bottom ); + const int lastColRight = GetColRight(GetColAt(m_numCols - 1)); + if ( right > lastColRight ) + right = lastColRight; } - else + + if ( m_gridLinesClipVert ) { - wxCoord x, y, w, h; - reg.GetBox(x, y, w, h); - CalcUnscrolledPosition( x, y, &left, &top ); - CalcUnscrolledPosition( x + w, y + h, &right, &bottom ); - } -#else - int cw, ch; - m_gridWin->GetClientSize(&cw, &ch); - CalcUnscrolledPosition( 0, 0, &left, &top ); - CalcUnscrolledPosition( cw, ch, &right, &bottom ); -#endif + if ( !m_numRows ) + return; - // avoid drawing grid lines past the last row and col - // - right = wxMin( right, GetColRight(GetColAt( m_numCols - 1 )) ); - bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) ); + const int lastRowBottom = GetRowBottom(m_numRows - 1); + if ( bottom > lastRowBottom ) + bottom = lastRowBottom; + } // no gridlines inside multicells, clip them out int leftCol = GetColPos( internalXToCol(left) ); @@ -7816,15 +5260,14 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) wxRegion clippedcells(0, 0, cw, ch); - int i, j, cell_rows, cell_cols; + int cell_rows, cell_cols; wxRect rect; - for (j=topRow; j<=bottomRow; j++) + for ( int j = topRow; j <= bottomRow; j++ ) { - int colPos; - for (colPos=leftCol; colPos<=rightCol; colPos++) + for ( int colPos = leftCol; colPos <= rightCol; colPos++ ) { - i = GetColAt( colPos ); + int i = GetColAt( colPos ); GetCellSize( j, i, &cell_rows, &cell_cols ); if ((cell_rows > 1) || (cell_cols > 1)) @@ -7842,20 +5285,16 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) } } - dc.SetClippingRegion( clippedcells ); + dc.SetDeviceClippingRegion( clippedcells ); // horizontal grid lines - // - // already declared above - int i; - for ( i = internalYToRow(top); i < m_numRows; i++ ) + for ( int i = internalYToRow(top); i < m_numRows; i++ ) { int bot = GetRowBottom(i) - 1; if ( bot > bottom ) - { break; - } if ( bot >= top ) { @@ -7865,11 +5304,9 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) } // vertical grid lines - // - int colPos; - for ( colPos = leftCol; colPos < m_numCols; colPos++ ) + for ( int colPos = leftCol; colPos < m_numCols; colPos++ ) { - i = GetColAt( colPos ); + int i = GetColAt( colPos ); int colRight = GetColRight(i); #ifdef __WXGTK__ @@ -7878,9 +5315,7 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) colRight--; if ( colRight > right ) - { break; - } if ( colRight >= left ) { @@ -7897,10 +5332,8 @@ void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows) if ( !m_numRows ) return; - size_t i; - size_t numLabels = rows.GetCount(); - - for ( i = 0; i < numLabels; i++ ) + const size_t numLabels = rows.GetCount(); + for ( size_t i = 0; i < numLabels; i++ ) { DrawRowLabel( dc, rows[i] ); } @@ -7916,7 +5349,7 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) int rowTop = GetRowTop(row), rowBottom = GetRowBottom(row) - 1; - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); + dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom ); dc.DrawLine( 0, rowTop, 0, rowBottom ); dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom ); @@ -7925,7 +5358,7 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) dc.DrawLine( 1, rowTop, 1, rowBottom ); dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop ); - dc.SetBackgroundMode( wxTRANSPARENT ); + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); dc.SetTextForeground( GetLabelTextColour() ); dc.SetFont( GetLabelFont() ); @@ -7939,16 +5372,35 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); } +void wxGrid::UseNativeColHeader(bool native) +{ + if ( native == m_useNativeHeader ) + return; + + delete m_colWindow; + m_useNativeHeader = native; + + CreateColumnWindow(); + + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + CalcWindowSizes(); +} + void wxGrid::SetUseNativeColLabels( bool native ) { + wxASSERT_MSG( !m_useNativeHeader, + "doesn't make sense when using native header" ); + m_nativeColumnLabels = native; if (native) { int height = wxRendererNative::Get().GetHeaderButtonHeight( this ); SetColLabelSize( height ); } - - m_colLabelWin->Refresh(); + + GetColLabelWindow()->Refresh(); + m_cornerLabelWin->Refresh(); } void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) @@ -7956,64 +5408,93 @@ void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) if ( !m_numCols ) return; - size_t i; - size_t numLabels = cols.GetCount(); - - for ( i = 0; i < numLabels; i++ ) + const size_t numLabels = cols.GetCount(); + for ( size_t i = 0; i < numLabels; i++ ) { DrawColLabel( dc, cols[i] ); } } -void wxGrid::DrawColLabel( wxDC& dc, int col ) +void wxGrid::DrawCornerLabel(wxDC& dc) +{ + if ( m_nativeColumnLabels ) + { + wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight)); + rect.Deflate(1); + + wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0); + } + else + { + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, + m_rowLabelWidth - 1, 0 ); + dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, + 0, m_colLabelHeight - 1 ); + dc.DrawLine( 0, 0, m_rowLabelWidth, 0 ); + dc.DrawLine( 0, 0, 0, m_colLabelHeight ); + + dc.SetPen( *wxWHITE_PEN ); + dc.DrawLine( 1, 1, m_rowLabelWidth - 1, 1 ); + dc.DrawLine( 1, 1, 1, m_colLabelHeight - 1 ); + } +} + +void wxGrid::DrawColLabel(wxDC& dc, int col) { if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 ) return; int colLeft = GetColLeft(col); - wxRect rect; + wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight); - if (m_nativeColumnLabels) + if ( m_nativeColumnLabels ) { - rect.SetX( colLeft); - rect.SetY( 0 ); - rect.SetWidth( GetColWidth(col)); - rect.SetHeight( m_colLabelHeight ); - - wxWindowDC *win_dc = (wxWindowDC*) &dc; - wxRendererNative::Get().DrawHeaderButton( win_dc->GetWindow(), dc, rect, 0 ); + wxRendererNative::Get().DrawHeaderButton + ( + GetColLabelWindow(), + dc, + rect, + 0, + IsSortingBy(col) + ? IsSortOrderAscending() + ? wxHDR_SORT_ICON_UP + : wxHDR_SORT_ICON_DOWN + : wxHDR_SORT_ICON_NONE + ); } else { int colRight = GetColRight(col) - 1; - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); - dc.DrawLine( colRight, 0, colRight, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 0, colRight, 0 ); + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine( colRight, 0, + colRight, m_colLabelHeight - 1 ); + dc.DrawLine( colLeft, 0, + colRight, 0 ); dc.DrawLine( colLeft, m_colLabelHeight - 1, - colRight + 1, m_colLabelHeight - 1 ); + colRight + 1, m_colLabelHeight - 1 ); dc.SetPen( *wxWHITE_PEN ); dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 ); dc.DrawLine( colLeft, 1, colRight, 1 ); } - dc.SetBackgroundMode( wxTRANSPARENT ); + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); dc.SetTextForeground( GetLabelTextColour() ); dc.SetFont( GetLabelFont() ); - int hAlign, vAlign, orient; + int hAlign, vAlign; GetColLabelAlignment( &hAlign, &vAlign ); - orient = GetColLabelTextOrientation(); + const int orient = GetColLabelTextOrientation(); - rect.SetX( colLeft + 2 ); - rect.SetY( 2 ); - rect.SetWidth( GetColWidth(col) - 4 ); - rect.SetHeight( m_colLabelHeight - 4 ); - DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient ); + rect.Deflate(2); + DrawTextRectangle(dc, GetColLabelValue(col), rect, hAlign, vAlign, orient); } +// TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which +// we just have to add textOrientation support void wxGrid::DrawTextRectangle( wxDC& dc, const wxString& value, const wxRect& rect, @@ -8025,17 +5506,9 @@ void wxGrid::DrawTextRectangle( wxDC& dc, StringToLines( value, lines ); - // Forward to new API. - DrawTextRectangle( dc, - lines, - rect, - horizAlign, - vertAlign, - textOrientation ); + DrawTextRectangle(dc, lines, rect, horizAlign, vertAlign, textOrientation); } -// VZ: this should be replaced with wxDC::DrawLabel() to which we just have to -// add textOrientation support void wxGrid::DrawTextRectangle(wxDC& dc, const wxArrayString& lines, const wxRect& rect, @@ -8140,6 +5613,7 @@ void wxGrid::DrawTextRectangle(wxDC& dc, // Split multi-line text up into an array of strings. // Any existing contents of the string array are preserved. // +// TODO: refactor wxTextFile::Read() and reuse the same code from here void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const { int startPos = 0; @@ -8160,15 +5634,15 @@ void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const } else { - lines.Add( value.Mid(startPos, pos) ); + lines.Add( tVal.Mid(startPos, pos) ); } startPos += pos + 1; } - if ( startPos < (int)value.length() ) + if ( startPos < (int)tVal.length() ) { - lines.Add( value.Mid( startPos ) ); + lines.Add( tVal.Mid( startPos ) ); } } @@ -8204,7 +5678,7 @@ void wxGrid::EndBatch() { CalcDimensions(); m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); m_gridWin->Refresh(); } @@ -8238,8 +5712,6 @@ bool wxGrid::Enable(bool enable) void wxGrid::EnableEditing( bool edit ) { - // TODO: improve this ? - // if ( edit != m_editable ) { if (!edit) @@ -8257,7 +5729,7 @@ void wxGrid::EnableCellEditControl( bool enable ) { if ( enable ) { - if (SendEvent( wxEVT_GRID_EDITOR_SHOWN) <0) + if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 ) return; // this should be checked by the caller! @@ -8270,8 +5742,7 @@ void wxGrid::EnableCellEditControl( bool enable ) } else { - //FIXME:add veto support - SendEvent( wxEVT_GRID_EDITOR_HIDDEN ); + SendEvent(wxEVT_GRID_EDITOR_HIDDEN); HideCellEditControl(); SaveEditControlValue(); @@ -8362,7 +5833,7 @@ void wxGrid::ShowCellEditControl() wxClientDC dc( m_gridWin ); PrepareDC( dc ); wxGridCellAttr* attr = GetCellAttr(row, col); - dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); + dc.SetBrush(wxBrush(attr->GetBackgroundColour())); dc.SetPen(*wxTRANSPARENT_PEN); dc.DrawRectangle(rect); @@ -8434,7 +5905,7 @@ void wxGrid::ShowCellEditControl() if (rect.GetRight() > client_right) rect.SetRight( client_right - 1 ); } - + editor->SetCellAttr( attr ); editor->SetSize( rect ); if (nXMove != 0) @@ -8465,11 +5936,18 @@ void wxGrid::HideCellEditControl() wxGridCellAttr *attr = GetCellAttr(row, col); wxGridCellEditor *editor = attr->GetEditor(this, row, col); + const bool editorHadFocus = editor->GetControl()->HasFocus(); editor->Show( false ); editor->DecRef(); attr->DecRef(); - m_gridWin->SetFocus(); + // return the focus to the grid itself if the editor had it + // + // note that we must not do this unconditionally to avoid stealing + // focus from the window which just received it if we are hiding the + // editor precisely because we lost focus + if ( editorHadFocus ) + m_gridWin->SetFocus(); // refresh whole row to the right wxRect rect( CellToRect(row, col) ); @@ -8496,21 +5974,26 @@ void wxGrid::SaveEditControlValue() wxGridCellAttr* attr = GetCellAttr(row, col); wxGridCellEditor* editor = attr->GetEditor(this, row, col); - bool changed = editor->EndEdit(row, col, this); - editor->DecRef(); - attr->DecRef(); + wxString newval; + bool changed = editor->EndEdit(oldval, &newval); - if (changed) + if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 ) { - if ( SendEvent( wxEVT_GRID_CELL_CHANGE, - m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol() ) < 0 ) + editor->ApplyEdit(row, col, this); + + // for compatibility reasons dating back to wx 2.8 when this event + // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING + // didn't exist we allow vetoing this one too + if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 ) { // Event has been vetoed, set the data back. SetCellValue(row, col, oldval); } } + + editor->DecRef(); + attr->DecRef(); } } @@ -8521,203 +6004,168 @@ void wxGrid::SaveEditControlValue() // coordinates for mouse events etc. // -void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) const +wxGridCellCoords wxGrid::XYToCell(int x, int y) const { int row = YToRow(y); int col = XToCol(x); - if ( row == -1 || col == -1 ) - { - coords = wxGridNoCellCoords; - } - else - { - coords.Set( row, col ); - } + return row == -1 || col == -1 ? wxGridNoCellCoords + : wxGridCellCoords(row, col); } -// Internal Helper function for computing row or column from some -// (unscrolled) coordinate value, using either -// m_defaultRowHeight/m_defaultColWidth or binary search on array -// of m_rowBottoms/m_ColRights to speed up the search! - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax) +// compute row or column from some (unscrolled) coordinate value, using either +// m_defaultRowHeight/m_defaultColWidth or binary search on array of +// m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used +// for large grids) +int wxGrid::PosToLinePos(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { - if (coord < 0) - return clipToMinMax && (nMax > 0) ? 0 : -1; + const int numLines = oper.GetNumberOfLines(this); + + if ( coord < 0 ) + return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND; - if (!defaultDist) - defaultDist = 1; + const int defaultLineSize = oper.GetDefaultLineSize(this); + wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); - size_t i_max = coord / defaultDist, - i_min = 0; + int maxPos = coord / defaultLineSize, + minPos = 0; - if (BorderArray.IsEmpty()) + // check for the simplest case: if we have no explicit line sizes + // configured, then we already know the line this position falls in + const wxArrayInt& lineEnds = oper.GetLineEnds(this); + if ( lineEnds.empty() ) { - if ((int) i_max < nMax) - return i_max; - return clipToMinMax ? nMax - 1 : -1; + if ( maxPos < numLines ) + return maxPos; + + return clipToMinMax ? numLines - 1 : -1; } - if ( i_max >= BorderArray.GetCount()) + + // adjust maxPos before starting the binary search + if ( maxPos >= numLines ) { - i_max = BorderArray.GetCount() - 1; + maxPos = numLines - 1; } else { - if ( coord >= BorderArray[i_max]) + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)]) { - i_min = i_max; - if (minDist) - i_max = coord / minDist; + minPos = maxPos; + const int minDist = oper.GetMinimalAcceptableLineSize(this); + if ( minDist ) + maxPos = coord / minDist; else - i_max = BorderArray.GetCount() - 1; + maxPos = numLines - 1; } - if ( i_max >= BorderArray.GetCount()) - i_max = BorderArray.GetCount() - 1; + if ( maxPos >= numLines ) + maxPos = numLines - 1; } - if ( coord >= BorderArray[i_max]) - return clipToMinMax ? (int)i_max : -1; - if ( coord < BorderArray[0] ) + // check if the position is beyond the last column + const int lineAtMaxPos = oper.GetLineAt(this, maxPos); + if ( coord >= lineEnds[lineAtMaxPos] ) + return clipToMinMax ? maxPos : -1; + + // or before the first one + const int lineAt0 = oper.GetLineAt(this, 0); + if ( coord < lineEnds[lineAt0] ) return 0; - while ( i_max - i_min > 0 ) + + // finally do perform the binary search + while ( minPos < maxPos ) { - wxCHECK_MSG(BorderArray[i_min] <= coord && coord < BorderArray[i_max], - 0, _T("wxGrid: internal error in CoordToRowOrCol")); - if (coord >= BorderArray[ i_max - 1]) - return i_max; + wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord && + coord < lineEnds[oper.GetLineAt(this, maxPos)], + -1, + "wxGrid: internal error in PosToLinePos()" ); + + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] ) + return maxPos; else - i_max--; - int median = i_min + (i_max - i_min + 1) / 2; - if (coord < BorderArray[median]) - i_max = median; + maxPos--; + + const int median = minPos + (maxPos - minPos + 1) / 2; + if ( coord < lineEnds[oper.GetLineAt(this, median)] ) + maxPos = median; else - i_min = median; + minPos = median; } - return i_max; + return maxPos; } -int wxGrid::YToRow( int y ) const +int +wxGrid::PosToLine(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { - return CoordToRowOrCol(y, m_defaultRowHeight, - m_minAcceptableRowHeight, m_rowBottoms, m_numRows, false); + int pos = PosToLinePos(coord, clipToMinMax, oper); + + return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos); } -int wxGrid::XToCol( int x, bool clipToMinMax ) const +int wxGrid::YToRow(int y, bool clipToMinMax) const { - if (x < 0) - return clipToMinMax && (m_numCols > 0) ? GetColAt( 0 ) : -1; - - wxASSERT_MSG(m_defaultColWidth > 0, wxT("Default column width can not be zero")); - - int maxPos = x / m_defaultColWidth; - int minPos = 0; - - if (m_colRights.IsEmpty()) - { - if(maxPos < m_numCols) - return GetColAt( maxPos ); - return clipToMinMax ? GetColAt( m_numCols - 1 ) : -1; - } - - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; - else - { - if ( x >= m_colRights[GetColAt( maxPos )]) - { - minPos = maxPos; - if (m_minAcceptableColWidth) - maxPos = x / m_minAcceptableColWidth; - else - maxPos = m_numCols - 1; - } - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; - } - - //X is beyond the last column - if ( x >= m_colRights[GetColAt( maxPos )]) - return clipToMinMax ? GetColAt( maxPos ) : -1; - - //X is before the first column - if ( x < m_colRights[GetColAt( 0 )] ) - return GetColAt( 0 ); + return PosToLine(y, clipToMinMax, wxGridRowOperations()); +} - //Perform a binary search - while ( maxPos - minPos > 0 ) - { - wxCHECK_MSG(m_colRights[GetColAt( minPos )] <= x && x < m_colRights[GetColAt( maxPos )], - 0, _T("wxGrid: internal error in XToCol")); +int wxGrid::XToCol(int x, bool clipToMinMax) const +{ + return PosToLine(x, clipToMinMax, wxGridColumnOperations()); +} - if (x >= m_colRights[GetColAt( maxPos - 1 )]) - return GetColAt( maxPos ); - else - maxPos--; - int median = minPos + (maxPos - minPos + 1) / 2; - if (x < m_colRights[GetColAt( median )]) - maxPos = median; - else - minPos = median; - } - return GetColAt( maxPos ); +int wxGrid::XToPos(int x) const +{ + return PosToLinePos(x, true /* clip */, wxGridColumnOperations()); } -// return the row number that that the y coord is near -// the edge of, or -1 if not near an edge. +// return the row number that that the y coord is near the edge of, or -1 if +// not near an edge. +// // coords can only possibly be near an edge if // (a) the row/column is large enough to still allow for an "inner" area -// that is _not_ nead the edge (i.e., if the height/width is smaller +// that is _not_ near the edge (i.e., if the height/width is smaller // than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be // near the edge). // and // (b) resizing rows/columns (the thing for which edge detection is // relevant at all) is enabled. // -int wxGrid::YToEdgeOfRow( int y ) const +int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { - int i; - i = internalYToRow(y); + if ( !oper.CanResizeLines(this) ) + return -1; - if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE && CanDragRowSize() ) + const int line = oper.PosToLine(this, pos, true); + + if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE ) { - // We know that we are in row i, test whether we are - // close enough to lower or upper border, respectively. - if ( abs(GetRowBottom(i) - y) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && y - GetRowTop(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; + // We know that we are in this line, test whether we are close enough + // to start or end border, respectively. + if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE ) + return line; + else if ( line > 0 && + pos - oper.GetLineStartPos(this, + line) < WXGRID_LABEL_EDGE_ZONE ) + return line - 1; } return -1; } -// return the col number that that the x coord is near the edge of, or -// -1 if not near an edge -// See comment at YToEdgeOfRow for conditions on edge detection. -// -int wxGrid::XToEdgeOfCol( int x ) const +int wxGrid::YToEdgeOfRow(int y) const { - int i; - i = internalXToCol(x); - - if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE && CanDragColSize() ) - { - // We know that we are in column i; test whether we are - // close enough to right or left border, respectively. - if ( abs(GetColRight(i) - x) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && x - GetColLeft(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; - } + return PosToEdgeOfLine(y, wxGridRowOperations()); +} - return -1; +int wxGrid::XToEdgeOfCol(int x) const +{ + return PosToEdgeOfLine(x, wxGridColumnOperations()); } wxRect wxGrid::CellToRect( int row, int col ) const @@ -8870,463 +6318,197 @@ void wxGrid::MakeCellVisible( int row, int col ) // ------ Grid cursor movement functions // -bool wxGrid::MoveCursorUp( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() >= 0 ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() > 0 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow() - 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MoveCursorDown( bool expandSelection ) +bool +wxGrid::DoMoveCursor(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() < m_numRows - 1 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() < m_numRows - 1 ) - { - int row = m_currentCellCoords.GetRow() + 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; -bool wxGrid::MoveCursorLeft( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() >= 0 ) + if ( expandSelection ) { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() > 0 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) - 1 ); - ClearSelection(); + wxGridCellCoords coords = m_selectedBlockCorner; + if ( coords == wxGridNoCellCoords ) + coords = m_currentCellCoords; - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else + if ( diroper.IsAtBoundary(coords) ) return false; - return true; - } - - return false; -} + diroper.Advance(coords); -bool wxGrid::MoveCursorRight( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols ) + UpdateBlockBeingSelected(m_currentCellCoords, coords); + } + else // don't expand selection { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() < m_numCols - 1 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) + 1 ); - ClearSelection(); + ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else + if ( diroper.IsAtBoundary(m_currentCellCoords) ) return false; - return true; + wxGridCellCoords coords = m_currentCellCoords; + diroper.Advance(coords); + + GoToCell(coords); } - return false; + return true; } -bool wxGrid::MovePageUp() +bool wxGrid::MoveCursorUp(bool expandSelection) { - if ( m_currentCellCoords == wxGridNoCellCoords ) - return false; - - int row = m_currentCellCoords.GetRow(); - if ( row > 0 ) - { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - - int y = GetRowTop(row); - int newRow = internalYToRow( y - ch + 1 ); - - if ( newRow == row ) - { - // row > 0, so newRow can never be less than 0 here. - newRow = row - 1; - } + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations())); +} - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); +bool wxGrid::MoveCursorDown(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridRowOperations())); +} - return true; - } +bool wxGrid::MoveCursorLeft(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations())); +} - return false; +bool wxGrid::MoveCursorRight(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations())); } -bool wxGrid::MovePageDown() +bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper) { if ( m_currentCellCoords == wxGridNoCellCoords ) return false; - int row = m_currentCellCoords.GetRow(); - if ( (row + 1) < m_numRows ) + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; + + const int oldRow = m_currentCellCoords.GetRow(); + int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y); + if ( newRow == oldRow ) { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + wxGridCellCoords coords(m_currentCellCoords); + diroper.Advance(coords); + newRow = coords.GetRow(); + } - int y = GetRowTop(row); - int newRow = internalYToRow( y + ch ); - if ( newRow == row ) - { - // row < m_numRows, so newRow can't overflow here. - newRow = row + 1; - } + GoToCell(newRow, m_currentCellCoords.GetCol()); - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); + return true; +} - return true; - } +bool wxGrid::MovePageUp() +{ + return DoMoveCursorByPage( + wxGridBackwardOperations(this, wxGridRowOperations())); +} - return false; +bool wxGrid::MovePageDown() +{ + return DoMoveCursorByPage( + wxGridForwardOperations(this, wxGridRowOperations())); } -bool wxGrid::MoveCursorUpBlock( bool expandSelection ) +// helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper +// until we find a non-empty cell or reach the grid end +void +wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords, + const wxGridDirectionOperations& diroper) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() > 0 ) + while ( !diroper.IsAtBoundary(coords) ) { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( row > 0 ) - { - row--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row - 1, col) ) - { - // starting at the top of a block: find the next block - // - row--; - while ( row > 0 ) - { - row--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the top of the block - // - while ( row > 0 ) - { - row--; - if ( m_table->IsEmptyCell(row, col) ) - { - row++; - break; - } - } - } - - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } - - return true; + diroper.Advance(coords); + if ( !m_table->IsEmpty(coords) ) + break; } - - return false; } -bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +bool +wxGrid::DoMoveCursorByBlock(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( row < m_numRows - 1 ) - { - row++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row + 1, col) ) - { - // starting at the bottom of a block: find the next block - // - row++; - while ( row < m_numRows - 1 ) - { - row++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the bottom of the block - // - while ( row < m_numRows - 1 ) - { - row++; - if ( m_table->IsEmptyCell(row, col) ) - { - row--; - break; - } - } - } + if ( !m_table || m_currentCellCoords == wxGridNoCellCoords ) + return false; - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; - return true; + wxGridCellCoords coords(m_currentCellCoords); + if ( m_table->IsEmpty(coords) ) + { + // we are in an empty cell: find the next block of non-empty cells + AdvanceToNextNonEmpty(coords, diroper); } - - return false; -} - -bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) -{ - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() > 0 ) + else // current cell is not empty { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row, col - 1) ) + diroper.Advance(coords); + if ( m_table->IsEmpty(coords) ) { - // starting at the left of a block: find the next block - // - col--; - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } + // we started at the end of a block, find the next one + AdvanceToNextNonEmpty(coords, diroper); } - else + else // we're in a middle of a block { - // starting within a block: find the left of the block - // - while ( col > 0 ) + // go to the end of it, i.e. find the last cell before the next + // empty one + while ( !diroper.IsAtBoundary(coords) ) { - col--; - if ( m_table->IsEmptyCell(row, col) ) - { - col++; + wxGridCellCoords coordsNext(coords); + diroper.Advance(coordsNext); + if ( m_table->IsEmpty(coordsNext) ) break; - } - } - } - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); + coords = coordsNext; + } } + } - return true; + if ( expandSelection ) + { + UpdateBlockBeingSelected(m_currentCellCoords, coords); + } + else + { + ClearSelection(); + GoToCell(coords); } - return false; + return true; } -bool wxGrid::MoveCursorRightBlock( bool expandSelection ) +bool wxGrid::MoveCursorUpBlock(bool expandSelection) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row, col + 1) ) - { - // starting at the right of a block: find the next block - // - col++; - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the right of the block - // - while ( col < m_numCols - 1 ) - { - col++; - if ( m_table->IsEmptyCell(row, col) ) - { - col--; - break; - } - } - } + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations()) + ); +} - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } +bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridRowOperations()) + ); +} - return true; - } +bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations()) + ); +} - return false; +bool wxGrid::MoveCursorRightBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations()) + ); } // @@ -9424,12 +6606,12 @@ void wxGrid::SetColLabelSize( int height ) { if ( height == 0 ) { - m_colLabelWin->Show( false ); + m_colWindow->Show( false ); m_cornerLabelWin->Show( false ); } else if ( m_colLabelHeight == 0 ) { - m_colLabelWin->Show( true ); + m_colWindow->Show( true ); if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( true ); } @@ -9446,13 +6628,13 @@ void wxGrid::SetLabelBackgroundColour( const wxColour& colour ) { m_labelBackgroundColour = colour; m_rowLabelWin->SetBackgroundColour( colour ); - m_colLabelWin->SetBackgroundColour( colour ); + m_colWindow->SetBackgroundColour( colour ); m_cornerLabelWin->SetBackgroundColour( colour ); if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); } } @@ -9466,7 +6648,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } } @@ -9477,7 +6659,7 @@ void wxGrid::SetLabelFont( const wxFont& font ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9543,7 +6725,7 @@ void wxGrid::SetColLabelAlignment( int horiz, int vert ) if ( !GetBatchCount() ) { - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9560,7 +6742,7 @@ void wxGrid::SetColLabelTextOrientation( int textOrientation ) m_colLabelTextOrientation = textOrientation; if ( !GetBatchCount() ) - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } void wxGrid::SetRowLabelValue( int row, const wxString& s ) @@ -9589,13 +6771,20 @@ void wxGrid::SetColLabelValue( int col, const wxString& s ) m_table->SetColLabelValue( col, s ); if ( !GetBatchCount() ) { - wxRect rect = CellToRect( 0, col ); - if ( rect.width > 0 ) + if ( m_useNativeHeader ) { - CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y); - rect.y = 0; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); + GetGridColHeader()->UpdateColumn(col); + } + else + { + wxRect rect = CellToRect( 0, col ); + if ( rect.width > 0 ) + { + CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y); + rect.y = 0; + rect.height = m_colLabelHeight; + GetColLabelWindow()->Refresh( true, &rect ); + } } } } @@ -9607,9 +6796,8 @@ void wxGrid::SetGridLineColour( const wxColour& colour ) { m_gridLineColour = colour; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9655,7 +6843,8 @@ void wxGrid::SetCellHighlightROPenWidth(int width) // make any visible change if the the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); - if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) + if ( row == -1 || col == -1 || + GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) return; wxRect rect = CellToRect(row, col); @@ -9663,25 +6852,42 @@ void wxGrid::SetCellHighlightROPenWidth(int width) } } +void wxGrid::RedrawGridLines() +{ + // the lines will be redrawn when the window is thawn + if ( GetBatchCount() ) + return; + + if ( GridLinesEnabled() ) + { + wxClientDC dc( m_gridWin ); + PrepareDC( dc ); + DrawAllGridLines( dc, wxRegion() ); + } + else // remove the grid lines + { + m_gridWin->Refresh(); + } +} + void wxGrid::EnableGridLines( bool enable ) { if ( enable != m_gridLinesEnabled ) { m_gridLinesEnabled = enable; - if ( !GetBatchCount() ) - { - if ( enable ) - { - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); - } - else - { - m_gridWin->Refresh(); - } - } + RedrawGridLines(); + } +} + +void wxGrid::DoClipGridLines(bool& var, bool clip) +{ + if ( clip != var ) + { + var = clip; + + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9903,9 +7109,14 @@ void wxGrid::ClearAttrCache() { if ( m_attrCache.row != -1 ) { - wxSafeDecRef(m_attrCache.attr); + wxGridCellAttr *oldAttr = m_attrCache.attr; m_attrCache.attr = NULL; m_attrCache.row = -1; + // wxSafeDecRec(...) might cause event processing that accesses + // the cached attribute, if one exists (e.g. by deleting the + // editor stored within the attribute). Therefore it is important + // to invalidate the cache before calling wxSafeDecRef! + wxSafeDecRef(oldAttr); } } @@ -9956,7 +7167,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const if ( !LookupAttr(row, col, &attr) ) { attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any) - : (wxGridCellAttr *)NULL; + : NULL; CacheAttr(row, col, attr); } } @@ -9976,7 +7187,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; bool canHave = ((wxGrid*)this)->CanHaveAttributes(); wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed")); @@ -10027,6 +7238,8 @@ void wxGrid::SetColFormatCustom(int col, const wxString& typeName) attr = new wxGridCellAttr; wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName); attr->SetRenderer(renderer); + wxGridCellEditor *editor = GetDefaultEditorForType(typeName); + attr->SetEditor(editor); SetColAttr(col, attr); @@ -10146,7 +7359,7 @@ void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols ) wxT("wxGrid::SetCellSize setting cell size to < 1")); // if this was already a multicell then "turn off" the other cells first - if ((cell_rows > 1) || (cell_rows > 1)) + if ((cell_rows > 1) || (cell_cols > 1)) { int i, j; for (j=row; j < row + cell_rows; j++) @@ -10309,6 +7522,19 @@ void wxGrid::SetRowSize( int row, int height ) { wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") ); + // if < 0 then calculate new height from label + if ( height < 0 ) + { + long w, h; + wxArrayString lines; + wxClientDC dc(m_rowLabelWin); + dc.SetFont(GetLabelFont()); + StringToLines(GetRowLabelValue( row ), lines); + GetTextBoxSize( dc, lines, &w, &h ); + //check that it is not less than the minimal height + height = wxMax(h, GetRowMinimalAcceptableHeight()); + } + // See comment in SetColSize if ( height < GetRowMinimalAcceptableHeight()) return; @@ -10354,38 +7580,42 @@ void wxGrid::SetColSize( int col, int width ) { wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") ); - // should we check that it's bigger than GetColMinimalWidth(col) here? - // (VZ) - // No, because it is reasonable to assume the library user know's - // what he is doing. However we should test against the weaker - // constraint of minimalAcceptableWidth, as this breaks rendering - // - // This test then fixes sf.net bug #645734 - - if ( width < GetColMinimalAcceptableWidth() ) - return; - - if ( m_colWidths.IsEmpty() ) - { - // need to really create the array - InitColWidths(); - } - // if < 0 then calculate new width from label if ( width < 0 ) { long w, h; wxArrayString lines; - wxClientDC dc(m_colLabelWin); + wxClientDC dc(m_colWindow); dc.SetFont(GetLabelFont()); StringToLines(GetColLabelValue(col), lines); - GetTextBoxSize(dc, lines, &w, &h); + if ( GetColLabelTextOrientation() == wxHORIZONTAL ) + GetTextBoxSize( dc, lines, &w, &h ); + else + GetTextBoxSize( dc, lines, &h, &w ); width = w + 6; + //check that it is not less than the minimal width + width = wxMax(width, GetColMinimalAcceptableWidth()); + } + + // we intentionally don't test whether the width is less than + // GetColMinimalWidth() here but we do compare it with + // GetColMinimalAcceptableWidth() as otherwise things currently break (see + // #651) -- and we also always allow the width of 0 as it has the special + // sense of hiding the column + if ( width > 0 && width < GetColMinimalAcceptableWidth() ) + return; + + if ( m_colWidths.IsEmpty() ) + { + // need to really create the array + InitColWidths(); } - int w = wxMax( 0, width ); - int diff = w - m_colWidths[col]; - m_colWidths[col] = w; + const int diff = width - m_colWidths[col]; + m_colWidths[col] = width; + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + //else: will be refreshed when the header is redrawn for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) { @@ -10393,7 +7623,10 @@ void wxGrid::SetColSize( int col, int width ) } if ( !GetBatchCount() ) + { CalcDimensions(); + Refresh(); + } } void wxGrid::SetColMinimalWidth( int col, int width ) @@ -10545,15 +7778,22 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) SetColSize( col, extentMax ); if ( !GetBatchCount() ) - { - int cw, ch, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - wxRect rect ( CellToRect( 0, col ) ); - rect.y = 0; - CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); - rect.width = cw - rect.x; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); + { + if ( m_useNativeHeader ) + { + GetGridColHeader()->UpdateColumn(col); + } + else + { + int cw, ch, dummy; + m_gridWin->GetClientSize( &cw, &ch ); + wxRect rect ( CellToRect( 0, col ) ); + rect.y = 0; + CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); + rect.width = cw - rect.x; + rect.height = m_colLabelHeight; + GetColLabelWindow()->Refresh( true, &rect ); + } } } else @@ -10682,77 +7922,22 @@ void wxGrid::AutoSize() { wxGridUpdateLocker locker(this); - // we need to round up the size of the scrollable area to a multiple of - // scroll step to ensure that we don't get the scrollbars when we're sized - // exactly to fit our contents wxSize size(SetOrCalcColumnSizes(false) - m_rowLabelWidth + m_extraWidth, SetOrCalcRowSizes(false) - m_colLabelHeight + m_extraHeight); - wxSize sizeFit(GetScrollX(size.x) * GetScrollLineX(), - GetScrollY(size.y) * GetScrollLineY()); - - // distribute the extra space between the columns/rows to avoid having - // extra white space - wxCoord diff = sizeFit.x - size.x; - if ( diff && m_numCols ) - { - // try to resize the columns uniformly - wxCoord diffPerCol = diff / m_numCols; - if ( diffPerCol ) - { - for ( int col = 0; col < m_numCols; col++ ) - { - SetColSize(col, GetColWidth(col) + diffPerCol); - } - } - - // add remaining amount to the last columns - diff -= diffPerCol * m_numCols; - if ( diff ) - { - for ( int col = m_numCols - 1; col >= m_numCols - diff; col-- ) - { - SetColSize(col, GetColWidth(col) + 1); - } - } - } - - // same for rows - diff = sizeFit.y - size.y; - if ( diff && m_numRows ) - { - // try to resize the columns uniformly - wxCoord diffPerRow = diff / m_numRows; - if ( diffPerRow ) - { - for ( int row = 0; row < m_numRows; row++ ) - { - SetRowSize(row, GetRowHeight(row) + diffPerRow); - } - } - - // add remaining amount to the last rows - diff -= diffPerRow * m_numRows; - if ( diff ) - { - for ( int row = m_numRows - 1; row >= m_numRows - diff; row-- ) - { - SetRowSize(row, GetRowHeight(row) + 1); - } - } - } // we know that we're not going to have scrollbars so disable them now to // avoid trouble in SetClientSize() which can otherwise set the correct // client size but also leave space for (not needed any more) scrollbars SetScrollbars(0, 0, 0, 0, 0, 0, true); - SetClientSize(sizeFit.x + m_rowLabelWidth, sizeFit.y + m_colLabelHeight); + + // restore the scroll rate parameters overwritten by SetScrollbars() + SetScrollRate(m_scrollLineX, m_scrollLineY); + + SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight); } void wxGrid::AutoSizeRowLabelSize( int row ) { - wxArrayString lines; - long w, h; - // Hide the edit control, so it // won't interfere with drag-shrinking. if ( IsCellEditControlShown() ) @@ -10762,20 +7947,12 @@ void wxGrid::AutoSizeRowLabelSize( int row ) } // autosize row height depending on label text - StringToLines( GetRowLabelValue( row ), lines ); - wxClientDC dc( m_rowLabelWin ); - GetTextBoxSize( dc, lines, &w, &h ); - if ( h < m_defaultRowHeight ) - h = m_defaultRowHeight; - SetRowSize(row, h); + SetRowSize(row, -1); ForceRefresh(); } void wxGrid::AutoSizeColLabelSize( int col ) { - wxArrayString lines; - long w, h; - // Hide the edit control, so it // won't interfere with drag-shrinking. if ( IsCellEditControlShown() ) @@ -10785,15 +7962,7 @@ void wxGrid::AutoSizeColLabelSize( int col ) } // autosize column width depending on label text - StringToLines( GetColLabelValue( col ), lines ); - wxClientDC dc( m_colLabelWin ); - if ( GetColLabelTextOrientation() == wxHORIZONTAL ) - GetTextBoxSize( dc, lines, &w, &h ); - else - GetTextBoxSize( dc, lines, &h, &w ); - if ( w < m_defaultColWidth ) - w = m_defaultColWidth; - SetColSize(col, w); + SetColSize(col, -1); ForceRefresh(); } @@ -10805,15 +7974,13 @@ wxSize wxGrid::DoGetBestSize() const // change the column/row sizes, only calculate them wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth, self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight); - wxSize sizeFit(GetScrollX(size.x) * GetScrollLineX(), - GetScrollY(size.y) * GetScrollLineY()); // NOTE: This size should be cached, but first we need to add calls to // InvalidateBestSize everywhere that could change the results of this // calculation. // CacheBestSize(size); - return wxSize(sizeFit.x + m_rowLabelWidth, sizeFit.y + m_colLabelHeight) + return wxSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight) + GetWindowBorderSize(); } @@ -10865,31 +8032,36 @@ void wxGrid::SetCellValue( int row, int col, const wxString& s ) void wxGrid::SelectRow( int row, bool addToSelected ) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectRow( row, false, addToSelected ); + m_selection->SelectRow(row); } void wxGrid::SelectCol( int col, bool addToSelected ) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectCol( col, false, addToSelected ); + m_selection->SelectCol(col); } -void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol, - bool addToSelected ) +void wxGrid::SelectBlock(int topRow, int leftCol, int bottomRow, int rightCol, + bool addToSelected) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectBlock( topRow, leftCol, bottomRow, rightCol, - false, addToSelected ); + m_selection->SelectBlock(topRow, leftCol, bottomRow, rightCol); } void wxGrid::SelectAll() @@ -10905,46 +8077,40 @@ void wxGrid::SelectAll() // cell, row and col deselection // ---------------------------------------------------------------------------- -void wxGrid::DeselectRow( int row ) +void wxGrid::DeselectLine(int line, const wxGridOperations& oper) { if ( !m_selection ) return; - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows ) + const wxGridSelectionModes mode = m_selection->GetSelectionMode(); + if ( mode == oper.GetSelectionMode() ) { - if ( m_selection->IsInSelection(row, 0 ) ) - m_selection->ToggleCellSelection(row, 0); + const wxGridCellCoords c(oper.MakeCoords(line, 0)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } - else + else if ( mode != oper.Dual().GetSelectionMode() ) { - int nCols = GetNumberCols(); - for ( int i = 0; i < nCols; i++ ) + const int nOther = oper.Dual().GetNumberOfLines(this); + for ( int i = 0; i < nOther; i++ ) { - if ( m_selection->IsInSelection(row, i ) ) - m_selection->ToggleCellSelection(row, i); + const wxGridCellCoords c(oper.MakeCoords(line, i)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } } + //else: can only select orthogonal lines so no lines in this direction + // could have been selected anyhow } -void wxGrid::DeselectCol( int col ) +void wxGrid::DeselectRow(int row) { - if ( !m_selection ) - return; + DeselectLine(row, wxGridRowOperations()); +} - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns ) - { - if ( m_selection->IsInSelection(0, col ) ) - m_selection->ToggleCellSelection(0, col); - } - else - { - int nRows = GetNumberRows(); - for ( int i = 0; i < nRows; i++ ) - { - if ( m_selection->IsInSelection(i, col ) ) - m_selection->ToggleCellSelection(i, col); - } - } +void wxGrid::DeselectCol(int col) +{ + DeselectLine(col, wxGridColumnOperations()); } void wxGrid::DeselectCell( int row, int col ) @@ -10956,17 +8122,17 @@ void wxGrid::DeselectCell( int row, int col ) bool wxGrid::IsSelection() const { return ( m_selection && (m_selection->IsSelection() || - ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords) ) ); + ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords) ) ); } bool wxGrid::IsInSelection( int row, int col ) const { return ( m_selection && (m_selection->IsInSelection( row, col ) || - ( row >= m_selectingTopLeft.GetRow() && - col >= m_selectingTopLeft.GetCol() && - row <= m_selectingBottomRight.GetRow() && - col <= m_selectingBottomRight.GetCol() )) ); + ( row >= m_selectedBlockTopLeft.GetRow() && + col >= m_selectedBlockTopLeft.GetCol() && + row <= m_selectedBlockBottomRight.GetRow() && + col <= m_selectedBlockBottomRight.GetCol() )) ); } wxGridCellCoordsArray wxGrid::GetSelectedCells() const @@ -11026,9 +8192,18 @@ wxArrayInt wxGrid::GetSelectedCols() const void wxGrid::ClearSelection() { - m_selectingTopLeft = - m_selectingBottomRight = - m_selectingKeyboard = wxGridNoCellCoords; + wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft, + m_selectedBlockBottomRight); + wxRect r2 = BlockToDeviceRect(m_currentCellCoords, + m_selectedBlockCorner); + + m_selectedBlockTopLeft = + m_selectedBlockBottomRight = + m_selectedBlockCorner = wxGridNoCellCoords; + + Refresh( false, &r1 ); + Refresh( false, &r2 ); + if ( m_selection ) m_selection->ClearSelection(); } @@ -11036,37 +8211,36 @@ void wxGrid::ClearSelection() // This function returns the rectangle that encloses the given block // in device coords clipped to the client size of the grid window. // -wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft, - const wxGridCellCoords &bottomRight ) const +wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight ) const { - wxRect rect( wxGridNoCellRect ); - wxRect cellRect; - - cellRect = CellToRect( topLeft ); - if ( cellRect != wxGridNoCellRect ) + wxRect resultRect; + wxRect tempCellRect = CellToRect(topLeft); + if ( tempCellRect != wxGridNoCellRect ) { - rect = cellRect; + resultRect = tempCellRect; } else { - rect = wxRect(0, 0, 0, 0); + resultRect = wxRect(0, 0, 0, 0); } - cellRect = CellToRect( bottomRight ); - if ( cellRect != wxGridNoCellRect ) + tempCellRect = CellToRect(bottomRight); + if ( tempCellRect != wxGridNoCellRect ) { - rect += cellRect; + resultRect += tempCellRect; } else { + // If both inputs were "wxGridNoCellRect," then there's nothing to do. return wxGridNoCellRect; } - int i, j; - int left = rect.GetLeft(); - int top = rect.GetTop(); - int right = rect.GetRight(); - int bottom = rect.GetBottom(); + // Ensure that left/right and top/bottom pairs are in order. + int left = resultRect.GetLeft(); + int top = resultRect.GetTop(); + int right = resultRect.GetRight(); + int bottom = resultRect.GetBottom(); int leftCol = topLeft.GetCol(); int topRow = topLeft.GetRow(); @@ -11075,67 +8249,105 @@ wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft, if (left > right) { - i = left; + int tmp = left; left = right; - right = i; - i = leftCol; + right = tmp; + + tmp = leftCol; leftCol = rightCol; - rightCol = i; + rightCol = tmp; } if (top > bottom) { - i = top; + int tmp = top; top = bottom; - bottom = i; - i = topRow; + bottom = tmp; + + tmp = topRow; topRow = bottomRow; - bottomRow = i; + bottomRow = tmp; } - for ( j = topRow; j <= bottomRow; j++ ) + // The following loop is ONLY necessary to detect and handle merged cells. + int cw, ch; + m_gridWin->GetClientSize( &cw, &ch ); + + // Get the origin coordinates: notice that they will be negative if the + // grid is scrolled downwards/to the right. + int gridOriginX = 0; + int gridOriginY = 0; + CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY); + + int onScreenLeftmostCol = internalXToCol(-gridOriginX); + int onScreenUppermostRow = internalYToRow(-gridOriginY); + + int onScreenRightmostCol = internalXToCol(-gridOriginX + cw); + int onScreenBottommostRow = internalYToRow(-gridOriginY + ch); + + // Bound our loop so that we only examine the portion of the selected block + // that is shown on screen. Therefore, we compare the Top-Left block values + // to the Top-Left screen values, and the Bottom-Right block values to the + // Bottom-Right screen values, choosing appropriately. + const int visibleTopRow = wxMax(topRow, onScreenUppermostRow); + const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow); + const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol); + const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol); + + for ( int j = visibleTopRow; j <= visibleBottomRow; j++ ) { - for ( i = leftCol; i <= rightCol; i++ ) + for ( int i = visibleLeftCol; i <= visibleRightCol; i++ ) { - if ((j == topRow) || (j == bottomRow) || (i == leftCol) || (i == rightCol)) + if ( (j == visibleTopRow) || (j == visibleBottomRow) || + (i == visibleLeftCol) || (i == visibleRightCol) ) { - cellRect = CellToRect( j, i ); - - if (cellRect.x < left) - left = cellRect.x; - if (cellRect.y < top) - top = cellRect.y; - if (cellRect.x + cellRect.width > right) - right = cellRect.x + cellRect.width; - if (cellRect.y + cellRect.height > bottom) - bottom = cellRect.y + cellRect.height; + tempCellRect = CellToRect( j, i ); + + if (tempCellRect.x < left) + left = tempCellRect.x; + if (tempCellRect.y < top) + top = tempCellRect.y; + if (tempCellRect.x + tempCellRect.width > right) + right = tempCellRect.x + tempCellRect.width; + if (tempCellRect.y + tempCellRect.height > bottom) + bottom = tempCellRect.y + tempCellRect.height; } else { - i = rightCol; // jump over inner cells. + i = visibleRightCol; // jump over inner cells. } } } - // convert to scrolled coords - // + // Convert to scrolled coords CalcScrolledPosition( left, top, &left, &top ); CalcScrolledPosition( right, bottom, &right, &bottom ); - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - if (right < 0 || bottom < 0 || left > cw || top > ch) return wxRect(0,0,0,0); - rect.SetLeft( wxMax(0, left) ); - rect.SetTop( wxMax(0, top) ); - rect.SetRight( wxMin(cw, right) ); - rect.SetBottom( wxMin(ch, bottom) ); + resultRect.SetLeft( wxMax(0, left) ); + resultRect.SetTop( wxMax(0, top) ); + resultRect.SetRight( wxMin(cw, right) ); + resultRect.SetBottom( wxMin(ch, bottom) ); - return rect; + return resultRect; +} + +// ---------------------------------------------------------------------------- +// drop target +// ---------------------------------------------------------------------------- + +#if wxUSE_DRAG_AND_DROP + +// this allow setting drop target directly on wxGrid +void wxGrid::SetDropTarget(wxDropTarget *dropTarget) +{ + GetGridWindow()->SetDropTarget(dropTarget); } +#endif // wxUSE_DRAG_AND_DROP + // ---------------------------------------------------------------------------- // grid event classes // ---------------------------------------------------------------------------- @@ -11145,36 +8357,23 @@ IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent ) wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj, int row, int col, int x, int y, bool sel, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) { - m_row = row; - m_col = col; - m_x = x; - m_y = y; - m_selecting = sel; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + Init(row, col, x, y, sel); SetEventObject(obj); } - IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent ) wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj, int rowOrCol, int x, int y, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) { - m_rowOrCol = rowOrCol; - m_x = x; - m_y = y; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + Init(rowOrCol, x, y); SetEventObject(obj); } @@ -11187,15 +8386,10 @@ wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObjec const wxGridCellCoords& bottomRight, bool sel, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) { - m_topLeft = topLeft; - m_bottomRight = bottomRight; - m_selecting = sel; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + Init(topLeft, bottomRight, sel); SetEventObject(obj); } @@ -11214,4 +8408,166 @@ wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type, m_ctrl = ctrl; } + +// ---------------------------------------------------------------------------- +// wxGridTypeRegistry +// ---------------------------------------------------------------------------- + +wxGridTypeRegistry::~wxGridTypeRegistry() +{ + size_t count = m_typeinfo.GetCount(); + for ( size_t i = 0; i < count; i++ ) + delete m_typeinfo[i]; +} + +void wxGridTypeRegistry::RegisterDataType(const wxString& typeName, + wxGridCellRenderer* renderer, + wxGridCellEditor* editor) +{ + wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor); + + // is it already registered? + int loc = FindRegisteredDataType(typeName); + if ( loc != wxNOT_FOUND ) + { + delete m_typeinfo[loc]; + m_typeinfo[loc] = info; + } + else + { + m_typeinfo.Add(info); + } +} + +int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName) +{ + size_t count = m_typeinfo.GetCount(); + for ( size_t i = 0; i < count; i++ ) + { + if ( typeName == m_typeinfo[i]->m_typeName ) + { + return i; + } + } + + return wxNOT_FOUND; +} + +int wxGridTypeRegistry::FindDataType(const wxString& typeName) +{ + int index = FindRegisteredDataType(typeName); + if ( index == wxNOT_FOUND ) + { + // check whether this is one of the standard ones, in which case + // register it "on the fly" +#if wxUSE_TEXTCTRL + if ( typeName == wxGRID_VALUE_STRING ) + { + RegisterDataType(wxGRID_VALUE_STRING, + new wxGridCellStringRenderer, + new wxGridCellTextEditor); + } + else +#endif // wxUSE_TEXTCTRL +#if wxUSE_CHECKBOX + if ( typeName == wxGRID_VALUE_BOOL ) + { + RegisterDataType(wxGRID_VALUE_BOOL, + new wxGridCellBoolRenderer, + new wxGridCellBoolEditor); + } + else +#endif // wxUSE_CHECKBOX +#if wxUSE_TEXTCTRL + if ( typeName == wxGRID_VALUE_NUMBER ) + { + RegisterDataType(wxGRID_VALUE_NUMBER, + new wxGridCellNumberRenderer, + new wxGridCellNumberEditor); + } + else if ( typeName == wxGRID_VALUE_FLOAT ) + { + RegisterDataType(wxGRID_VALUE_FLOAT, + new wxGridCellFloatRenderer, + new wxGridCellFloatEditor); + } + else +#endif // wxUSE_TEXTCTRL +#if wxUSE_COMBOBOX + if ( typeName == wxGRID_VALUE_CHOICE ) + { + RegisterDataType(wxGRID_VALUE_CHOICE, + new wxGridCellStringRenderer, + new wxGridCellChoiceEditor); + } + else +#endif // wxUSE_COMBOBOX + { + return wxNOT_FOUND; + } + + // we get here only if just added the entry for this type, so return + // the last index + index = m_typeinfo.GetCount() - 1; + } + + return index; +} + +int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName) +{ + int index = FindDataType(typeName); + if ( index == wxNOT_FOUND ) + { + // the first part of the typename is the "real" type, anything after ':' + // are the parameters for the renderer + index = FindDataType(typeName.BeforeFirst(_T(':'))); + if ( index == wxNOT_FOUND ) + { + return wxNOT_FOUND; + } + + wxGridCellRenderer *renderer = GetRenderer(index); + wxGridCellRenderer *rendererOld = renderer; + renderer = renderer->Clone(); + rendererOld->DecRef(); + + wxGridCellEditor *editor = GetEditor(index); + wxGridCellEditor *editorOld = editor; + editor = editor->Clone(); + editorOld->DecRef(); + + // do it even if there are no parameters to reset them to defaults + wxString params = typeName.AfterFirst(_T(':')); + renderer->SetParameters(params); + editor->SetParameters(params); + + // register the new typename + RegisterDataType(typeName, renderer, editor); + + // we just registered it, it's the last one + index = m_typeinfo.GetCount() - 1; + } + + return index; +} + +wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) +{ + wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; + if (renderer) + renderer->IncRef(); + + return renderer; +} + +wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) +{ + wxGridCellEditor* editor = m_typeinfo[index]->m_editor; + if (editor) + editor->IncRef(); + + return editor; +} + #endif // wxUSE_GRID