X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1372f8ccd05daece34d4f44a07f95f45aee967de..9d5cfd0e64a2c09d957517405758de680806e674:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 3506da69f0..91dce5ee8a 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,62 +71,40 @@ const wxChar wxGridNameStr[] = wxT("grid"); // Required for wxIs... functions #include + // ---------------------------------------------------------------------------- -// array classes +// globals // ---------------------------------------------------------------------------- -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridCellAttr *, wxArrayAttrs, - class WXDLLIMPEXP_ADV); +//#define DEBUG_ATTR_CACHE +#ifdef DEBUG_ATTR_CACHE + static size_t gs_nAttrCacheHits = 0; + static size_t gs_nAttrCacheMisses = 0; +#endif -struct wxGridCellWithAttr -{ - wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_) - : coords(row, col), attr(attr_) - { - wxASSERT( attr ); - } +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- - wxGridCellWithAttr(const wxGridCellWithAttr& other) - : coords(other.coords), - attr(other.attr) - { - attr->IncRef(); - } +wxGridCellCoords wxGridNoCellCoords( -1, -1 ); +wxRect wxGridNoCellRect( -1, -1, -1, -1 ); - wxGridCellWithAttr& operator=(const wxGridCellWithAttr& other) - { - coords = other.coords; - if (attr != other.attr) - { - attr->DecRef(); - attr = other.attr; - attr->IncRef(); - } - return *this; - } +namespace +{ - void ChangeAttr(wxGridCellAttr* new_attr) - { - if (attr != new_attr) - { - // "Delete" (i.e. DecRef) the old attribute. - attr->DecRef(); - attr = new_attr; - // Take ownership of the new attribute, i.e. no IncRef. - } - } +// scroll line size +const size_t GRID_SCROLL_LINE_X = 15; +const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; - ~wxGridCellWithAttr() - { - attr->DecRef(); - } +// 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; - wxGridCellCoords coords; - wxGridCellAttr *attr; -}; +// the minimal distance in pixels the mouse needs to move to start a drag +// operation +const int DRAG_SENSITIVITY = 3; -WX_DECLARE_OBJARRAY_WITH_DECL(wxGridCellWithAttr, wxGridCellWithAttrArray, - class WXDLLIMPEXP_ADV); +} // anonymous namespace #include "wx/arrimpl.cpp" @@ -124,179 +115,30 @@ 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, - wxBORDER_NONE | additionalStyle, - name) - { - m_owner = owner; - } - - virtual bool AcceptsFocus() const { return false; } - - 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 ); - - 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 ); - - 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 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 ); - - virtual bool AcceptsFocus() const { return true; } - -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) -}; +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) @@ -306,2152 +148,171 @@ BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler ) EVT_CHAR( wxGridCellEditorEvtHandler::OnChar ) END_EVENT_TABLE() +BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick) -// ---------------------------------------------------------------------------- -// 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; + EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize) + EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing) + EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize) - wxGridCellWithAttrArray m_attrs; -}; + EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder) + EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder) +END_EVENT_TABLE() -// this class stores attributes set for rows or columns -class WXDLLIMPEXP_ADV wxGridRowOrColAttrData +wxGridOperations& wxGridRowOperations::Dual() const { -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 ); + static wxGridColumnOperations s_colOper; -private: - wxArrayInt m_rowsOrCols; - wxArrayAttrs m_attrs; -}; + return s_colOper; +} -// 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 +wxGridOperations& wxGridColumnOperations::Dual() const { -public: - wxGridCellAttrData m_cellAttrs; - wxGridRowOrColAttrData m_rowAttrs, - m_colAttrs; -}; + static wxGridRowOperations s_rowOper; + return s_rowOper; +} // ---------------------------------------------------------------------------- -// data structures used for the data type registry +// wxGridCellWorker is an (almost) empty common base class for +// wxGridCellRenderer and wxGridCellEditor managing ref counting // ---------------------------------------------------------------------------- -struct wxGridDataTypeInfo +void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params)) { - 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); - + // nothing to do +} -class WXDLLIMPEXP_ADV wxGridTypeRegistry +wxGridCellWorker::~wxGridCellWorker() { -public: - wxGridTypeRegistry() {} - ~wxGridTypeRegistry(); - - void RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor); +} - // find one of already registered data types - int FindRegisteredDataType(const wxString& typeName); +// ---------------------------------------------------------------------------- +// wxGridCellAttr +// ---------------------------------------------------------------------------- - // try to FindRegisteredDataType(), if this fails and typeName is one of - // standard typenames, register it and return its index - int FindDataType(const wxString& typeName); +void wxGridCellAttr::Init(wxGridCellAttr *attrDefault) +{ + m_nRef = 1; - // 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); + m_isReadOnly = Unset; - wxGridCellRenderer* GetRenderer(int index); - wxGridCellEditor* GetEditor(int index); + m_renderer = NULL; + m_editor = NULL; -private: - wxGridDataTypeInfoArray m_typeinfo; -}; + m_attrkind = wxGridCellAttr::Cell; -// ---------------------------------------------------------------------------- -// globals -// ---------------------------------------------------------------------------- + m_sizeRows = m_sizeCols = 1; + m_overflow = UnsetOverflow; -//#define DEBUG_ATTR_CACHE -#ifdef DEBUG_ATTR_CACHE - static size_t gs_nAttrCacheHits = 0; - static size_t gs_nAttrCacheMisses = 0; -#endif + SetDefAttr(attrDefault); +} -// ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- +wxGridCellAttr *wxGridCellAttr::Clone() const +{ + wxGridCellAttr *attr = new wxGridCellAttr(m_defGridAttr); -wxGridCellCoords wxGridNoCellCoords( -1, -1 ); -wxRect wxGridNoCellRect( -1, -1, -1, -1 ); + if ( HasTextColour() ) + attr->SetTextColour(GetTextColour()); + if ( HasBackgroundColour() ) + attr->SetBackgroundColour(GetBackgroundColour()); + if ( HasFont() ) + attr->SetFont(GetFont()); + if ( HasAlignment() ) + attr->SetAlignment(m_hAlign, m_vAlign); -// scroll line size -static const size_t GRID_SCROLL_LINE_X = 15; -static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; + attr->SetSize( m_sizeRows, m_sizeCols ); -// 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 ( m_renderer ) + { + attr->SetRenderer(m_renderer); + m_renderer->IncRef(); + } + if ( m_editor ) + { + attr->SetEditor(m_editor); + m_editor->IncRef(); + } -// ---------------------------------------------------------------------------- -// private helpers -// ---------------------------------------------------------------------------- + if ( IsReadOnly() ) + attr->SetReadOnly(); -namespace -{ + attr->SetOverflow( m_overflow == Overflow ); + attr->SetKind( m_attrkind ); -// ensure that first is less or equal to second, swapping the values if -// necessary -void EnsureFirstLessThanSecond(int& first, int& second) -{ - if ( first > second ) - wxSwap(first, second); + return attr; } -} // anonymous namespace +void wxGridCellAttr::MergeWith(wxGridCellAttr *mergefrom) +{ + if ( !HasTextColour() && mergefrom->HasTextColour() ) + SetTextColour(mergefrom->GetTextColour()); + if ( !HasBackgroundColour() && mergefrom->HasBackgroundColour() ) + SetBackgroundColour(mergefrom->GetBackgroundColour()); + if ( !HasFont() && mergefrom->HasFont() ) + SetFont(mergefrom->GetFont()); + if ( !HasAlignment() && mergefrom->HasAlignment() ) + { + int hAlign, vAlign; + mergefrom->GetAlignment( &hAlign, &vAlign); + SetAlignment(hAlign, vAlign); + } + if ( !HasSize() && mergefrom->HasSize() ) + mergefrom->GetSize( &m_sizeRows, &m_sizeCols ); -// ============================================================================ -// implementation -// ============================================================================ + // Directly access member functions as GetRender/Editor don't just return + // m_renderer/m_editor + // + // Maybe add support for merge of Render and Editor? + if (!HasRenderer() && mergefrom->HasRenderer() ) + { + m_renderer = mergefrom->m_renderer; + m_renderer->IncRef(); + } + if ( !HasEditor() && mergefrom->HasEditor() ) + { + m_editor = mergefrom->m_editor; + m_editor->IncRef(); + } + if ( !HasReadWriteMode() && mergefrom->HasReadWriteMode() ) + SetReadOnly(mergefrom->IsReadOnly()); -// ---------------------------------------------------------------------------- -// wxGridCellEditor -// ---------------------------------------------------------------------------- + if (!HasOverflowMode() && mergefrom->HasOverflowMode() ) + SetOverflow(mergefrom->GetOverflow()); -wxGridCellEditor::wxGridCellEditor() -{ - m_control = NULL; - m_attr = NULL; + SetDefAttr(mergefrom->m_defGridAttr); } -wxGridCellEditor::~wxGridCellEditor() +void wxGridCellAttr::SetSize(int num_rows, int num_cols) { - Destroy(); -} + // The size of a cell is normally 1,1 -void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent), - wxWindowID WXUNUSED(id), - wxEvtHandler* evtHandler) -{ - if ( evtHandler ) - m_control->PushEventHandler(evtHandler); -} + // If this cell is larger (2,2) then this is the top left cell + // the other cells that will be covered (lower right cells) must be + // set to negative or zero values such that + // row + num_rows of the covered cell points to the larger cell (this cell) + // same goes for the col + num_cols. -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); + // Size of 0,0 is NOT valid, neither is <=0 and any positive value - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(wxBrush(attr->GetBackgroundColour())); - dc.DrawRectangle(rectCell); + wxASSERT_MSG( (!((num_rows > 0) && (num_cols <= 0)) || + !((num_rows <= 0) && (num_cols > 0)) || + !((num_rows == 0) && (num_cols == 0))), + wxT("wxGridCellAttr::SetSize only takes two postive values or negative/zero values")); - // redraw the control we just painted over - m_control->Refresh(); + m_sizeRows = num_rows; + m_sizeCols = num_cols; } -void wxGridCellEditor::Destroy() +const wxColour& wxGridCellAttr::GetTextColour() const { - if (m_control) + if (HasTextColour()) { - m_control->PopEventHandler( true /* delete it*/ ); - - m_control->Destroy(); - m_control = NULL; + return m_colText; } -} - -void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - m_control->Show(show); - - if ( show ) + else if (m_defGridAttr && m_defGridAttr != this) { - // 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 | 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) -{ - long value = 0; - wxString text; - -#if wxUSE_SPINCTRL - if ( HasRange() ) - { - value = Spin()->GetValue(); - if ( value == m_valueOld ) - return false; - - text.Printf(wxT("%ld"), value); - } - else // using unconstrained input -#endif // wxUSE_SPINCTRL - { - const wxString textOld(grid->GetCellValue(row, col)); - text = Text()->GetValue(); - if ( text.empty() ) - { - if ( textOld.empty() ) - return false; - } - else // non-empty text now (maybe 0) - { - if ( !text.ToLong(&value) ) - return false; - - // if value == m_valueOld == 0 but old text was "" and new one is - // "0" something still did change - if ( value == m_valueOld && (value || !textOld.empty()) ) - return false; - } - } - - wxGridTableBase * const table = grid->GetTable(); - if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) ) - table->SetValueAsLong(row, col, value); - else - table->SetValue(row, col, text); - - return true; -} - -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 * const table = grid->GetTable(); - if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - { - m_valueOld = table->GetValueAsDouble(row, col); - } - else - { - m_valueOld = 0.0; - - const wxString value = table->GetValue(row, col); - if ( !value.empty() ) - { - if ( !value.ToDouble(&m_valueOld) ) - { - wxFAIL_MSG( _T("this cell doesn't have float value") ); - return; - } - } - } - - DoBeginEdit(GetString()); -} - -bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid) -{ - const wxString text(Text()->GetValue()), - textOld(grid->GetCellValue(row, col)); - - double value; - if ( !text.empty() ) - { - if ( !text.ToDouble(&value) ) - return false; - } - else // new value is empty string - { - if ( textOld.empty() ) - return false; // nothing changed - - value = 0.; - } - - // the test for empty strings ensures that we don't skip the value setting - // when "" is replaced by "0" or vice versa as "" numeric value is also 0. - if ( wxIsSameDouble(value, m_valueOld) && !text.empty() && !textOld.empty() ) - return false; // nothing changed - - wxGridTableBase * const table = grid->GetTable(); - - if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) ) - table->SetValueAsDouble(row, col, value); - else - table->SetValue(row, col, text); - - return true; -} - -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 = wxTE_PROCESS_ENTER | - wxTE_PROCESS_TAB | - wxBORDER_NONE; - - if ( !m_allowOthers ) - style |= wxCB_READONLY; - m_control = new wxComboBox(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize, - m_choices, - style); - - wxGridCellEditor::Create(parent, id, evtHandler); -} - -void wxGridCellChoiceEditor::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); - - Reset(); // this updates combo box to correspond to m_startValue - - 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() -{ - 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); - } -} - -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( wxBRUSHSTYLE_SOLID ); - - wxColour clr; - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - if ( grid.HasFocus() ) - clr = grid.GetSelectionBackground(); - else - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); - } - else - { - clr = attr.GetBackgroundColour(); - } - } - else // grey out fields if the grid is disabled - { - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); - } - - dc.SetBrush(clr); - dc.SetPen( *wxTRANSPARENT_PEN ); - dc.DrawRectangle(rect); -} - -// ---------------------------------------------------------------------------- -// wxGridCellStringRenderer -// ---------------------------------------------------------------------------- - -void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, - const wxGridCellAttr& attr, - wxDC& dc, - bool isSelected) -{ - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - - // TODO some special colours for attr.IsReadOnly() case? - - // different coloured text when the grid is disabled - if ( grid.IsEnabled() ) - { - if ( isSelected ) - { - wxColour clr; - if ( grid.HasFocus() ) - clr = grid.GetSelectionBackground(); - else - clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); - dc.SetTextBackground( clr ); - 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 -// ---------------------------------------------------------------------------- - -wxSize wxGridCellBoolRenderer::ms_sizeCheckMark; - -// FIXME these checkbox size calculations are really ugly... - -// between checkmark and box -static const wxCoord wxGRID_CHECKMARK_MARGIN = 2; - -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; - -#if defined(__WXMOTIF__) - checkSize -= size.y / 2; -#endif - - delete checkbox; - - ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize; - } - - return ms_sizeCheckMark; -} - -void wxGridCellBoolRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) -{ - wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); - - // draw a check mark in the centre (ignoring alignment - TODO) - wxSize size = GetBestSize(grid, attr, dc, row, col); - - // 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; - } - - // draw a border around checkmark - int vAlign, hAlign; - attr.GetAlignment(&hAlign, &vAlign); - - 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; - } - - 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); - } - - int flags = 0; - if (value) - flags |= wxCONTROL_CHECKED; - - wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); -} - -// ---------------------------------------------------------------------------- -// wxGridCellAttr -// ---------------------------------------------------------------------------- - -void wxGridCellAttr::Init(wxGridCellAttr *attrDefault) -{ - m_nRef = 1; - - m_isReadOnly = Unset; - - m_renderer = NULL; - m_editor = NULL; - - m_attrkind = wxGridCellAttr::Cell; - - m_sizeRows = m_sizeCols = 1; - m_overflow = UnsetOverflow; - - SetDefAttr(attrDefault); -} - -wxGridCellAttr *wxGridCellAttr::Clone() const -{ - wxGridCellAttr *attr = new wxGridCellAttr(m_defGridAttr); - - if ( HasTextColour() ) - attr->SetTextColour(GetTextColour()); - if ( HasBackgroundColour() ) - attr->SetBackgroundColour(GetBackgroundColour()); - if ( HasFont() ) - attr->SetFont(GetFont()); - if ( HasAlignment() ) - attr->SetAlignment(m_hAlign, m_vAlign); - - attr->SetSize( m_sizeRows, m_sizeCols ); - - if ( m_renderer ) - { - attr->SetRenderer(m_renderer); - m_renderer->IncRef(); - } - if ( m_editor ) - { - attr->SetEditor(m_editor); - m_editor->IncRef(); - } - - if ( IsReadOnly() ) - attr->SetReadOnly(); - - attr->SetOverflow( m_overflow == Overflow ); - attr->SetKind( m_attrkind ); - - return attr; -} - -void wxGridCellAttr::MergeWith(wxGridCellAttr *mergefrom) -{ - if ( !HasTextColour() && mergefrom->HasTextColour() ) - SetTextColour(mergefrom->GetTextColour()); - if ( !HasBackgroundColour() && mergefrom->HasBackgroundColour() ) - SetBackgroundColour(mergefrom->GetBackgroundColour()); - if ( !HasFont() && mergefrom->HasFont() ) - SetFont(mergefrom->GetFont()); - if ( !HasAlignment() && mergefrom->HasAlignment() ) - { - int hAlign, vAlign; - mergefrom->GetAlignment( &hAlign, &vAlign); - SetAlignment(hAlign, vAlign); - } - if ( !HasSize() && mergefrom->HasSize() ) - mergefrom->GetSize( &m_sizeRows, &m_sizeCols ); - - // Directly access member functions as GetRender/Editor don't just return - // m_renderer/m_editor - // - // Maybe add support for merge of Render and Editor? - if (!HasRenderer() && mergefrom->HasRenderer() ) - { - m_renderer = mergefrom->m_renderer; - m_renderer->IncRef(); - } - if ( !HasEditor() && mergefrom->HasEditor() ) - { - m_editor = mergefrom->m_editor; - m_editor->IncRef(); - } - if ( !HasReadWriteMode() && mergefrom->HasReadWriteMode() ) - SetReadOnly(mergefrom->IsReadOnly()); - - if (!HasOverflowMode() && mergefrom->HasOverflowMode() ) - SetOverflow(mergefrom->GetOverflow()); - - SetDefAttr(mergefrom->m_defGridAttr); -} - -void wxGridCellAttr::SetSize(int num_rows, int num_cols) -{ - // The size of a cell is normally 1,1 - - // If this cell is larger (2,2) then this is the top left cell - // the other cells that will be covered (lower right cells) must be - // set to negative or zero values such that - // row + num_rows of the covered cell points to the larger cell (this cell) - // same goes for the col + num_cols. - - // Size of 0,0 is NOT valid, neither is <=0 and any positive value - - wxASSERT_MSG( (!((num_rows > 0) && (num_cols <= 0)) || - !((num_rows <= 0) && (num_cols > 0)) || - !((num_rows == 0) && (num_cols == 0))), - wxT("wxGridCellAttr::SetSize only takes two postive values or negative/zero values")); - - m_sizeRows = num_rows; - m_sizeCols = num_cols; -} - -const wxColour& wxGridCellAttr::GetTextColour() const -{ - if (HasTextColour()) - { - return m_colText; - } - else if (m_defGridAttr && m_defGridAttr != this) - { - return m_defGridAttr->GetTextColour(); + return m_defGridAttr->GetTextColour(); } else { @@ -2651,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 ) @@ -2761,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 ) @@ -2847,7 +708,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols wxGridCellAttrProvider::wxGridCellAttrProvider() { - m_data = (wxGridCellAttrProviderData *)NULL; + m_data = NULL; } wxGridCellAttrProvider::~wxGridCellAttrProvider() @@ -2863,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) @@ -2996,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 // ---------------------------------------------------------------------------- @@ -3165,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() @@ -3196,7 +896,7 @@ 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) @@ -3395,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; @@ -3471,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; @@ -3807,22 +1498,12 @@ 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 ) END_EVENT_TABLE() -wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); @@ -3849,27 +1530,18 @@ void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - 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 ) 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); @@ -3899,28 +1571,18 @@ void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - 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 ) 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); @@ -3935,13 +1597,12 @@ void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - 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 ) @@ -3954,21 +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, - wxWANTS_CHARS | wxCLIP_CHILDREN, - wxT("grid window") ) -{ - m_owner = parent; - m_rowLabelWin = rowLblWin; - m_colLabelWin = colLblWin; -} - void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPaintDC dc( this ); @@ -3977,17 +1623,18 @@ void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); m_owner->DrawGridCellArea( dc, dirtyCells ); + m_owner->DrawGridSpace( dc ); + m_owner->DrawAllGridLines( dc, reg ); - 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 ) @@ -4000,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 @@ -4057,23 +1705,8 @@ void wxGridWindow::OnFocus(wxFocusEvent& 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) ///////////////////////////////////////////////////////////////////// @@ -4138,22 +1771,6 @@ BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow ) EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground ) END_EVENT_TABLE() -wxGrid::wxGrid() -{ - InitVars(); -} - -wxGrid::wxGrid( wxWindow *parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name ) -{ - InitVars(); - Create(parent, id, pos, size, style, name); -} - bool wxGrid::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) @@ -4173,8 +1790,16 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, return true; } -wxGrid::~wxGrid() -{ +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(); @@ -4242,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 ); @@ -4286,13 +1891,38 @@ 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, @@ -4360,6 +1990,9 @@ wxGrid::SetTable(wxGridTableBase *table, 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; @@ -4369,22 +2002,22 @@ wxGrid::SetTable(wxGridTableBase *table, // 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(); @@ -4394,13 +2027,13 @@ wxGrid::SetTable(wxGridTableBase *table, return m_created; } -void wxGrid::InitVars() +void wxGrid::Init() { m_created = false; m_cornerLabelWin = NULL; m_rowLabelWin = NULL; - m_colLabelWin = NULL; + m_colWindow = NULL; m_gridWin = NULL; m_table = NULL; @@ -4410,24 +2043,10 @@ void wxGrid::InitVars() m_defaultCellAttr = NULL; m_typeRegistry = NULL; m_winCapture = NULL; -} -void wxGrid::Init() -{ m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; - if ( m_rowLabelWin ) - { - m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); - } - else - { - m_labelBackgroundColour = *wxWHITE; - } - - m_labelTextColour = *wxBLACK; - // init attr cache m_attrCache.row = -1; m_attrCache.col = -1; @@ -4444,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; @@ -4464,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; @@ -4473,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; @@ -4482,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); @@ -4538,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 ); } } @@ -4663,8 +2284,8 @@ void wxGrid::CalcWindowSizes() 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, gw, 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, gh ); @@ -4692,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: @@ -4856,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 @@ -4910,7 +2520,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4921,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() ) { @@ -4963,7 +2575,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4974,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() ) { @@ -5038,7 +2652,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -5188,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; @@ -5198,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 ); - - if ( GetColRight(col) <= left ) - continue; + // do determine the dirty columns + for ( int pos = XToPos(left); pos <= XToPos(right); pos++ ) + cols.push_back(GetColAt(pos)); - 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; @@ -5264,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; @@ -5321,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); } } @@ -5370,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; } } @@ -5438,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() ) @@ -5460,78 +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 ) ); - else if ( x >= (GetColLeft( m_moveToCol ) + (GetColWidth(m_moveToCol) / 2)) ) - { - m_moveToCol = GetColAt( GetColPos( m_moveToCol ) + 1 ); - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); - else - markerX = GetColLeft( m_moveToCol ); - } + 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); @@ -5542,9 +3200,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) 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 ))) + if ( colNew == m_dragRowOrCol ) color = wxLIGHT_GREY; else color = wxBLUE; @@ -5576,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; } @@ -5585,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 @@ -5598,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 { @@ -5622,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()); } } } @@ -5649,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()); } } @@ -5657,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 ) ) { @@ -5670,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; } } @@ -5685,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; } @@ -5714,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 ) ) { @@ -5726,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 ) ) { @@ -5745,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); } } } @@ -5793,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; @@ -5819,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 @@ -5838,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; @@ -5875,612 +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 ( isFirstDrag ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - - SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG, - coords.GetRow(), - coords.GetCol(), - event ); - return; - } - } - else - { - 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 too fast, at least under MSW and 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 ( event.LeftIsDown() && - 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 ( event.LeftIsDown() && - 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 ) + { + m_winCapture = m_gridWin; + m_winCapture->CaptureMouse(); + } +} + +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; + } + + 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 ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + DisableCellEditControl(); + MakeCellVisible( coords ); + + if ( event.CmdDown() ) { - if ( !event.CmdDown() ) - ClearSelection(); - if ( event.ShiftDown() ) + if ( m_selection ) { - if ( m_selection ) - { - m_selection->SelectBlock( m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol(), - coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->ToggleCellSelection(coords, event); } - else if ( XToEdgeOfCol(x) < 0 && - YToEdgeOfRow(y) < 0 ) - { - DisableCellEditControl(); - MakeCellVisible( coords ); - 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 ); - } - } + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = coords; + } + else + { + m_waitForSlowClick = m_currentCellCoords == coords && + coords != wxGridNoCellCoords; + SetCurrentCell( coords ); } } +} - // ------------ Left double click - // - else if ( event.LeftDClick() && coords != wxGridNoCellCoords ) +void +wxGrid::DoGridCellLeftDClick(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) { - DisableCellEditControl(); - - if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 ) + if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) - { - // 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; - } + // 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; } } +} - // ------------ Left button released - // - else if ( event.LeftUp() ) +void +wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) +{ + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + if (m_winCapture) { - if (m_winCapture) - { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - } + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + } - if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) - { - ClearSelection(); - EnableCellEditControl(); + if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) + { + ClearSelection(); + EnableCellEditControl(); - wxGridCellAttr *attr = GetCellAttr(coords); - wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); - editor->StartingClick(); - editor->DecRef(); - attr->DecRef(); + wxGridCellAttr *attr = GetCellAttr(coords); + wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); + editor->StartingClick(); + editor->DecRef(); + attr->DecRef(); - m_waitForSlowClick = false; - } - else if ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords ) + m_waitForSlowClick = false; + } + else if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) + { + if ( m_selection ) { - 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() ); - } + m_selection->SelectBlock( m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event ); + } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; - // Show the edit control, if it has been hidden for - // drag-shrinking. - ShowCellEditControl(); - } + // 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(); + } + else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + DoEndDragResizeRow(); - // 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(); + // 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(); + } - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event ); - } + m_dragLastPos = -1; +} - 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; } - // ------------ Right button down + 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 ) ) - { - // no default action at the moment - } + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + return; } - // ------------ Right double click - // - else if ( event.RightDClick() && coords != wxGridNoCellCoords ) + if ( dragRow >= 0 ) { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + 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; - // ------------ Moving and no button action - // - else if ( event.Moving() && !event.IsButton() ) + 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 { - if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) + if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - // out of grid cell area ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - return; } + } +} - int dragRow = YToEdgeOfRow( y ); - int dragCol = XToEdgeOfCol( x ); +void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) +{ + const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); - // 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; - } + // coordinates of the cell under mouse + wxGridCellCoords coords = XYToCell(pos); - if ( dragRow >= 0 ) - { - m_dragRowOrCol = dragRow; + 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 ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); - } - } - else if ( dragCol >= 0 ) + if ( event.Dragging() ) + { + if ( event.LeftIsDown() ) + DoGridDragEvent(event, coords); + else + event.Skip(); + return; + } + + m_isDragging = false; + m_startDragPos = wxDefaultPosition; + + // 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 + + // deal with various button presses + if ( event.IsButton() ) + { + if ( coords != wxGridNoCellCoords ) { - m_dragRowOrCol = dragCol; + DisableCellEditControl(); - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragColSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, 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 // 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(); +} + +void wxGrid::SetColumnsOrder(const wxArrayInt& order) +{ + m_colAt = order; - m_colAt[newPos] = colID; + RefreshAfterColPosChange(); +} - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) +void wxGrid::SetColPos(int idx, int pos) +{ + // we're going to need m_colAt now, initialize it if needed + if ( m_colAt.empty() ) { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - int colID = GetColAt( colPos ); - - colRight += m_colWidths[colID]; - m_colRights[colID] = colRight; - } + 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 } @@ -6512,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() @@ -6528,144 +4152,48 @@ void wxGrid::ClearGrid() } } -bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) -{ - 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) ) -{ - 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) ) +bool +wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t), + int pos, int num, bool WXUNUSED(updateLabels) ) { - 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 - } + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - return false; -} - -bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) -{ - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") ); + if ( !m_table ) return false; - } - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); + 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 (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::AppendCols( int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), + int num, bool WXUNUSED(updateLabels)) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") ); - return false; - } - - 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 false; -} + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); -bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) -{ - 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; @@ -6679,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(); @@ -6693,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(); @@ -6723,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(); } @@ -6739,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(); } @@ -6754,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; @@ -6774,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(); @@ -6854,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. @@ -6874,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); } @@ -7001,8 +4517,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) case WXK_HOME: if ( event.ControlDown() ) { - MakeCellVisible( 0, 0 ); - SetCurrentCell( 0, 0 ); + GoToCell(0, 0); } else { @@ -7013,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 { @@ -7076,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; } } @@ -7144,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__) @@ -7194,35 +4703,62 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) 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) { - 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; } } + m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); + MakeCellVisible(m_selectedBlockCorner); + 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 ), @@ -7231,8 +4767,8 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol } // 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 @@ -7246,10 +4782,10 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol 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. EnsureFirstLessThanSecond(oldLeft, leftCol); @@ -7308,8 +4844,8 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol } // change selection - m_selectingTopLeft = updateTopLeft; - m_selectingBottomRight = updateBottomRight; + m_selectedBlockTopLeft = updateTopLeft; + m_selectedBlockBottomRight = updateBottomRight; } // @@ -7589,27 +5125,6 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) 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() @@ -7706,7 +5221,7 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) // void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) { - if ( !m_gridLinesEnabled || !m_numRows || !m_numCols ) + if ( !m_gridLinesEnabled ) return; int top, bottom, left, right; @@ -7717,9 +5232,25 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) CalcUnscrolledPosition( cw, ch, &right, &bottom ); // 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) ); + if ( m_gridLinesClipHorz ) + { + if ( !m_numCols ) + return; + + const int lastColRight = GetColRight(GetColAt(m_numCols - 1)); + if ( right > lastColRight ) + right = lastColRight; + } + + if ( m_gridLinesClipVert ) + { + if ( !m_numRows ) + return; + + const int lastRowBottom = GetRowBottom(m_numRows - 1); + if ( bottom > lastRowBottom ) + bottom = lastRowBottom; + } // no gridlines inside multicells, clip them out int leftCol = GetColPos( internalXToCol(left) ); @@ -7841,8 +5372,26 @@ 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) { @@ -7850,7 +5399,7 @@ void wxGrid::SetUseNativeColLabels( bool native ) SetColLabelSize( height ); } - m_colLabelWin->Refresh(); + GetColLabelWindow()->Refresh(); m_cornerLabelWin->Refresh(); } @@ -7902,7 +5451,18 @@ void wxGrid::DrawColLabel(wxDC& dc, int col) if ( m_nativeColumnLabels ) { - wxRendererNative::Get().DrawHeaderButton(m_colLabelWin, 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 { @@ -8118,7 +5678,7 @@ void wxGrid::EndBatch() { CalcDimensions(); m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); m_gridWin->Refresh(); } @@ -8152,8 +5712,6 @@ bool wxGrid::Enable(bool enable) void wxGrid::EnableEditing( bool edit ) { - // TODO: improve this ? - // if ( edit != m_editable ) { if (!edit) @@ -8171,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! @@ -8184,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(); @@ -8417,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(); } } @@ -8442,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 (!defaultDist) - defaultDist = 1; + if ( coord < 0 ) + return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND; - size_t i_max = coord / defaultDist, - i_min = 0; + const int defaultLineSize = oper.GetDefaultLineSize(this); + wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); - if (BorderArray.IsEmpty()) + int maxPos = coord / defaultLineSize, + minPos = 0; + + // 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; + + const int line = oper.PosToLine(this, pos, true); - if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE && CanDragRowSize() ) + 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 @@ -8664,13 +6191,13 @@ wxRect wxGrid::CellToRect( int row, int col ) const rect.width += GetColWidth(i); for (i=row; i < row + cell_rows; i++) rect.height += GetRowHeight(i); - } - // if grid lines are enabled, then the area of the cell is a bit smaller - if (m_gridLinesEnabled) - { - rect.width -= 1; - rect.height -= 1; + // if grid lines are enabled, then the area of the cell is a bit smaller + if (m_gridLinesEnabled) + { + rect.width -= 1; + rect.height -= 1; + } } return rect; @@ -8779,475 +6306,209 @@ void wxGrid::MakeCellVisible( int row, int col ) { if ( xpos != -1 ) xpos /= m_scrollLineX; - if ( ypos != -1 ) - ypos /= m_scrollLineY; - Scroll( xpos, ypos ); - AdjustScrollbars(); - } - } -} - -// -// ------ 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 ) -{ - 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 ); + if ( ypos != -1 ) + ypos /= m_scrollLineY; + Scroll( xpos, ypos ); + AdjustScrollbars(); } - else - return false; - - return true; } - - return false; } -bool wxGrid::MoveCursorLeft( bool expandSelection ) +// +// ------ Grid cursor movement functions +// + +bool +wxGrid::DoMoveCursor(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() >= 0 ) + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; + + 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()) + ); } // @@ -9345,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 ); } @@ -9367,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(); } } @@ -9387,7 +6648,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } } @@ -9398,7 +6659,7 @@ void wxGrid::SetLabelFont( const wxFont& font ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9464,7 +6725,7 @@ void wxGrid::SetColLabelAlignment( int horiz, int vert ) if ( !GetBatchCount() ) { - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9481,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 ) @@ -9510,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 ); + } } } } @@ -9528,9 +6796,8 @@ void wxGrid::SetGridLineColour( const wxColour& colour ) { m_gridLineColour = colour; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9585,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(); } } @@ -9883,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); } } @@ -9903,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")); @@ -10301,7 +7585,7 @@ void wxGrid::SetColSize( int col, int width ) { long w, h; wxArrayString lines; - wxClientDC dc(m_colLabelWin); + wxClientDC dc(m_colWindow); dc.SetFont(GetLabelFont()); StringToLines(GetColLabelValue(col), lines); if ( GetColLabelTextOrientation() == wxHORIZONTAL ) @@ -10313,15 +7597,12 @@ void wxGrid::SetColSize( int col, int width ) width = wxMax(width, GetColMinimalAcceptableWidth()); } - // 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() ) + // 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() ) @@ -10330,9 +7611,11 @@ void wxGrid::SetColSize( int col, int width ) 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++ ) { @@ -10340,7 +7623,10 @@ void wxGrid::SetColSize( int col, int width ) } if ( !GetBatchCount() ) + { CalcDimensions(); + Refresh(); + } } void wxGrid::SetColMinimalWidth( int col, int width ) @@ -10493,14 +7779,21 @@ 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 @@ -10739,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() @@ -10779,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 ) @@ -10830,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 @@ -10900,13 +8192,20 @@ wxArrayInt wxGrid::GetSelectedCols() const void wxGrid::ClearSelection() { - wxRect r1 = BlockToDeviceRect( m_selectingTopLeft, m_selectingBottomRight); - wxRect r2 = BlockToDeviceRect( m_currentCellCoords, m_selectingKeyboard ); - m_selectingTopLeft = - m_selectingBottomRight = - m_selectingKeyboard = wxGridNoCellCoords; - Refresh( false, &r1 ); - Refresh( false, &r2 ); + wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft, + m_selectedBlockBottomRight); + wxRect r2 = BlockToDeviceRect(m_currentCellCoords, + m_selectedBlockCorner); + + m_selectedBlockTopLeft = + m_selectedBlockBottomRight = + m_selectedBlockCorner = wxGridNoCellCoords; + + if ( !r1.IsEmpty() ) + RefreshRect(r1, false); + if ( !r2.IsEmpty() ) + RefreshRect(r2, false); + if ( m_selection ) m_selection->ClearSelection(); } @@ -11037,6 +8336,48 @@ wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, return resultRect; } +void wxGrid::DoSetSizes(const wxGridSizesInfo& sizeInfo, + const wxGridOperations& oper) +{ + BeginBatch(); + oper.SetDefaultLineSize(this, sizeInfo.m_sizeDefault, true); + const int numLines = oper.GetNumberOfLines(this); + for ( int i = 0; i < numLines; i++ ) + { + int size = sizeInfo.GetSize(i); + if ( size != sizeInfo.m_sizeDefault) + oper.SetLineSize(this, i, size); + } + EndBatch(); +} + +void wxGrid::SetColSizes(const wxGridSizesInfo& sizeInfo) +{ + DoSetSizes(sizeInfo, wxGridColumnOperations()); +} + +void wxGrid::SetRowSizes(const wxGridSizesInfo& sizeInfo) +{ + DoSetSizes(sizeInfo, wxGridRowOperations()); +} + +wxGridSizesInfo::wxGridSizesInfo(int defSize, const wxArrayInt& allSizes) +{ + m_sizeDefault = defSize; + for ( size_t i = 0; i < allSizes.size(); i++ ) + { + if ( allSizes[i] != defSize ) + m_customSizes[i] = allSizes[i]; + } +} + +int wxGridSizesInfo::GetSize(unsigned pos) const +{ + wxUnsignedToIntHashMap::const_iterator it = m_customSizes.find(pos); + + return it == m_customSizes.end() ? m_sizeDefault : it->second; +} + // ---------------------------------------------------------------------------- // drop target // ---------------------------------------------------------------------------- @@ -11060,36 +8401,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); } @@ -11102,15 +8430,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); } @@ -11129,4 +8452,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