X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/54181a33299792665cabdadb22459255e3959a09..07aaf32633ecf18ec3edfbb41793a112914792d0:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index a09bdeacfe..6bd231ea0b 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,15 @@ #include "wx/spinctrl.h" #include "wx/tokenzr.h" #include "wx/renderer.h" +#include "wx/headerctrl.h" +#include "wx/hashset.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 +72,59 @@ const wxChar wxGridNameStr[] = wxT("grid"); // Required for wxIs... functions #include +WX_DECLARE_HASH_SET_WITH_DECL_PTR(int, wxIntegerHash, wxIntegerEqual, + wxGridFixedIndicesSet, class WXDLLIMPEXP_ADV); + + // ---------------------------------------------------------------------------- -// array classes +// globals // ---------------------------------------------------------------------------- -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridCellAttr *, wxArrayAttrs, - class WXDLLIMPEXP_ADV); +namespace +{ + +//#define DEBUG_ATTR_CACHE +#ifdef DEBUG_ATTR_CACHE + static size_t gs_nAttrCacheHits = 0; + static size_t gs_nAttrCacheMisses = 0; +#endif -struct wxGridCellWithAttr +// this struct simply combines together the default header renderers +// +// as the renderers ctors are trivial, there is no problem with making them +// globals +struct DefaultHeaderRenderers { - wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_) - : coords(row, col), attr(attr_) - { - wxASSERT( attr ); - } + wxGridColumnHeaderRendererDefault colRenderer; + wxGridRowHeaderRendererDefault rowRenderer; + wxGridCornerHeaderRendererDefault cornerRenderer; +} gs_defaultHeaderRenderers; - wxGridCellWithAttr(const wxGridCellWithAttr& other) - : coords(other.coords), - attr(other.attr) - { - attr->IncRef(); - } +} // anonymous namespace - wxGridCellWithAttr& operator=(const wxGridCellWithAttr& other) - { - coords = other.coords; - if (attr != other.attr) - { - attr->DecRef(); - attr = other.attr; - attr->IncRef(); - } - return *this; - } +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- - 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. - } - } +wxGridCellCoords wxGridNoCellCoords( -1, -1 ); +wxRect wxGridNoCellRect( -1, -1, -1, -1 ); - ~wxGridCellWithAttr() - { - attr->DecRef(); - } +namespace +{ + +// scroll line size +const size_t GRID_SCROLL_LINE_X = 15; +const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; + +// the size of hash tables used a bit everywhere (the max number of elements +// in these hash tables is the number of rows/columns) +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 +135,49 @@ 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) +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_AUTO_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 ); +wxDEFINE_EVENT( wxEVT_GRID_TABBING, wxGridEvent ); // ---------------------------------------------------------------------------- -// private classes +// private helpers // ---------------------------------------------------------------------------- -// 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 +namespace { -public: - wxGridCellEditorEvtHandler(wxGrid* grid, wxGridCellEditor* editor) - : m_grid(grid), - m_editor(editor), - m_inSetFocus(false) + + // 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); } + +} // anonymous namespace - 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) -}; - +// ============================================================================ +// implementation +// ============================================================================ IMPLEMENT_ABSTRACT_CLASS(wxGridCellEditorEvtHandler, wxEvtHandler) @@ -306,2158 +187,241 @@ BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler ) EVT_CHAR( wxGridCellEditorEvtHandler::OnChar ) END_EVENT_TABLE() +BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick) + EVT_HEADER_DCLICK(wxID_ANY, wxGridHeaderCtrl::OnDoubleClick) + EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxGridHeaderCtrl::OnRightClick) -// ---------------------------------------------------------------------------- -// 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(); + static wxGridColumnOperations s_colOper; - void SetAttr(wxGridCellAttr *attr, int rowOrCol); - wxGridCellAttr *GetAttr(int rowOrCol) const; - void UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols ); - -private: - wxArrayInt m_rowsOrCols; - wxArrayAttrs m_attrs; -}; + 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) -}; - + // nothing to do +} -WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray, - class WXDLLIMPEXP_ADV); +wxGridCellWorker::~wxGridCellWorker() +{ +} +// ---------------------------------------------------------------------------- +// wxGridHeaderLabelsRenderer and related classes +// ---------------------------------------------------------------------------- -class WXDLLIMPEXP_ADV wxGridTypeRegistry +void wxGridHeaderLabelsRenderer::DrawLabel(const wxGrid& grid, + wxDC& dc, + const wxString& value, + const wxRect& rect, + int horizAlign, + int vertAlign, + int textOrientation) const { -public: - wxGridTypeRegistry() {} - ~wxGridTypeRegistry(); - - void RegisterDataType(const wxString& typeName, - wxGridCellRenderer* renderer, - wxGridCellEditor* editor); + dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); + dc.SetTextForeground(grid.GetLabelTextColour()); + dc.SetFont(grid.GetLabelFont()); + grid.DrawTextRectangle(dc, value, rect, horizAlign, vertAlign, textOrientation); +} - // find one of already registered data types - int FindRegisteredDataType(const wxString& typeName); - // try to FindRegisteredDataType(), if this fails and typeName is one of - // standard typenames, register it and return its index - int FindDataType(const wxString& typeName); +void wxGridRowHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const +{ + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight(), rect.GetTop(), + rect.GetRight(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetLeft(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetBottom(), + rect.GetRight() + 1, rect.GetBottom()); - // 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); + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), + rect.GetLeft() + 1, rect.GetBottom()); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), + rect.GetRight(), rect.GetTop()); - wxGridCellRenderer* GetRenderer(int index); - wxGridCellEditor* GetEditor(int index); + rect.Deflate(2); +} -private: - wxGridDataTypeInfoArray m_typeinfo; -}; +void wxGridColumnHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const +{ + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight(), rect.GetTop(), + rect.GetRight(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetRight(), rect.GetTop()); + dc.DrawLine(rect.GetLeft(), rect.GetBottom(), + rect.GetRight() + 1, rect.GetBottom()); + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1, + rect.GetLeft(), rect.GetBottom()); + dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1, + rect.GetRight(), rect.GetTop() + 1); -// ---------------------------------------------------------------------------- -// conditional compilation -// ---------------------------------------------------------------------------- + rect.Deflate(2); +} -#ifndef WXGRID_DRAW_LINES -#define WXGRID_DRAW_LINES 1 -#endif +void wxGridCornerHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid), + wxDC& dc, + wxRect& rect) const +{ + dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW))); + dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1, + rect.GetRight() - 1, rect.GetTop()); + dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1, + rect.GetLeft(), rect.GetBottom() - 1); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetRight(), rect.GetTop()); + dc.DrawLine(rect.GetLeft(), rect.GetTop(), + rect.GetLeft(), rect.GetBottom()); -// ---------------------------------------------------------------------------- -// globals -// ---------------------------------------------------------------------------- + dc.SetPen(*wxWHITE_PEN); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1, + rect.GetRight() - 1, rect.GetTop() + 1); + dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1, + rect.GetLeft() + 1, rect.GetBottom() - 1); -//#define DEBUG_ATTR_CACHE -#ifdef DEBUG_ATTR_CACHE - static size_t gs_nAttrCacheHits = 0; - static size_t gs_nAttrCacheMisses = 0; -#endif + rect.Deflate(2); +} // ---------------------------------------------------------------------------- -// constants +// wxGridCellAttr // ---------------------------------------------------------------------------- -wxGridCellCoords wxGridNoCellCoords( -1, -1 ); -wxRect wxGridNoCellRect( -1, -1, -1, -1 ); +void wxGridCellAttr::Init(wxGridCellAttr *attrDefault) +{ + m_isReadOnly = Unset; -// scroll line size -// TODO: this doesn't work at all, grid cells have different sizes and approx -// calculations don't work as because of the size mismatch scrollbars -// sometimes fail to be shown when they should be or vice versa -// -// The scroll bars may be a little flakey once in a while, but that is -// surely much less horrible than having scroll lines of only 1!!! -// -- Robin -// -// Well, it's still seriously broken so it might be better but needs -// fixing anyhow -// -- Vadim -static const size_t GRID_SCROLL_LINE_X = 15; // 1; -static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; + m_renderer = NULL; + m_editor = NULL; -// 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; + m_attrkind = wxGridCellAttr::Cell; -#if 0 -// ---------------------------------------------------------------------------- -// private functions -// ---------------------------------------------------------------------------- + m_sizeRows = m_sizeCols = 1; + m_overflow = UnsetOverflow; -static inline int GetScrollX(int x) -{ - return (x + GRID_SCROLL_LINE_X - 1) / GRID_SCROLL_LINE_X; + SetDefAttr(attrDefault); } -static inline int GetScrollY(int y) +wxGridCellAttr *wxGridCellAttr::Clone() const { - return (y + GRID_SCROLL_LINE_Y - 1) / GRID_SCROLL_LINE_Y; -} -#endif - -// ============================================================================ -// implementation -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxGridCellEditor -// ---------------------------------------------------------------------------- + wxGridCellAttr *attr = new wxGridCellAttr(m_defGridAttr); -wxGridCellEditor::wxGridCellEditor() -{ - m_control = NULL; - m_attr = NULL; -} + if ( HasTextColour() ) + attr->SetTextColour(GetTextColour()); + if ( HasBackgroundColour() ) + attr->SetBackgroundColour(GetBackgroundColour()); + if ( HasFont() ) + attr->SetFont(GetFont()); + if ( HasAlignment() ) + attr->SetAlignment(m_hAlign, m_vAlign); -wxGridCellEditor::~wxGridCellEditor() -{ - Destroy(); -} + attr->SetSize( m_sizeRows, m_sizeCols ); -void wxGridCellEditor::Create(wxWindow* WXUNUSED(parent), - wxWindowID WXUNUSED(id), - wxEvtHandler* evtHandler) -{ - if ( evtHandler ) - m_control->PushEventHandler(evtHandler); -} + if ( m_renderer ) + { + attr->SetRenderer(m_renderer); + m_renderer->IncRef(); + } + if ( m_editor ) + { + attr->SetEditor(m_editor); + m_editor->IncRef(); + } -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); + if ( IsReadOnly() ) + attr->SetReadOnly(); - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); - dc.DrawRectangle(rectCell); + attr->SetOverflow( m_overflow == Overflow ); + attr->SetKind( m_attrkind ); - // redraw the control we just painted over - m_control->Refresh(); + return attr; } -void wxGridCellEditor::Destroy() +void wxGridCellAttr::MergeWith(wxGridCellAttr *mergefrom) { - if (m_control) + 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() ) { - m_control->PopEventHandler( true /* delete it*/ ); - - m_control->Destroy(); - m_control = NULL; + int hAlign, vAlign; + mergefrom->GetAlignment( &hAlign, &vAlign); + SetAlignment(hAlign, vAlign); } -} - -void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); - - m_control->Show(show); + if ( !HasSize() && mergefrom->HasSize() ) + mergefrom->GetSize( &m_sizeRows, &m_sizeCols ); - if ( show ) + // 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() ) { - // 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 - } + m_renderer = mergefrom->m_renderer; + m_renderer->IncRef(); } - else + if ( !HasEditor() && mergefrom->HasEditor() ) { - // 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 + m_editor = mergefrom->m_editor; + m_editor->IncRef(); } -} + if ( !HasReadWriteMode() && mergefrom->HasReadWriteMode() ) + SetReadOnly(mergefrom->IsReadOnly()); -void wxGridCellEditor::SetSize(const wxRect& rect) -{ - wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!")); + if (!HasOverflowMode() && mergefrom->HasOverflowMode() ) + SetOverflow(mergefrom->GetOverflow()); - m_control->SetSize(rect, wxSIZE_ALLOW_MINUS_ONE); + SetDefAttr(mergefrom->m_defGridAttr); } -void wxGridCellEditor::HandleReturn(wxKeyEvent& event) +void wxGridCellAttr::SetSize(int num_rows, int num_cols) { - event.Skip(); -} + // The size of a cell is normally 1,1 -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 ); - - // grey out fields if 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.SetBrush( wxBrush(clr, wxBRUSHSTYLE_SOLID) ); - } - else - { - dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID) ); - } - } - else - { - dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxBRUSHSTYLE_SOLID)); - } - - 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. + // 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")); + wxT("wxGridCellAttr::SetSize only takes two positive values or negative/zero values")); m_sizeRows = num_rows; m_sizeCols = num_cols; @@ -2533,6 +497,15 @@ void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const } } +void wxGridCellAttr::GetNonDefaultAlignment(int *hAlign, int *vAlign) const +{ + if ( hAlign && m_hAlign != wxALIGN_INVALID ) + *hAlign = m_hAlign; + + if ( vAlign && m_vAlign != wxALIGN_INVALID ) + *vAlign = m_vAlign; +} + void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const { if ( num_rows ) @@ -2671,7 +644,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 ) @@ -2781,7 +754,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 ) @@ -2800,30 +773,30 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) { if ( attr ) { - // add the attribute - no need to do anything to reference count - // since we take ownership of the attribute. + // store the new attribute, taking its ownership m_rowsOrCols.Add(rowOrCol); m_attrs.Add(attr); } // nothing to remove } - else + else // we have an attribute for this row or column { size_t n = (size_t)i; - if ( m_attrs[n] == attr ) - // nothing to do - return; + + // notice that this code works correctly even when the old attribute is + // the same as the new one: as we own of it, we must call DecRef() on + // it in any case and this won't result in destruction of the new + // attribute if it's the same as old one because it must have ref count + // of at least 2 to be passed to us while we keep a reference to it too + m_attrs[n]->DecRef(); + if ( attr ) { - // change the attribute, handling reference count manually, - // taking ownership of the new attribute. - m_attrs[n]->DecRef(); + // replace the attribute with the new one m_attrs[n] = attr; } - else + else // remove the attribute { - // remove this attribute, handling reference count manually - m_attrs[n]->DecRef(); m_rowsOrCols.RemoveAt(n); m_attrs.RemoveAt(n); } @@ -2867,7 +840,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols wxGridCellAttrProvider::wxGridCellAttrProvider() { - m_data = (wxGridCellAttrProviderData *)NULL; + m_data = NULL; } wxGridCellAttrProvider::~wxGridCellAttrProvider() @@ -2883,7 +856,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) @@ -2938,243 +911,99 @@ wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col, attr->DecRef(); attr = attrcol; } - if (attrcell) - { - if (attr) - attr->DecRef(); - attr = attrcell; - } - } - } - break; - - case (wxGridCellAttr::Cell): - attr = m_data->m_cellAttrs.GetAttr(row, col); - break; - - case (wxGridCellAttr::Col): - attr = m_data->m_colAttrs.GetAttr(col); - break; - - case (wxGridCellAttr::Row): - attr = m_data->m_rowAttrs.GetAttr(row); - break; - - default: - // unused as yet... - // (wxGridCellAttr::Default): - // (wxGridCellAttr::Merged): - break; - } - } - - return attr; -} - -void wxGridCellAttrProvider::SetAttr(wxGridCellAttr *attr, - int row, int col) -{ - if ( !m_data ) - InitData(); - - m_data->m_cellAttrs.SetAttr(attr, row, col); -} - -void wxGridCellAttrProvider::SetRowAttr(wxGridCellAttr *attr, int row) -{ - if ( !m_data ) - InitData(); - - m_data->m_rowAttrs.SetAttr(attr, row); -} - -void wxGridCellAttrProvider::SetColAttr(wxGridCellAttr *attr, int col) -{ - if ( !m_data ) - InitData(); - - m_data->m_colAttrs.SetAttr(attr, col); -} - -void wxGridCellAttrProvider::UpdateAttrRows( size_t pos, int numRows ) -{ - if ( m_data ) - { - m_data->m_cellAttrs.UpdateAttrRows( pos, numRows ); - - m_data->m_rowAttrs.UpdateAttrRowsOrCols( pos, numRows ); - } -} - -void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) -{ - if ( m_data ) - { - m_data->m_cellAttrs.UpdateAttrCols( pos, numCols ); - - m_data->m_colAttrs.UpdateAttrRowsOrCols( pos, 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; - } + if (attrcell) + { + if (attr) + attr->DecRef(); + attr = attrcell; + } + } + } + break; - // we get here only if just added the entry for this type, so return - // the last index - index = m_typeinfo.GetCount() - 1; + case (wxGridCellAttr::Cell): + attr = m_data->m_cellAttrs.GetAttr(row, col); + break; + + case (wxGridCellAttr::Col): + attr = m_data->m_colAttrs.GetAttr(col); + break; + + case (wxGridCellAttr::Row): + attr = m_data->m_rowAttrs.GetAttr(row); + break; + + default: + // unused as yet... + // (wxGridCellAttr::Default): + // (wxGridCellAttr::Merged): + break; + } } - return index; + return attr; } -int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName) +void wxGridCellAttrProvider::SetAttr(wxGridCellAttr *attr, + int row, int col) { - 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; - } + if ( !m_data ) + InitData(); - wxGridCellRenderer *renderer = GetRenderer(index); - wxGridCellRenderer *rendererOld = renderer; - renderer = renderer->Clone(); - rendererOld->DecRef(); + m_data->m_cellAttrs.SetAttr(attr, row, col); +} - wxGridCellEditor *editor = GetEditor(index); - wxGridCellEditor *editorOld = editor; - editor = editor->Clone(); - editorOld->DecRef(); +void wxGridCellAttrProvider::SetRowAttr(wxGridCellAttr *attr, int row) +{ + if ( !m_data ) + InitData(); - // do it even if there are no parameters to reset them to defaults - wxString params = typeName.AfterFirst(_T(':')); - renderer->SetParameters(params); - editor->SetParameters(params); + m_data->m_rowAttrs.SetAttr(attr, row); +} - // register the new typename - RegisterDataType(typeName, renderer, editor); +void wxGridCellAttrProvider::SetColAttr(wxGridCellAttr *attr, int col) +{ + if ( !m_data ) + InitData(); - // we just registered it, it's the last one - index = m_typeinfo.GetCount() - 1; - } + m_data->m_colAttrs.SetAttr(attr, col); +} - return index; +void wxGridCellAttrProvider::UpdateAttrRows( size_t pos, int numRows ) +{ + if ( m_data ) + { + m_data->m_cellAttrs.UpdateAttrRows( pos, numRows ); + + m_data->m_rowAttrs.UpdateAttrRowsOrCols( pos, numRows ); + } } -wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) +void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) { - wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; - if (renderer) - renderer->IncRef(); + if ( m_data ) + { + m_data->m_cellAttrs.UpdateAttrCols( pos, numCols ); - return renderer; + m_data->m_colAttrs.UpdateAttrRowsOrCols( pos, numCols ); + } } -wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) +const wxGridColumnHeaderRenderer& +wxGridCellAttrProvider::GetColumnHeaderRenderer(int WXUNUSED(col)) { - wxGridCellEditor* editor = m_typeinfo[index]->m_editor; - if (editor) - editor->IncRef(); + return gs_defaultHeaderRenderers.colRenderer; +} - return editor; +const wxGridRowHeaderRenderer& +wxGridCellAttrProvider::GetRowHeaderRenderer(int WXUNUSED(row)) +{ + return gs_defaultHeaderRenderers.rowRenderer; +} + +const wxGridCornerHeaderRenderer& wxGridCellAttrProvider::GetCornerRenderer() +{ + return gs_defaultHeaderRenderers.cornerRenderer; } // ---------------------------------------------------------------------------- @@ -3185,8 +1014,8 @@ IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject ) wxGridTableBase::wxGridTableBase() { - m_view = (wxGrid *) NULL; - m_attrProvider = (wxGridCellAttrProvider *) NULL; + m_view = NULL; + m_attrProvider = NULL; } wxGridTableBase::~wxGridTableBase() @@ -3216,7 +1045,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) @@ -3333,7 +1162,7 @@ wxString wxGridTableBase::GetColLabelValue( int col ) unsigned int i, n; for ( n = 1; ; n++ ) { - s += (wxChar) (_T('A') + (wxChar)(col % 26)); + s += (wxChar) (wxT('A') + (wxChar)(col % 26)); col = col / 26 - 1; if ( col < 0 ) break; @@ -3415,7 +1244,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; @@ -3443,11 +1272,14 @@ IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase ) wxGridStringTable::wxGridStringTable() : wxGridTableBase() { + m_numCols = 0; } wxGridStringTable::wxGridStringTable( int numRows, int numCols ) : wxGridTableBase() { + m_numCols = numCols; + m_data.Alloc( numRows ); wxArrayString sa; @@ -3457,49 +1289,25 @@ wxGridStringTable::wxGridStringTable( int numRows, int numCols ) m_data.Add( sa, numRows ); } -wxGridStringTable::~wxGridStringTable() -{ -} - -int wxGridStringTable::GetNumberRows() -{ - return m_data.GetCount(); -} - -int wxGridStringTable::GetNumberCols() -{ - if ( m_data.GetCount() > 0 ) - return m_data[0].GetCount(); - else - return 0; -} - wxString wxGridStringTable::GetValue( int row, int col ) { - wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()), + wxCHECK_MSG( (row >= 0 && row < GetNumberRows()) && + (col >= 0 && col < GetNumberCols()), wxEmptyString, - _T("invalid row or column index in wxGridStringTable") ); + wxT("invalid row or column index in wxGridStringTable") ); return m_data[row][col]; } void wxGridStringTable::SetValue( int row, int col, const wxString& value ) { - wxCHECK_RET( (row < GetNumberRows()) && (col < GetNumberCols()), - _T("invalid row or column index in wxGridStringTable") ); + wxCHECK_RET( (row >= 0 && row < GetNumberRows()) && + (col >= 0 && col < GetNumberCols()), + wxT("invalid row or column index in wxGridStringTable") ); 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; @@ -3652,6 +1460,8 @@ bool wxGridStringTable::InsertCols( size_t pos, size_t numCols ) } } + m_numCols += numCols; + if ( GetView() ) { wxGridTableMessage msg( this, @@ -3671,21 +1481,13 @@ bool wxGridStringTable::AppendCols( size_t numCols ) size_t curNumRows = m_data.GetCount(); -#if 0 - if ( !curNumRows ) - { - // TODO: something better than this ? - // - wxFAIL_MSG( wxT("Unable to append cols to a grid table with no rows.\nCall AppendRows() first") ); - return false; - } -#endif - for ( row = 0; row < curNumRows; row++ ) { m_data[row].Add( wxEmptyString, numCols ); } + m_numCols += numCols; + if ( GetView() ) { wxGridTableMessage msg( this, @@ -3734,21 +1536,28 @@ bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols ) // m_colLabels stores just as many elements as it needs, e.g. if only // the label of the first column had been set it would have only one // element and not numCols, so account for it - int nToRm = m_colLabels.size() - colID; - if ( nToRm > 0 ) - m_colLabels.RemoveAt( colID, nToRm ); + int numRemaining = m_colLabels.size() - colID; + if (numRemaining > 0) + m_colLabels.RemoveAt( colID, wxMin(numCols, numRemaining) ); } - for ( row = 0; row < curNumRows; row++ ) + if ( numCols >= curNumCols ) { - if ( numCols >= curNumCols ) + for ( row = 0; row < curNumRows; row++ ) { m_data[row].Clear(); } - else + + m_numCols = 0; + } + else // something will be left + { + for ( row = 0; row < curNumRows; row++ ) { m_data[row].RemoveAt( colID, numCols ); } + + m_numCols -= numCols; } if ( GetView() ) @@ -3837,22 +1646,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); @@ -3879,27 +1678,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); @@ -3918,131 +1708,375 @@ void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) else dc.SetDeviceOrigin( pt.x-x, pt.y ); - wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() ); - m_owner->DrawColLabels( dc, cols ); -} + wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() ); + m_owner->DrawColLabels( dc, cols ); +} + +void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) +{ + m_owner->ProcessColLabelMouseEvent( event ); +} + +void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) +{ + if (!m_owner->GetEventHandler()->ProcessEvent( event )) + event.Skip(); +} + +////////////////////////////////////////////////////////////////////// + +BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow ) + EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel ) + EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent ) + EVT_PAINT( wxGridCornerLabelWindow::OnPaint ) +END_EVENT_TABLE() + +void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) +{ + wxPaintDC dc(this); + + m_owner->DrawCornerLabel(dc); +} + +void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) +{ + m_owner->ProcessCornerLabelMouseEvent( event ); +} + +void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) +{ + if (!m_owner->GetEventHandler()->ProcessEvent(event)) + event.Skip(); +} + +////////////////////////////////////////////////////////////////////// + +BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) + EVT_PAINT( wxGridWindow::OnPaint ) + EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel ) + EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent ) + EVT_KEY_DOWN( wxGridWindow::OnKeyDown ) + EVT_KEY_UP( wxGridWindow::OnKeyUp ) + EVT_CHAR( wxGridWindow::OnChar ) + EVT_SET_FOCUS( wxGridWindow::OnFocus ) + EVT_KILL_FOCUS( wxGridWindow::OnFocus ) + EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground ) +END_EVENT_TABLE() + +void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) +{ + wxPaintDC dc( this ); + m_owner->PrepareDC( dc ); + wxRegion reg = GetUpdateRegion(); + wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); + m_owner->DrawGridCellArea( dc, dirtyCells ); + + m_owner->DrawGridSpace( dc ); + + m_owner->DrawAllGridLines( dc, reg ); + + m_owner->DrawHighlight( dc, dirtyCells ); +} + +void wxGrid::Render( wxDC& dc, + const wxPoint& position, + const wxSize& size, + const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight, + int style ) +{ + wxCHECK_RET( bottomRight.GetCol() < GetNumberCols(), + "Invalid right column" ); + wxCHECK_RET( bottomRight.GetRow() < GetNumberRows(), + "Invalid bottom row" ); + + // store user settings and reset later + + // remove grid selection, don't paint selection colour + // unless we have wxGRID_DRAW_SELECTION + // block selections are the only ones catered for here + wxGridCellCoordsArray selectedCells; + bool hasSelection = IsSelection(); + if ( hasSelection && !( style & wxGRID_DRAW_SELECTION ) ) + { + selectedCells = GetSelectionBlockTopLeft(); + // non block selections may not have a bottom right + if ( GetSelectionBlockBottomRight().size() ) + selectedCells.Add( GetSelectionBlockBottomRight()[ 0 ] ); + + ClearSelection(); + } + + // store user device origin + wxCoord userOriginX, userOriginY; + dc.GetDeviceOrigin( &userOriginX, &userOriginY ); + + // store user scale + double scaleUserX, scaleUserY; + dc.GetUserScale( &scaleUserX, &scaleUserY ); + + // set defaults if necessary + wxGridCellCoords leftTop( topLeft ), rightBottom( bottomRight ); + if ( leftTop.GetCol() < 0 ) + leftTop.SetCol(0); + if ( leftTop.GetRow() < 0 ) + leftTop.SetRow(0); + if ( rightBottom.GetCol() < 0 ) + rightBottom.SetCol(GetNumberCols() - 1); + if ( rightBottom.GetRow() < 0 ) + rightBottom.SetRow(GetNumberRows() - 1); + + // get grid offset, size and cell parameters + wxPoint pointOffSet; + wxSize sizeGrid; + wxGridCellCoordsArray renderCells; + wxArrayInt arrayCols; + wxArrayInt arrayRows; + + GetRenderSizes( leftTop, rightBottom, + pointOffSet, sizeGrid, + renderCells, + arrayCols, arrayRows ); + + // add headers/labels to dimensions + if ( style & wxGRID_DRAW_ROWS_HEADER ) + sizeGrid.x += GetRowLabelSize(); + if ( style & wxGRID_DRAW_COLS_HEADER ) + sizeGrid.y += GetColLabelSize(); + + // get render start position in logical units + wxPoint positionRender = GetRenderPosition( dc, position ); + + wxCoord originX = dc.LogicalToDeviceX( positionRender.x ); + wxCoord originY = dc.LogicalToDeviceY( positionRender.y ); + + dc.SetDeviceOrigin( originX, originY ); + + SetRenderScale( dc, positionRender, size, sizeGrid ); + + // draw row headers at specified origin + if ( GetRowLabelSize() > 0 && ( style & wxGRID_DRAW_ROWS_HEADER ) ) + { + if ( style & wxGRID_DRAW_COLS_HEADER ) + { + DrawCornerLabel( dc ); // do only if both col and row labels drawn + originY += dc.LogicalToDeviceYRel( GetColLabelSize() ); + } + + originY -= dc.LogicalToDeviceYRel( pointOffSet.y ); + dc.SetDeviceOrigin( originX, originY ); + + DrawRowLabels( dc, arrayRows ); + + // reset for columns + if ( style & wxGRID_DRAW_COLS_HEADER ) + originY -= dc.LogicalToDeviceYRel( GetColLabelSize() ); + + originY += dc.LogicalToDeviceYRel( pointOffSet.y ); + // X offset so we don't overwrite row labels + originX += dc.LogicalToDeviceXRel( GetRowLabelSize() ); + } + + // subtract col offset where startcol > 0 + originX -= dc.LogicalToDeviceXRel( pointOffSet.x ); + // no y offset for col labels, they are at the Y origin + + // draw column labels + if ( style & wxGRID_DRAW_COLS_HEADER ) + { + dc.SetDeviceOrigin( originX, originY ); + DrawColLabels( dc, arrayCols ); + // don't overwrite the labels, increment originY + originY += dc.LogicalToDeviceYRel( GetColLabelSize() ); + } + + // set device origin to draw grid cells and lines + originY -= dc.LogicalToDeviceYRel( pointOffSet.y ); + dc.SetDeviceOrigin( originX, originY ); + + // draw cell area background + dc.SetBrush( GetDefaultCellBackgroundColour() ); + dc.SetPen( *wxTRANSPARENT_PEN ); + // subtract headers from grid area dimensions + wxSize sizeCells( sizeGrid ); + if ( style & wxGRID_DRAW_ROWS_HEADER ) + sizeCells.x -= GetRowLabelSize(); + if ( style & wxGRID_DRAW_COLS_HEADER ) + sizeCells.y -= GetColLabelSize(); + + dc.DrawRectangle( pointOffSet, sizeCells ); + + // draw cells + DrawGridCellArea( dc, renderCells ); + + // draw grid lines + if ( style & wxGRID_DRAW_CELL_LINES ) + { + wxRegion regionClip( pointOffSet.x, pointOffSet.y, + sizeCells.x, sizeCells.y ); + + DrawRangeGridLines(dc, regionClip, renderCells[0], renderCells.Last()); + } + + // draw render rectangle bounding lines + DoRenderBox( dc, style, + pointOffSet, sizeCells, + leftTop, rightBottom ); -void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) -{ - m_owner->ProcessColLabelMouseEvent( event ); + // restore user setings + dc.SetDeviceOrigin( userOriginX, userOriginY ); + dc.SetUserScale( scaleUserX, scaleUserY ); + + if ( selectedCells.size() && !( style & wxGRID_DRAW_SELECTION ) ) + { + SelectBlock( selectedCells[ 0 ].GetRow(), + selectedCells[ 0 ].GetCol(), + selectedCells[ selectedCells.size() -1 ].GetRow(), + selectedCells[ selectedCells.size() -1 ].GetCol() ); + } } -void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) +void +wxGrid::SetRenderScale(wxDC& dc, + const wxPoint& pos, const wxSize& size, + const wxSize& sizeGrid ) { - m_owner->GetEventHandler()->ProcessEvent( event ); -} + double scaleX, scaleY; + wxSize sizeTemp; -////////////////////////////////////////////////////////////////////// + if ( size.GetWidth() != wxDefaultSize.GetWidth() ) // size.x was specified + sizeTemp.SetWidth( size.GetWidth() ); + else + sizeTemp.SetWidth( dc.DeviceToLogicalXRel( dc.GetSize().GetWidth() ) + - pos.x ); -IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow ) + if ( size.GetHeight() != wxDefaultSize.GetHeight() ) // size.y was specified + sizeTemp.SetHeight( size.GetHeight() ); + else + sizeTemp.SetHeight( dc.DeviceToLogicalYRel( dc.GetSize().GetHeight() ) + - pos.y ); -BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow ) - EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel ) - EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent ) - EVT_PAINT( wxGridCornerLabelWindow::OnPaint ) -END_EVENT_TABLE() + scaleX = (double)( (double) sizeTemp.GetWidth() / (double) sizeGrid.GetWidth() ); + scaleY = (double)( (double) sizeTemp.GetHeight() / (double) sizeGrid.GetHeight() ); -wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; + dc.SetUserScale( wxMin( scaleX, scaleY), wxMin( scaleX, scaleY ) ); } -void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) +// get grid rendered size, origin offset and fill cell arrays +void wxGrid::GetRenderSizes( const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight, + wxPoint& pointOffSet, wxSize& sizeGrid, + wxGridCellCoordsArray& renderCells, + wxArrayInt& arrayCols, wxArrayInt& arrayRows ) { - wxPaintDC dc(this); + pointOffSet.x = 0; + pointOffSet.y = 0; + sizeGrid.SetWidth( 0 ); + sizeGrid.SetHeight( 0 ); - int client_height = 0; - int client_width = 0; - GetClientSize( &client_width, &client_height ); + int col, row; - // VZ: any reason for this ifdef? (FIXME) -#if 0 -def __WXGTK__ - wxRect rect; - rect.SetX( 1 ); - rect.SetY( 1 ); - rect.SetWidth( client_width - 2 ); - rect.SetHeight( client_height - 2 ); - - wxRendererNative::Get().DrawHeaderButton( this, dc, rect, 0 ); -#else // !__WXGTK__ - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); - dc.DrawLine( client_width - 1, client_height - 1, client_width - 1, 0 ); - dc.DrawLine( client_width - 1, client_height - 1, 0, client_height - 1 ); - dc.DrawLine( 0, 0, client_width, 0 ); - dc.DrawLine( 0, 0, 0, client_height ); - - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 1, 1, client_width - 1, 1 ); - dc.DrawLine( 1, 1, 1, client_height - 1 ); -#endif -} + wxGridSizesInfo sizeinfo = GetColSizes(); + for ( col = 0; col <= bottomRight.GetCol(); col++ ) + { + if ( col < topLeft.GetCol() ) + { + pointOffSet.x += sizeinfo.GetSize( col ); + } + else + { + for ( row = topLeft.GetRow(); row <= bottomRight.GetRow(); row++ ) + { + renderCells.Add( wxGridCellCoords( row, col )); + arrayRows.Add( row ); // column labels rendered in DrawColLabels + } + arrayCols.Add( col ); // row labels rendered in DrawRowLabels + sizeGrid.x += sizeinfo.GetSize( col ); + } + } -void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) -{ - m_owner->ProcessCornerLabelMouseEvent( event ); + sizeinfo = GetRowSizes(); + for ( row = 0; row <= bottomRight.GetRow(); row++ ) + { + if ( row < topLeft.GetRow() ) + pointOffSet.y += sizeinfo.GetSize( row ); + else + sizeGrid.y += sizeinfo.GetSize( row ); + } } -void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) +// get render start position +// if position not specified use dc draw extents MaxX and MaxY +wxPoint wxGrid::GetRenderPosition( wxDC& dc, const wxPoint& position ) { - m_owner->GetEventHandler()->ProcessEvent(event); -} - -////////////////////////////////////////////////////////////////////// + wxPoint positionRender( position ); -IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow ) + if ( !positionRender.IsFullySpecified() ) + { + if ( positionRender.x == wxDefaultPosition.x ) + positionRender.x = dc.MaxX(); -BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) - EVT_PAINT( wxGridWindow::OnPaint ) - EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel ) - EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent ) - EVT_KEY_DOWN( wxGridWindow::OnKeyDown ) - EVT_KEY_UP( wxGridWindow::OnKeyUp ) - EVT_CHAR( wxGridWindow::OnChar ) - EVT_SET_FOCUS( wxGridWindow::OnFocus ) - EVT_KILL_FOCUS( wxGridWindow::OnFocus ) - EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground ) -END_EVENT_TABLE() + if ( positionRender.y == wxDefaultPosition.y ) + positionRender.y = dc.MaxY(); + } -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; + return positionRender; } -void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) +// draw render rectangle bounding lines +// useful where there is multi cell row or col clipping and no cell border +void wxGrid::DoRenderBox( wxDC& dc, const int& style, + const wxPoint& pointOffSet, + const wxSize& sizeCells, + const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight ) { - wxPaintDC dc( this ); - m_owner->PrepareDC( dc ); - wxRegion reg = GetUpdateRegion(); - wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); - m_owner->DrawGridCellArea( dc, dirtyCells ); + if ( !( style & wxGRID_DRAW_BOX_RECT ) ) + return; -#if WXGRID_DRAW_LINES - m_owner->DrawAllGridLines( dc, reg ); -#endif + int bottom = pointOffSet.y + sizeCells.GetY(), + right = pointOffSet.x + sizeCells.GetX() - 1; - m_owner->DrawGridSpace( dc ); - m_owner->DrawHighlight( dc, dirtyCells ); + // horiz top line if we are not drawing column header/labels + if ( !( style & wxGRID_DRAW_COLS_HEADER ) ) + { + int left = pointOffSet.x; + left += ( style & wxGRID_DRAW_COLS_HEADER ) + ? - GetRowLabelSize() : 0; + dc.SetPen( GetRowGridLinePen( topLeft.GetRow() ) ); + dc.DrawLine( left, + pointOffSet.y, + right, + pointOffSet.y ); + } + + // horiz bottom line + dc.SetPen( GetRowGridLinePen( bottomRight.GetRow() ) ); + dc.DrawLine( pointOffSet.x, bottom - 1, right, bottom - 1 ); + + // left vertical line if we are not drawing row header/labels + if ( !( style & wxGRID_DRAW_ROWS_HEADER ) ) + { + int top = pointOffSet.y; + top += ( style & wxGRID_DRAW_COLS_HEADER ) + ? - GetColLabelSize() : 0; + dc.SetPen( GetColGridLinePen( topLeft.GetCol() ) ); + dc.DrawLine( pointOffSet.x -1, + top, + pointOffSet.x - 1, + bottom - 1 ); + } + + // right vertical line + dc.SetPen( GetColGridLinePen( bottomRight.GetCol() ) ); + dc.DrawLine( right, pointOffSet.y, right, bottom - 1 ); } 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 ) @@ -4055,7 +2089,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 @@ -4112,78 +2147,11 @@ 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) ///////////////////////////////////////////////////////////////////// -#if wxUSE_EXTENDED_RTTI -WX_DEFINE_FLAGS( wxGridStyle ) - -wxBEGIN_FLAGS( wxGridStyle ) - // new style border flags, we put them first to - // use them for streaming out - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - -wxEND_FLAGS( wxGridStyle ) - -IMPLEMENT_DYNAMIC_CLASS_XTI(wxGrid, wxScrolledWindow,"wx/grid.h") - -wxBEGIN_PROPERTIES_TABLE(wxGrid) - wxHIDE_PROPERTY( Children ) - wxPROPERTY_FLAGS( WindowStyle , wxGridStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxGrid) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_5( wxGrid , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) - -/* - TODO : Expose more information of a list's layout, etc. via appropriate objects (e.g., NotebookPageInfo) -*/ -#else -IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow ) -#endif - BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow ) EVT_PAINT( wxGrid::OnPaint ) EVT_SIZE( wxGrid::OnSize ) @@ -4193,22 +2161,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) @@ -4222,7 +2174,6 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, Create(); SetInitialSize(size); - SetScrollRate(m_scrollLineX, m_scrollLineY); CalcDimensions(); return true; @@ -4230,6 +2181,14 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, wxGrid::~wxGrid() { + if ( m_winCapture ) + m_winCapture->ReleaseMouse(); + + // Ensure that the editor control is destroyed before the grid is, + // otherwise we crash later when the editor tries to do something with the + // half destroyed grid + HideCellEditControl(); + // Must do this or ~wxScrollHelper will pop the wrong event handler SetTargetWindow(this); ClearAttrCache(); @@ -4237,7 +2196,7 @@ wxGrid::~wxGrid() #ifdef DEBUG_ATTR_CACHE size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses; - wxPrintf(_T("wxGrid attribute cache statistics: " + wxPrintf(wxT("wxGrid attribute cache statistics: " "total: %u, hits: %u (%u%%)\n"), total, gs_nAttrCacheHits, total ? (gs_nAttrCacheHits*100) / total : 0); @@ -4252,6 +2211,9 @@ wxGrid::~wxGrid() delete m_typeRegistry; delete m_selection; + + delete m_setFixedRows; + delete m_setFixedCols; } // @@ -4297,31 +2259,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 ); @@ -4341,38 +2283,51 @@ void wxGrid::Create() m_cornerLabelWin->SetOwnBackgroundColour(lbg); m_rowLabelWin->SetOwnForegroundColour(lfg); m_rowLabelWin->SetOwnBackgroundColour(lbg); - m_colLabelWin->SetOwnForegroundColour(lfg); - m_colLabelWin->SetOwnBackgroundColour(lbg); + m_colWindow->SetOwnForegroundColour(lfg); + m_colWindow->SetOwnBackgroundColour(lbg); m_gridWin->SetOwnForegroundColour(gfg); m_gridWin->SetOwnBackgroundColour(gbg); - Init(); + m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); + m_labelTextColour = m_rowLabelWin->GetForegroundColour(); + + // now that we have the grid window, use its font to compute the default + // row height + m_defaultRowHeight = m_gridWin->GetCharHeight(); +#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl() + m_defaultRowHeight += 8; +#else + m_defaultRowHeight += 4; +#endif + +} + +void wxGrid::CreateColumnWindow() +{ + if ( m_useNativeHeader ) + { + m_colWindow = new wxGridHeaderCtrl(this); + m_colLabelHeight = m_colWindow->GetBestSize().y; + } + else // draw labels ourselves + { + m_colWindow = new wxGridColLabelWindow(this); + m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + } } bool wxGrid::CreateGrid( int numRows, int numCols, - wxGrid::wxGridSelectionModes selmode ) + wxGridSelectionModes selmode ) { wxCHECK_MSG( !m_created, false, wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") ); - m_numRows = numRows; - m_numCols = numCols; - - m_table = new wxGridStringTable( m_numRows, m_numCols ); - m_table->SetView( this ); - m_ownTable = true; - m_selection = new wxGridSelection( this, selmode ); - - CalcDimensions(); - - m_created = true; - - return m_created; + return SetTable(new wxGridStringTable(numRows, numCols), true, selmode); } -void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) +void wxGrid::SetSelectionMode(wxGridSelectionModes selmode) { wxCHECK_RET( m_created, wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") ); @@ -4382,14 +2337,16 @@ void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode) wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const { - wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells, + wxCHECK_MSG( m_created, wxGridSelectCells, wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") ); return m_selection->GetSelectionMode(); } -bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, - wxGrid::wxGridSelectionModes selmode ) +bool +wxGrid::SetTable(wxGridTableBase *table, + bool takeOwnership, + wxGrid::wxGridSelectionModes selmode ) { bool checkSelection = false; if ( m_created ) @@ -4405,8 +2362,7 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, m_table = NULL; } - delete m_selection; - m_selection = NULL; + wxDELETE(m_selection); m_ownTable = false; m_numRows = 0; @@ -4425,6 +2381,9 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, m_numRows = table->GetNumberRows(); m_numCols = table->GetNumberCols(); + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + m_table = table; m_table->SetView( this ); m_ownTable = takeOwnership; @@ -4434,38 +2393,40 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, // If the newly set table is smaller than the // original one current cell and selection regions // might be invalid, - m_selectingKeyboard = wxGridNoCellCoords; + m_selectedBlockCorner = wxGridNoCellCoords; m_currentCellCoords = wxGridCellCoords(wxMin(m_numRows, m_currentCellCoords.GetRow()), wxMin(m_numCols, m_currentCellCoords.GetCol())); - if (m_selectingTopLeft.GetRow() >= m_numRows || - m_selectingTopLeft.GetCol() >= m_numCols) + if (m_selectedBlockTopLeft.GetRow() >= m_numRows || + m_selectedBlockTopLeft.GetCol() >= m_numCols) { - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; } else - m_selectingBottomRight = + m_selectedBlockBottomRight = wxGridCellCoords(wxMin(m_numRows, - m_selectingBottomRight.GetRow()), + m_selectedBlockBottomRight.GetRow()), wxMin(m_numCols, - m_selectingBottomRight.GetCol())); + m_selectedBlockBottomRight.GetCol())); } CalcDimensions(); m_created = true; } + InvalidateBestSize(); + 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; @@ -4475,32 +2436,19 @@ 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; + m_setFixedRows = + m_setFixedCols = NULL; // init attr cache m_attrCache.row = -1; m_attrCache.col = -1; m_attrCache.attr = NULL; - // TODO: something better than this ? - // - m_labelFont = this->GetFont(); + m_labelFont = GetFont(); m_labelFont.SetWeight( wxBOLD ); m_rowLabelHorizAlign = wxALIGN_CENTRE; @@ -4511,19 +2459,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; @@ -4531,7 +2475,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; @@ -4540,6 +2484,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; @@ -4549,7 +2498,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); @@ -4562,8 +2513,13 @@ void wxGrid::Init() m_extraWidth = m_extraHeight = 0; - m_scrollLineX = GRID_SCROLL_LINE_X; - m_scrollLineY = GRID_SCROLL_LINE_Y; + // we can't call SetScrollRate() as the window isn't created yet but OTOH + // we don't need to call it neither as the scroll position is (0, 0) right + // now anyhow, so just set the parameters directly + m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X; + m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y; + + m_tabBehaviour = Tab_Stop; } // ---------------------------------------------------------------------------- @@ -4605,23 +2561,28 @@ 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 ); } } int wxGrid::GetColWidth(int col) const { - return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col]; + if ( m_colWidths.IsEmpty() ) + return m_defaultColWidth; + + // a negative width indicates a hidden column + return m_colWidths[col] > 0 ? m_colWidths[col] : 0; } int wxGrid::GetColLeft(int col) const { - return m_colRights.IsEmpty() ? GetColPos( col ) * m_defaultColWidth - : m_colRights[col] - m_colWidths[col]; + if ( m_colRights.IsEmpty() ) + return GetColPos( col ) * m_defaultColWidth; + + return m_colRights[col] - GetColWidth(col); } int wxGrid::GetColRight(int col) const @@ -4632,13 +2593,20 @@ int wxGrid::GetColRight(int col) const int wxGrid::GetRowHeight(int row) const { - return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row]; + // no custom heights / hidden rows + if ( m_rowHeights.IsEmpty() ) + return m_defaultRowHeight; + + // a negative height indicates a hidden row + return m_rowHeights[row] > 0 ? m_rowHeights[row] : 0; } int wxGrid::GetRowTop(int row) const { - return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight - : m_rowBottoms[row] - m_rowHeights[row]; + if ( m_rowBottoms.IsEmpty() ) + return row * m_defaultRowHeight; + + return m_rowBottoms[row] - GetRowHeight(row); } int wxGrid::GetRowBottom(int row) const @@ -4730,8 +2698,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 ); @@ -4759,20 +2727,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: @@ -4923,10 +2877,12 @@ 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 - int i; for ( i = 0; i < m_numCols - numCols; i++ ) { if ( m_colAt[i] >= (int)pos ) @@ -4977,7 +2933,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4988,13 +2944,14 @@ 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() ) { m_colAt.Add( 0, numCols ); //Set the new columns' positions - int i; for ( i = oldNumCols; i < m_numCols; i++ ) { m_colAt[i] = i; @@ -5030,7 +2987,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -5041,6 +2998,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() ) { @@ -5105,13 +3064,15 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; break; } + InvalidateBestSize(); + if (result && !GetBatchCount() ) m_gridWin->Refresh(); @@ -5225,16 +3186,20 @@ wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const { - wxRegionIterator iter( reg ); wxRect r; wxGridCellCoordsArray cellsExposed; int left, top, right, bottom; - while ( iter ) + for ( wxRegionIterator iter(reg); iter; ++iter ) { r = iter.GetRect(); + // Skip 0-height cells, they're invisible anyhow, don't waste time + // getting their rectangles and so on. + if ( !r.GetHeight() ) + continue; + // TODO: remove this when we can... // There is a bug in wxMotif that gives garbage update // rectangles if you jump-scroll a long way by clicking the @@ -5255,9 +3220,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; @@ -5265,22 +3229,24 @@ 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 ) ); } - } - ++iter; + const size_t count = cols.size(); + for ( size_t n = 0; n < count; n++ ) + cellsExposed.Add(wxGridCellCoords(row, cols[n])); + } } return cellsExposed; @@ -5331,13 +3297,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; @@ -5372,11 +3332,12 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.LeftDown() ) { - // don't send a label click event for a hit on the - // edge of the row label - this is probably the user - // wanting to resize the row - // - if ( YToEdgeOfRow(y) < 0 ) + row = YToEdgeOfRow(y); + if ( row != wxNOT_FOUND && CanDragRowSize(row) ) + { + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); + } + else // not a request to start resizing { row = YToRow(y); if ( row >= 0 && @@ -5388,34 +3349,22 @@ 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); } } ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin); } } - else - { - // starting to drag-resize a row - if ( CanDragRowSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); - } } // ------------ Left double click @@ -5423,7 +3372,19 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if (event.LeftDClick() ) { row = YToEdgeOfRow(y); - if ( row < 0 ) + if ( row != wxNOT_FOUND && CanDragRowSize(row) ) + { + // adjust row height depending on label text + // + // TODO: generate RESIZING event, see #10754 + AutoSizeRowLabelSize( row ); + + SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, row, -1, event); + + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); + m_dragLastPos = -1; + } + else // not on row separator or it's not resizable { row = YToRow(y); if ( row >=0 && @@ -5432,14 +3393,6 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // no default action at the moment } } - else - { - // adjust row height depending on label text - AutoSizeRowLabelSize( row ); - - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); - m_dragLastPos = -1; - } } // ------------ Left button released @@ -5447,14 +3400,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if ( event.LeftUp() ) { if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) - { - DoEndDragResizeRow(); - - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); - } + DoEndDragResizeRow(event); ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin); m_dragLastPos = -1; @@ -5488,38 +3434,122 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.Moving() ) { - m_dragRowOrCol = YToEdgeOfRow( y ); - if ( m_dragRowOrCol >= 0 ) - { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - // don't capture the mouse yet - if ( CanDragRowSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false); - } - } - else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false); - } + m_dragRowOrCol = YToEdgeOfRow( y ); + if ( m_dragRowOrCol != wxNOT_FOUND ) + { + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + { + if ( CanDragRowSize(m_dragRowOrCol) ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false); + } + } + else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false); + } + } +} + +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; - wxPoint pos( event.GetPosition() ); - CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); + int x; + CalcUnscrolledPosition( event.GetPosition().x, 0, &x, NULL ); + 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() ) @@ -5527,69 +3557,45 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - { - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); - - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - - x = wxMax( x, GetColLeft(m_dragRowOrCol) + - GetColMinimalWidth(m_dragRowOrCol)); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - } - dc.DrawLine( x, top, x, top + ch ); - m_dragLastPos = x; - } + DoUpdateResizeCol(x); break; case WXGRID_CURSOR_SELECT_COL: { - if ( (col = XToCol( x )) >= 0 ) + if ( col != -1 ) { if ( m_selection ) - { - m_selection->SelectCol( col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->SelectCol(col, event); } } break; case WXGRID_CURSOR_MOVE_COL: { - if ( x < 0 ) - m_moveToCol = GetColAt( 0 ); - else - m_moveToCol = XToCol( x ); + int posNew = XToPos(x); + int colNew = GetColAt(posNew); + // determine the position of the drop marker int markerX; - - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); + if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) ) + markerX = GetColRight(colNew); else - markerX = GetColLeft( m_moveToCol ); + markerX = GetColLeft(colNew); if ( markerX != m_dragLastPos ) { - wxClientDC dc( m_colLabelWin ); + wxClientDC dc( GetColLabelWindow() ); + DoPrepareDC(dc); int cw, ch; - m_colLabelWin->GetClientSize( &cw, &ch ); + GetColLabelWindow()->GetClientSize( &cw, &ch ); markerX++; //Clean up the last indicator if ( m_dragLastPos >= 0 ) { - wxPen pen( m_colLabelWin->GetBackgroundColour(), 2 ); + wxPen pen( GetColLabelWindow()->GetBackgroundColour(), 2 ); dc.SetPen(pen); dc.DrawLine( m_dragLastPos + 1, 0, m_dragLastPos + 1, ch ); dc.SetPen(wxNullPen); @@ -5598,17 +3604,15 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) DrawColLabel( dc, XToCol( m_dragLastPos ) ); } + const wxColour *color; //Moving to the same place? Don't draw a marker - if ( (m_moveToCol == m_dragRowOrCol) - || (GetColPos( m_moveToCol ) == GetColPos( m_dragRowOrCol ) + 1) - || (m_moveToCol < 0 && m_dragRowOrCol == GetColAt( m_numCols - 1 ))) - { - m_dragLastPos = -1; - return; - } + if ( colNew == m_dragRowOrCol ) + color = wxLIGHT_GREY; + else + color = wxBLUE; //Draw the marker - wxPen pen( *wxBLUE, 2 ); + wxPen pen( *color, 2 ); dc.SetPen(pen); dc.DrawLine( markerX, 0, markerX, ch ); @@ -5634,8 +3638,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) if (m_isDragging) { - if (m_colLabelWin->HasCapture()) - m_colLabelWin->ReleaseMouse(); + if (GetColLabelWindow()->HasCapture()) + GetColLabelWindow()->ReleaseMouse(); m_isDragging = false; } @@ -5643,34 +3647,34 @@ 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 // else if ( event.LeftDown() ) { - // don't send a label click event for a hit on the - // edge of the col label - this is probably the user - // wanting to resize the col - // - if ( XToEdgeOfCol(x) < 0 ) + int colEdge = XToEdgeOfCol(x); + if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) ) + { + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); + } + else // not a request to start resizing { - 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 { @@ -5680,45 +3684,32 @@ 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()); } } } - else - { - // starting to drag-resize a col - // - if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin); - } } // ------------ Left double click // 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 ) ) { @@ -5728,9 +3719,14 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) else { // adjust column width depending on label text - AutoSizeColLabelSize( col ); + // + // TODO: generate RESIZING event, see #10754 + if ( !SendGridSizeEvent(wxEVT_GRID_COL_AUTO_SIZE, -1, colEdge, event) ) + AutoSizeColLabelSize( colEdge ); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, colEdge, event); + + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } } @@ -5742,29 +3738,60 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { 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 ); + DoEndDragResizeCol(event); break; case WXGRID_CURSOR_MOVE_COL: - DoEndDragMoveCol(); + 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 + { + // get the position of the column we're over + int pos = XToPos(x); + + // we may need to adjust the drop position but don't bother + // checking for it if we can't anyhow + if ( pos > 1 ) + { + // also find the index of the column we're over: notice + // that the existing "col" variable may be invalid but + // we need a valid one here + const int colValid = GetColAt(pos); + + // if we're on the "near" (usually left but right in + // RTL case) part of the column, the actual position we + // should be placed in is actually the one before it + bool onNearPart; + const int middle = GetColLeft(colValid) + + GetColWidth(colValid)/2; + if ( GetLayoutDirection() == wxLayout_LeftToRight ) + onNearPart = (x <= middle); + else // wxLayout_RightToLeft + onNearPart = (x > middle); + + if ( onNearPart ) + pos--; + } - SendEvent( wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol, event ); + DoEndMoveCol(pos); + } 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; } @@ -5772,7 +3799,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 ) ) { @@ -5784,7 +3810,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 ) ) { @@ -5801,14 +3826,13 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // don't capture the cursor yet - if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, false); + if ( CanDragColSize(m_dragRowOrCol) ) + 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); } } } @@ -5851,6 +3875,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; @@ -5864,24 +3890,24 @@ void wxGrid::ChangeCursorMode(CursorMode mode, wxWindow *win, bool captureMouse) { -#ifdef __WXDEBUG__ - static const wxChar *cursorModes[] = +#if wxUSE_LOG_TRACE + static const wxChar *const cursorModes[] = { - _T("SELECT_CELL"), - _T("RESIZE_ROW"), - _T("RESIZE_COL"), - _T("SELECT_ROW"), - _T("SELECT_COL"), - _T("MOVE_COL"), + wxT("SELECT_CELL"), + wxT("RESIZE_ROW"), + wxT("RESIZE_COL"), + wxT("SELECT_ROW"), + wxT("SELECT_COL"), + wxT("MOVE_COL"), }; - wxLogTrace(_T("grid"), - _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"), - win == m_colLabelWin ? _T("colLabelWin") - : win ? _T("rowLabelWin") - : _T("gridWin"), + wxLogTrace(wxT("grid"), + wxT("wxGrid cursor mode (mouse capture for %s): %s -> %s"), + win == m_colWindow ? wxT("colLabelWin") + : win ? wxT("rowLabelWin") + : wxT("gridWin"), cursorModes[m_cursorMode], cursorModes[mode]); -#endif +#endif // wxUSE_LOG_TRACE if ( mode == m_cursorMode && win == m_winCapture && @@ -5896,9 +3922,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; @@ -5933,579 +3958,542 @@ 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 ); +bool +wxGrid::DoGridCellDrag(wxMouseEvent& event, + const wxGridCellCoords& coords, + bool isFirstDrag) +{ + bool performDefault = true ; + + if ( coords == wxGridNoCellCoords ) + return performDefault; // 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()); + case wxMOD_CONTROL: + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; + UpdateBlockBeingSelected(m_selectedBlockCorner, coords); + break; - // 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) + case wxMOD_NONE: + if ( CanDragCell() ) { - m_startDragPos = pos; - return; + if ( isFirstDrag ) + { + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; + + // if event is handled by user code, no further processing + if ( SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event) != 0 ) + performDefault = false; + + return performDefault; + } } - 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(); - } + UpdateBlockBeingSelected(m_currentCellCoords, coords); + break; - if ( coords != wxGridNoCellCoords ) - { - if ( event.CmdDown() ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - HighlightBlock( m_selectingKeyboard, coords ); - } - else if ( CanDragCell() ) - { - if ( isFirstDrag ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - - SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG, - coords.GetRow(), - coords.GetCol(), - event ); - return; - } - } - else - { - if ( !IsSelection() ) - { - HighlightBlock( coords, coords ); - } - else - { - HighlightBlock( m_currentCellCoords, coords ); - } - } + default: + // we don't handle the other key modifiers + event.Skip(); + } + + return performDefault; +} - if (! IsVisible(coords)) - { - MakeCellVisible(coords); - // TODO: need to introduce a delay or something here. The - // scrolling is way to fast, at least on MSW - also on GTK. - } - } - // Have we captured the mouse yet? - if (! m_winCapture) - { - m_winCapture = m_gridWin; - m_winCapture->CaptureMouse(); - } +void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper) +{ + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); + const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)), + m_gridWin->GetClientSize()); - } - 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 ); + // erase the previously drawn line, if any + if ( m_dragLastPos >= 0 ) + oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); - 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 ); + // we need the vertical position for rows and horizontal for columns here + m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition())); - 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; + // 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: + // no further handling if handled by user + if ( DoGridCellDrag(event, coords, isFirstDrag) == false ) + return; + break; + + case WXGRID_CURSOR_RESIZE_ROW: + DoGridLineDrag(event, wxGridRowOperations()); + break; + + case WXGRID_CURSOR_RESIZE_COL: + DoGridLineDrag(event, wxGridColumnOperations()); + break; + + default: + event.Skip(); } - else -#endif // 0 - // ------------ Left button pressed - // - if ( event.LeftDown() && coords != wxGridNoCellCoords ) + if ( isFirstDrag ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) - { - if ( !event.CmdDown() ) - ClearSelection(); - if ( event.ShiftDown() ) - { - if ( m_selection ) - { - m_selection->SelectBlock( m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol(), - coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - } - else if ( XToEdgeOfCol(x) < 0 && - YToEdgeOfRow(y) < 0 ) - { - DisableCellEditControl(); - MakeCellVisible( coords ); + wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" ); - if ( event.CmdDown() ) - { - if ( m_selection ) - { - m_selection->ToggleCellSelection( coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; - m_selectingKeyboard = coords; - } - else - { - m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords; - SetCurrentCell( coords ); - if ( m_selection ) - { - if ( m_selection->GetSelectionMode() != - wxGrid::wxGridSelectCells ) - { - HighlightBlock( coords, coords ); - } - } - } - } - } + m_winCapture = m_gridWin; + m_winCapture->CaptureMouse(); } +} - // ------------ Left double click - // - else if ( event.LeftDClick() && coords != wxGridNoCellCoords ) +void +wxGrid::DoGridCellLeftDown(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) ) { - DisableCellEditControl(); + // event handled by user code, no need to do anything here + return; + } + + if ( !event.CmdDown() ) + ClearSelection(); - if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 ) + if ( event.ShiftDown() ) + { + if ( m_selection ) { - 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; - } + m_selection->SelectBlock(m_currentCellCoords, coords, event); + m_selectedBlockCorner = coords; } } - - // ------------ Left button released - // - else if ( event.LeftUp() ) + else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + DisableCellEditControl(); + MakeCellVisible( coords ); + + if ( event.CmdDown() ) { - if (m_winCapture) + if ( m_selection ) { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; + m_selection->ToggleCellSelection(coords, event); } - if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = coords; + } + else + { + if ( m_selection ) { - ClearSelection(); - EnableCellEditControl(); + // In row or column selection mode just clicking on the cell + // should select the row or column containing it: this is more + // convenient for the kinds of controls that use such selection + // mode and is compatible with 2.8 behaviour (see #12062). + switch ( m_selection->GetSelectionMode() ) + { + case wxGridSelectCells: + case wxGridSelectRowsOrColumns: + // nothing to do in these cases + break; - wxGridCellAttr *attr = GetCellAttr(coords); - wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); - editor->StartingClick(); - editor->DecRef(); - attr->DecRef(); + case wxGridSelectRows: + m_selection->SelectRow(coords.GetRow()); + break; - m_waitForSlowClick = false; - } - else if ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords ) - { - if ( m_selection ) - { - m_selection->SelectBlock( m_selectingTopLeft.GetRow(), - m_selectingTopLeft.GetCol(), - m_selectingBottomRight.GetRow(), - m_selectingBottomRight.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + case wxGridSelectColumns: + m_selection->SelectCol(coords.GetCol()); + break; } + } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + m_waitForSlowClick = m_currentCellCoords == coords && + coords != wxGridNoCellCoords; + SetCurrentCell( coords ); + } + } +} - // Show the edit control, if it has been hidden for - // drag-shrinking. - ShowCellEditControl(); - } +void +wxGrid::DoGridCellLeftDClick(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) + { + if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, 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; } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) + } +} + +void +wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) +{ + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + { + if (m_winCapture) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeRow(); + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + } - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); + 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(); + + m_waitForSlowClick = false; } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + else if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeCol(); + if ( m_selection ) + { + m_selection->SelectBlock( m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event ); + } + + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + + // 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(event); + } + else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + DoEndDragResizeCol(event); + } - // 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 ) ) + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + return; + } + + if ( dragRow >= 0 && CanDragGridSize() && CanDragRowSize(dragRow) ) + { + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // no default action at the moment + m_dragRowOrCol = dragRow; + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); } } - - // ------------ Right double click - // - else if ( event.RightDClick() && coords != wxGridNoCellCoords ) + // 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 && + CanDragGridSize() && CanDragColSize(dragCol) ) { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // no default action at the moment + m_dragRowOrCol = dragCol; + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); } } - - // ------------ Moving and no button action - // - else if ( event.Moving() && !event.IsButton() ) + 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) +{ + if ( event.Entering() || event.Leaving() ) + { + // we don't care about these events but we must not reset m_isDragging + // if they happen so return before anything else is done + event.Skip(); + return; + } - // 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; - } + const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); - if ( dragRow >= 0 ) - { - m_dragRowOrCol = dragRow; + // coordinates of the cell under mouse + wxGridCellCoords coords = XYToCell(pos); - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); - } - } - else if ( dragCol >= 0 ) + int cell_rows, cell_cols; + GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); + if ( (cell_rows < 0) || (cell_cols < 0) ) + { + coords.SetRow(coords.GetRow() + cell_rows); + coords.SetCol(coords.GetCol() + cell_cols); + } + + if ( event.Dragging() ) + { + if ( event.LeftIsDown() ) + DoGridDragEvent(event, coords); + else + event.Skip(); + return; + } + + m_isDragging = false; + m_startDragPos = wxDefaultPosition; + + // 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() +// this function returns true only if the size really changed +bool 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 false; - 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); + const int lineSizeOld = oper.GetLineSize(this, m_dragRowOrCol); + oper.SetLineSize(this, m_dragRowOrCol, + wxMax(m_dragLastPos - lineStart, + oper.GetMinimalLineSize(this, m_dragRowOrCol))); + const bool + sizeChanged = oper.GetLineSize(this, m_dragRowOrCol) != lineSizeOld; + + 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; + int line = doper.PosToLine(this, posLineStart); + if ( line >= 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 = doper.PosToLine(this, posLineEnd, true); + for ( ; 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(); + + return sizeChanged; } -void wxGrid::DoEndDragMoveCol() +void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event) { - //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; - } - - int newPos; - if ( m_moveToCol == -1 ) - newPos = m_numCols - 1; - else - { - newPos = GetColPos( m_moveToCol ); - if ( newPos > GetColPos( m_dragRowOrCol ) ) - newPos--; - } + // TODO: generate RESIZING event, see #10754 - SetColPos( m_dragRowOrCol, newPos ); + if ( DoEndDragResizeLine(wxGridRowOperations()) ) + SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event); } -void wxGrid::SetColPos( int colID, int newPos ) +void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event) { - if ( m_colAt.IsEmpty() ) - { - m_colAt.Alloc( m_numCols ); + // TODO: generate RESIZING event, see #10754 - int i; - for ( i = 0; i < m_numCols; i++ ) - { - m_colAt.Add( i ); - } - } + if ( DoEndDragResizeLine(wxGridColumnOperations()) ) + SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event); +} - int oldPos = GetColPos( colID ); +void wxGrid::DoStartMoveCol(int col) +{ + m_dragRowOrCol = col; +} - //Reshuffle the m_colAt array - if ( newPos > oldPos ) - { - int i; - for ( i = oldPos; i < newPos; i++ ) - { - m_colAt[i] = m_colAt[i+1]; - } - } - else - { - int i; - for ( i = oldPos; i > newPos; i-- ) - { - m_colAt[i] = m_colAt[i-1]; - } - } +void wxGrid::DoEndMoveCol(int pos) +{ + wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" ); + + if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 ) + SetColPos(m_dragRowOrCol, pos); + //else: vetoed by user - m_colAt[newPos] = colID; + m_dragRowOrCol = -1; +} - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) +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 colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) + for ( int colPos = 0; colPos < m_numCols; colPos++ ) { int colID = GetColAt( colPos ); @@ -6514,38 +4502,67 @@ void wxGrid::SetColPos( int colID, int newPos ) } } - m_colLabelWin->Refresh(); + // and make the changes visible + if ( m_useNativeHeader ) + { + if ( m_colAt.empty() ) + GetGridColHeader()->ResetColumnsOrder(); + else + GetGridColHeader()->SetColumnsOrder(m_colAt); + } + else + { + m_colWindow->Refresh(); + } m_gridWin->Refresh(); } +void wxGrid::SetColumnsOrder(const wxArrayInt& order) +{ + m_colAt = order; + + RefreshAfterColPosChange(); +} + +void wxGrid::SetColPos(int idx, int pos) +{ + // we're going to need m_colAt now, initialize it if needed + if ( m_colAt.empty() ) + { + m_colAt.reserve(m_numCols); + for ( int i = 0; i < m_numCols; i++ ) + m_colAt.push_back(i); + } + + 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 } @@ -6562,221 +4579,110 @@ bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg ) case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES: return SetModelValues(); - case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: - case wxGRIDTABLE_NOTIFY_ROWS_APPENDED: - case wxGRIDTABLE_NOTIFY_ROWS_DELETED: - case wxGRIDTABLE_NOTIFY_COLS_INSERTED: - case wxGRIDTABLE_NOTIFY_COLS_APPENDED: - case wxGRIDTABLE_NOTIFY_COLS_DELETED: - return Redimension( msg ); - - default: - return false; - } -} - -// 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 -// not to change the number of rows or cols. -// -void wxGrid::ClearGrid() -{ - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - m_table->Clear(); - if (!GetBatchCount()) - m_gridWin->Refresh(); - } -} - -bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->InsertRows( pos, numRows ); - return done; - - // the table will have sent the results of the insert row - // operation to this view object as a grid table message - } - - return false; -} - -bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") ); - return false; - } - - if ( m_table ) - { - bool done = m_table && m_table->AppendRows( numRows ); - return done; + case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: + case wxGRIDTABLE_NOTIFY_ROWS_APPENDED: + case wxGRIDTABLE_NOTIFY_ROWS_DELETED: + case wxGRIDTABLE_NOTIFY_COLS_INSERTED: + case wxGRIDTABLE_NOTIFY_COLS_APPENDED: + case wxGRIDTABLE_NOTIFY_COLS_DELETED: + return Redimension( msg ); - // the table will have sent the results of the append row - // operation to this view object as a grid table message + default: + return false; } - - return false; } -bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) +// The behaviour of this function depends on the grid table class +// Clear() function. For the default wxGridStringTable class the +// behaviour is to replace all cell contents with wxEmptyString but +// not to change the number of rows or cols. +// +void wxGrid::ClearGrid() { - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") ); - return false; - } - if ( m_table ) { if (IsCellEditControlEnabled()) DisableCellEditControl(); - bool done = m_table->DeleteRows( pos, numRows ); - return done; - // the table will have sent the results of the delete row - // operation to this view object as a grid table message + m_table->Clear(); + if (!GetBatchCount()) + m_gridWin->Refresh(); } - - return false; } -bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t), + int pos, int num, bool WXUNUSED(updateLabels) ) { - // TODO: something with updateLabels flag + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") ); + if ( !m_table ) return false; - } - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); + 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)) { - // TODO: something with updateLabels flag + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") ); + if ( !m_table ) 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; + return (m_table->*funcAppend)(num); } -bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) -{ - // TODO: something with updateLabels flag - - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") ); - return false; - } +// ---------------------------------------------------------------------------- +// event generation helpers +// ---------------------------------------------------------------------------- - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); +bool +wxGrid::SendGridSizeEvent(wxEventType type, + int row, int col, + const wxMouseEvent& mouseEv) +{ + int rowOrCol = row == -1 ? col : row; - 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 - } + wxGridSizeEvent gridEvt( GetId(), + type, + this, + rowOrCol, + mouseEv.GetX() + GetRowLabelSize(), + mouseEv.GetY() + GetColLabelSize(), + mouseEv); - return false; + return GetEventHandler()->ProcessEvent(gridEvt); } -// -// ----- 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, + const wxMouseEvent& mouseEv) { bool claimed, vetoed; - if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) - { - int rowOrCol = (row == -1 ? col : row); - - wxGridSizeEvent gridEvt( GetId(), - type, - this, - rowOrCol, - mouseEv.GetX() + GetRowLabelSize(), - mouseEv.GetY() + GetColLabelSize(), - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); - - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } - else if ( type == wxEVT_GRID_RANGE_SELECT ) + if ( type == wxEVT_GRID_RANGE_SELECT ) { // Right now, it should _never_ end up here! 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(); @@ -6800,10 +4706,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(); } @@ -6816,10 +4719,15 @@ int wxGrid::SendEvent( const wxEventType type, mouseEv.GetX() + GetRowLabelSize(), mouseEv.GetY() + GetColLabelSize(), false, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); + + if ( type == wxEVT_GRID_CELL_BEGIN_DRAG ) + { + // by default the dragging is not supported, the user code must + // explicitly allow the event for it to take place + gridEvt.Veto(); + } + claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); } @@ -6831,33 +4739,18 @@ 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; - - if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE ) - { - int rowOrCol = (row == -1 ? col : row); - - wxGridSizeEvent gridEvt( GetId(), type, this, rowOrCol ); - - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } - else - { - wxGridEvent gridEvt( GetId(), type, this, row, col ); + wxGridEvent gridEvt( GetId(), type, this, row, col ); + gridEvt.SetString(s); - claimed = GetEventHandler()->ProcessEvent(gridEvt); - vetoed = !gridEvt.IsAllowed(); - } + const bool claimed = GetEventHandler()->ProcessEvent(gridEvt); // A Veto'd event may not be `claimed' so test this first - if (vetoed) + if ( !gridEvt.IsAllowed() ) return -1; return claimed ? 1 : 0; @@ -6931,7 +4824,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. @@ -6951,7 +4844,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); } @@ -7049,54 +4942,30 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) break; case WXK_TAB: - if (event.ShiftDown()) - { - if ( GetGridCursorCol() > 0 ) - { - MoveCursorLeft( false ); - } - else - { - // at left of grid - DisableCellEditControl(); - } - } - else { - if ( GetGridCursorCol() < GetNumberCols() - 1 ) + // send an event to the grid's parents for custom handling + wxGridEvent gridEvt(GetId(), wxEVT_GRID_TABBING, this, + GetGridCursorRow(), GetGridCursorCol(), + -1, -1, false, event); + if ( ProcessWindowEvent(gridEvt) ) { - MoveCursorRight( false ); - } - else - { - // at right of grid - DisableCellEditControl(); + // the event has been handled so no need for more processing + break; } } + DoGridProcessTab( event ); break; case WXK_HOME: - if ( event.ControlDown() ) - { - MakeCellVisible( 0, 0 ); - SetCurrentCell( 0, 0 ); - } - else - { - event.Skip(); - } + GoToCell(event.ControlDown() ? 0 + : m_currentCellCoords.GetRow(), + 0); break; case WXK_END: - if ( event.ControlDown() ) - { - MakeCellVisible( m_numRows - 1, m_numCols - 1 ); - SetCurrentCell( m_numRows - 1, m_numCols - 1 ); - } - else - { - event.Skip(); - } + GoToCell(event.ControlDown() ? m_numRows - 1 + : m_currentCellCoords.GetRow(), + m_numCols - 1); break; case WXK_PAGEUP: @@ -7108,25 +4977,34 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) break; case WXK_SPACE: - if ( event.ControlDown() ) + // Ctrl-Space selects the current column, Shift-Space -- the + // current row and Ctrl-Shift-Space -- everything + switch ( m_selection ? event.GetModifiers() : wxMOD_NONE ) { - if ( m_selection ) - { - m_selection->ToggleCellSelection( - m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - break; - } + case wxMOD_CONTROL: + m_selection->SelectCol(m_currentCellCoords.GetCol()); + break; - if ( !IsEditable() ) - MoveCursorRight( false ); - else - event.Skip(); + case wxMOD_SHIFT: + m_selection->SelectRow(m_currentCellCoords.GetRow()); + break; + + case wxMOD_CONTROL | wxMOD_SHIFT: + m_selection->SelectBlock(0, 0, + m_numRows - 1, m_numCols - 1); + break; + + case wxMOD_NONE: + if ( !IsEditable() ) + { + MoveCursorRight(false); + break; + } + //else: fall through + + default: + event.Skip(); + } break; default: @@ -7144,26 +5022,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; } } @@ -7212,12 +5085,75 @@ void wxGrid::OnEraseBackground(wxEraseEvent&) { } -void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) +void wxGrid::DoGridProcessTab(wxKeyboardState& kbdState) { - if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) ) + const bool isForwardTab = !kbdState.ShiftDown(); + + // TAB processing only changes when we are at the borders of the grid, so + // let's first handle the common behaviour when we are not at the border. + if ( isForwardTab ) { - // the event has been intercepted - do nothing - return; + if ( GetGridCursorCol() < GetNumberCols() - 1 ) + { + MoveCursorRight( false ); + return; + } + } + else // going back + { + if ( GetGridCursorCol() ) + { + MoveCursorLeft( false ); + return; + } + } + + + // We only get here if the cursor is at the border of the grid, apply the + // configured behaviour. + switch ( m_tabBehaviour ) + { + case Tab_Stop: + // Nothing special to do, we remain at the current cell. + break; + + case Tab_Wrap: + // Go to the beginning of the next or the end of the previous row. + if ( isForwardTab ) + { + if ( GetGridCursorRow() < GetNumberRows() - 1 ) + { + GoToCell( GetGridCursorRow() + 1, 0 ); + return; + } + } + else + { + if ( GetGridCursorRow() > 0 ) + { + GoToCell( GetGridCursorRow() - 1, GetNumberCols() - 1 ); + return; + } + } + break; + + case Tab_Leave: + if ( Navigate( isForwardTab ? wxNavigationKeyEvent::IsForward + : wxNavigationKeyEvent::IsBackward ) ) + return; + break; + } + + // If we remain in this cell, stop editing it if we were doing so. + DisableCellEditControl(); +} + +bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) +{ + if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 ) + { + // the event has been vetoed - do nothing + return false; } #if !defined(__WXMAC__) @@ -7262,47 +5198,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) { - int temp; - wxGridCellCoords updateTopLeft, updateBottomRight; + MakeCellVisible(m_selectedBlockCorner); + m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); 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 - if ( topRow > bottomRow ) - { - temp = topRow; - topRow = bottomRow; - bottomRow = temp; - } + case wxGridSelectCells: + // arbitrary blocks selection allowed so just use the cell + // coordinates as is + break; - if ( leftCol > rightCol ) - { - temp = leftCol; - leftCol = rightCol; - rightCol = temp; + 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; + } } - updateTopLeft = wxGridCellCoords( topRow, leftCol ); - updateBottomRight = wxGridCellCoords( bottomRow, rightCol ); + EnsureFirstLessThanSecond(topRow, bottomRow); + EnsureFirstLessThanSecond(leftCol, 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 ), @@ -7311,8 +5262,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo } // Now handle changing an existing selection area. - else if ( m_selectingTopLeft != updateTopLeft || - m_selectingBottomRight != updateBottomRight ) + else if ( m_selectedBlockTopLeft != updateTopLeft || + m_selectedBlockBottomRight != updateBottomRight ) { // Compute two optimal update rectangles: // Either one rectangle is a real subset of the @@ -7326,36 +5277,16 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo int i; // Store intermediate values - wxCoord oldLeft = m_selectingTopLeft.GetCol(); - wxCoord oldTop = m_selectingTopLeft.GetRow(); - wxCoord oldRight = m_selectingBottomRight.GetCol(); - wxCoord oldBottom = m_selectingBottomRight.GetRow(); + wxCoord oldLeft = m_selectedBlockTopLeft.GetCol(); + wxCoord oldTop = m_selectedBlockTopLeft.GetRow(); + wxCoord oldRight = m_selectedBlockBottomRight.GetCol(); + wxCoord oldBottom = m_selectedBlockBottomRight.GetRow(); // Determine the outer/inner coordinates. - if (oldLeft > leftCol) - { - temp = oldLeft; - oldLeft = leftCol; - leftCol = temp; - } - if (oldTop > topRow ) - { - temp = oldTop; - oldTop = topRow; - topRow = temp; - } - if (oldRight < rightCol ) - { - temp = oldRight; - oldRight = rightCol; - rightCol = temp; - } - if (oldBottom < bottomRow) - { - temp = oldBottom; - oldBottom = bottomRow; - bottomRow = temp; - } + EnsureFirstLessThanSecond(oldLeft, leftCol); + EnsureFirstLessThanSecond(oldTop, topRow); + EnsureFirstLessThanSecond(rightCol, oldRight); + EnsureFirstLessThanSecond(bottomRow, oldBottom); // Now, either the stuff marked old is the outer // rectangle or we don't have a situation where one @@ -7408,8 +5339,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo } // change selection - m_selectingTopLeft = updateTopLeft; - m_selectingBottomRight = updateBottomRight; + m_selectedBlockTopLeft = updateTopLeft; + m_selectedBlockBottomRight = updateBottomRight; } // @@ -7593,7 +5524,7 @@ void wxGrid::DrawGridSpace( wxDC& dc ) int left, top; CalcUnscrolledPosition( 0, 0, &left, &top ); - dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxBRUSHSTYLE_SOLID) ); + dc.SetBrush(GetDefaultCellBackgroundColour()); dc.SetPen( *wxTRANSPARENT_PEN ); if ( right > rightCol ) @@ -7617,11 +5548,6 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) return; // we draw the cell border ourselves -#if !WXGRID_DRAW_LINES - if ( m_gridLinesEnabled ) - DrawCellBorder( dc, coords ); -#endif - wxGridCellAttr* attr = GetCellAttr(row, col); bool isCurrent = coords == m_currentCellCoords; @@ -7638,7 +5564,7 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) // implicitly, causing this out-of order render. #if !defined(__WXMAC__) wxGridCellEditor *editor = attr->GetEditor(this, row, col); - editor->PaintBackground(rect, attr); + editor->PaintBackground(dc, rect, *attr); editor->DecRef(); #endif } @@ -7688,36 +5614,17 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) // Now draw the rectangle // use the cellHighlightColour if the cell is inside a selection, this // will ensure the cell is always visible. - dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground : m_cellHighlightColour, penWidth, wxPENSTYLE_SOLID)); + dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground + : m_cellHighlightColour, + penWidth)); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect); } - -#if 0 - // VZ: my experiments with 3D borders... - - // how to properly set colours for arbitrary bg? - wxCoord x1 = rect.x, - y1 = rect.y, - x2 = rect.x + rect.width - 1, - y2 = rect.y + rect.height - 1; - - dc.SetPen(*wxWHITE_PEN); - dc.DrawLine(x1, y1, x2, y1); - dc.DrawLine(x1, y1, x1, y2); - - dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1); - dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2); - - dc.SetPen(*wxBLACK_PEN); - dc.DrawLine(x1, y2, x2, y2); - dc.DrawLine(x2, y1, x2, y2 + 1); -#endif } wxPen wxGrid::GetDefaultGridLinePen() { - return wxPen(GetGridLineColour(), 1, wxPENSTYLE_SOLID); + return wxPen(GetGridLineColour()); } wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row)) @@ -7804,50 +5711,102 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) } } -// TODO: remove this ??? +// Used by wxGrid::Render() to draw the grid lines only for the cells in the +// specified range. +void +wxGrid::DrawRangeGridLines(wxDC& dc, + const wxRegion& reg, + const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight) +{ + if ( !m_gridLinesEnabled ) + return; + + int top, left, width, height; + reg.GetBox( left, top, width, height ); + + // create a clipping region + wxRegion clippedcells( dc.LogicalToDeviceX( left ), + dc.LogicalToDeviceY( top ), + dc.LogicalToDeviceXRel( width ), + dc.LogicalToDeviceYRel( height ) ); + + // subtract multi cell span area from clipping region for lines + wxRect rect; + for ( int row = topLeft.GetRow(); row <= bottomRight.GetRow(); row++ ) + { + for ( int col = topLeft.GetCol(); col <= bottomRight.GetCol(); col++ ) + { + int cell_rows, cell_cols; + GetCellSize( row, col, &cell_rows, &cell_cols ); + if ( cell_rows > 1 || cell_cols > 1 ) // multi cell + { + rect = CellToRect( row, col ); + // cater for scaling + // device origin already set in ::Render() for x, y + rect.x = dc.LogicalToDeviceX( rect.x ); + rect.y = dc.LogicalToDeviceY( rect.y ); + rect.width = dc.LogicalToDeviceXRel( rect.width ); + rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1; + clippedcells.Subtract( rect ); + } + else if ( cell_rows < 0 || cell_cols < 0 ) // part of multicell + { + rect = CellToRect( row + cell_rows, col + cell_cols ); + rect.x = dc.LogicalToDeviceX( rect.x ); + rect.y = dc.LogicalToDeviceY( rect.y ); + rect.width = dc.LogicalToDeviceXRel( rect.width ); + rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1; + clippedcells.Subtract( rect ); + } + } + } + + dc.SetDeviceClippingRegion( clippedcells ); + + DoDrawGridLines(dc, + top, left, top + height, left + width, + topLeft.GetRow(), topLeft.GetCol(), + bottomRight.GetRow(), bottomRight.GetCol()); + + dc.DestroyClippingRegion(); +} + // This is used to redraw all grid lines e.g. when the grid line colour // has been changed // void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) { -#if !WXGRID_DRAW_LINES - return; -#endif - - if ( !m_gridLinesEnabled || !m_numRows || !m_numCols ) + if ( !m_gridLinesEnabled ) return; int top, bottom, left, right; -#if 0 //#ifndef __WXGTK__ - if (reg.IsEmpty()) + int cw, ch; + m_gridWin->GetClientSize(&cw, &ch); + CalcUnscrolledPosition( 0, 0, &left, &top ); + CalcUnscrolledPosition( cw, ch, &right, &bottom ); + + // avoid drawing grid lines past the last row and col + if ( m_gridLinesClipHorz ) { - int cw, ch; - m_gridWin->GetClientSize(&cw, &ch); + if ( !m_numCols ) + return; - // virtual coords of visible area - // - CalcUnscrolledPosition( 0, 0, &left, &top ); - CalcUnscrolledPosition( cw, ch, &right, &bottom ); + const int lastColRight = GetColRight(GetColAt(m_numCols - 1)); + if ( right > lastColRight ) + right = lastColRight; } - else + + if ( m_gridLinesClipVert ) { - wxCoord x, y, w, h; - reg.GetBox(x, y, w, h); - CalcUnscrolledPosition( x, y, &left, &top ); - CalcUnscrolledPosition( x + w, y + h, &right, &bottom ); - } -#else - int cw, ch; - m_gridWin->GetClientSize(&cw, &ch); - CalcUnscrolledPosition( 0, 0, &left, &top ); - CalcUnscrolledPosition( cw, ch, &right, &bottom ); -#endif + if ( !m_numRows ) + return; - // avoid drawing grid lines past the last row and col - // - right = wxMin( right, GetColRight(GetColAt( m_numCols - 1 )) ); - bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) ); + const int lastRowBottom = GetRowBottom(m_numRows - 1); + if ( bottom > lastRowBottom ) + bottom = lastRowBottom; + } // no gridlines inside multicells, clip them out int leftCol = GetColPos( internalXToCol(left) ); @@ -7857,15 +5816,14 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) wxRegion clippedcells(0, 0, cw, ch); - int i, j, cell_rows, cell_cols; + int cell_rows, cell_cols; wxRect rect; - for (j=topRow; j<=bottomRow; j++) + for ( int j = topRow; j <= bottomRow; j++ ) { - int colPos; - for (colPos=leftCol; colPos<=rightCol; colPos++) + for ( int colPos = leftCol; colPos <= rightCol; colPos++ ) { - i = GetColAt( colPos ); + int i = GetColAt( colPos ); GetCellSize( j, i, &cell_rows, &cell_cols ); if ((cell_rows > 1) || (cell_cols > 1)) @@ -7885,18 +5843,27 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) dc.SetDeviceClippingRegion( clippedcells ); + DoDrawGridLines(dc, + top, left, bottom, right, + topRow, leftCol, m_numRows, m_numCols); + + dc.DestroyClippingRegion(); +} +void +wxGrid::DoDrawGridLines(wxDC& dc, + int top, int left, + int bottom, int right, + int topRow, int leftCol, + int bottomRow, int rightCol) +{ // horizontal grid lines - // - // already declared above - int i; - for ( i = internalYToRow(top); i < m_numRows; i++ ) + for ( int i = topRow; i < bottomRow; i++ ) { int bot = GetRowBottom(i) - 1; if ( bot > bottom ) - { break; - } if ( bot >= top ) { @@ -7906,11 +5873,9 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) } // vertical grid lines - // - int colPos; - for ( colPos = leftCol; colPos < m_numCols; colPos++ ) + for ( int colPos = leftCol; colPos < rightCol; colPos++ ) { - i = GetColAt( colPos ); + int i = GetColAt( colPos ); int colRight = GetColRight(i); #ifdef __WXGTK__ @@ -7919,9 +5884,7 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) colRight--; if ( colRight > right ) - { break; - } if ( colRight >= left ) { @@ -7929,8 +5892,6 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) dc.DrawLine( colRight, top, colRight, bottom ); } } - - dc.DestroyClippingRegion(); } void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows) @@ -7938,10 +5899,8 @@ void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows) if ( !m_numRows ) return; - size_t i; - size_t numLabels = rows.GetCount(); - - for ( i = 0; i < numLabels; i++ ) + const size_t numLabels = rows.GetCount(); + for ( size_t i = 0; i < numLabels; i++ ) { DrawRowLabel( dc, rows[i] ); } @@ -7952,36 +5911,47 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 ) return; - wxRect rect; + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; - int rowTop = GetRowTop(row), - rowBottom = GetRowBottom(row) - 1; + // notice that an explicit static_cast is needed to avoid a compilation + // error with VC7.1 which, for some reason, tries to instantiate (abstract) + // wxGridRowHeaderRenderer class without it + const wxGridRowHeaderRenderer& + rend = attrProvider ? attrProvider->GetRowHeaderRenderer(row) + : static_cast + (gs_defaultHeaderRenderers.rowRenderer); - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); - dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom ); - dc.DrawLine( 0, rowTop, 0, rowBottom ); - dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom ); + wxRect rect(0, GetRowTop(row), m_rowLabelWidth, GetRowHeight(row)); + rend.DrawBorder(*this, dc, rect); - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 1, rowTop, 1, rowBottom ); - dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop ); + int hAlign, vAlign; + GetRowLabelAlignment(&hAlign, &vAlign); - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - dc.SetTextForeground( GetLabelTextColour() ); - dc.SetFont( GetLabelFont() ); + rend.DrawLabel(*this, dc, GetRowLabelValue(row), + rect, hAlign, vAlign, wxHORIZONTAL); +} - int hAlign, vAlign; - GetRowLabelAlignment( &hAlign, &vAlign ); +void wxGrid::UseNativeColHeader(bool native) +{ + if ( native == m_useNativeHeader ) + return; + + delete m_colWindow; + m_useNativeHeader = native; - rect.SetX( 2 ); - rect.SetY( GetRowTop(row) + 2 ); - rect.SetWidth( m_rowLabelWidth - 4 ); - rect.SetHeight( GetRowHeight(row) - 4 ); - DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); + 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) { @@ -7989,7 +5959,8 @@ void wxGrid::SetUseNativeColLabels( bool native ) SetColLabelSize( height ); } - m_colLabelWin->Refresh(); + GetColLabelWindow()->Refresh(); + m_cornerLabelWin->Refresh(); } void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) @@ -7997,92 +5968,109 @@ void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) if ( !m_numCols ) return; - size_t i; - size_t numLabels = cols.GetCount(); - - for ( i = 0; i < numLabels; i++ ) + const size_t numLabels = cols.GetCount(); + for ( size_t i = 0; i < numLabels; i++ ) { DrawColLabel( dc, cols[i] ); } } -void wxGrid::DrawColLabel( wxDC& dc, int col ) +void wxGrid::DrawCornerLabel(wxDC& dc) { - if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 ) - return; - - int colLeft = GetColLeft(col); - - wxRect rect; + wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight)); - if (m_nativeColumnLabels) + if ( m_nativeColumnLabels ) { - rect.SetX( colLeft); - rect.SetY( 0 ); - rect.SetWidth( GetColWidth(col)); - rect.SetHeight( m_colLabelHeight ); + rect.Deflate(1); - wxWindowDC *win_dc = (wxWindowDC*) &dc; - wxRendererNative::Get().DrawHeaderButton( win_dc->GetWindow(), dc, rect, 0 ); + wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0); } else { - int colRight = GetColRight(col) - 1; + rect.width++; + rect.height++; - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); - dc.DrawLine( colRight, 0, colRight, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 0, colRight, 0 ); - dc.DrawLine( colLeft, m_colLabelHeight - 1, - colRight + 1, m_colLabelHeight - 1 ); + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; + const wxGridCornerHeaderRenderer& + rend = attrProvider ? attrProvider->GetCornerRenderer() + : static_cast + (gs_defaultHeaderRenderers.cornerRenderer); - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 1, colRight, 1 ); + rend.DrawBorder(*this, dc, rect); } +} - dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); - dc.SetTextForeground( GetLabelTextColour() ); - dc.SetFont( GetLabelFont() ); +void wxGrid::DrawColLabel(wxDC& dc, int col) +{ + if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 ) + return; + + int colLeft = GetColLeft(col); + + wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight); + wxGridCellAttrProvider * const + attrProvider = m_table ? m_table->GetAttrProvider() : NULL; + const wxGridColumnHeaderRenderer& + rend = attrProvider ? attrProvider->GetColumnHeaderRenderer(col) + : static_cast + (gs_defaultHeaderRenderers.colRenderer); + + if ( m_nativeColumnLabels ) + { + wxRendererNative::Get().DrawHeaderButton + ( + GetColLabelWindow(), + dc, + rect, + 0, + IsSortingBy(col) + ? IsSortOrderAscending() + ? wxHDR_SORT_ICON_UP + : wxHDR_SORT_ICON_DOWN + : wxHDR_SORT_ICON_NONE + ); + rect.Deflate(2); + } + else + { + // It is reported that we need to erase the background to avoid display + // artefacts, see #12055. + wxDCBrushChanger setBrush(dc, m_colWindow->GetBackgroundColour()); + dc.DrawRectangle(rect); + + rend.DrawBorder(*this, dc, rect); + } - int hAlign, vAlign, orient; - GetColLabelAlignment( &hAlign, &vAlign ); - orient = GetColLabelTextOrientation(); + int hAlign, vAlign; + GetColLabelAlignment(&hAlign, &vAlign); + const int orient = GetColLabelTextOrientation(); - rect.SetX( colLeft + 2 ); - rect.SetY( 2 ); - rect.SetWidth( GetColWidth(col) - 4 ); - rect.SetHeight( m_colLabelHeight - 4 ); - DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient ); + rend.DrawLabel(*this, dc, GetColLabelValue(col), rect, hAlign, vAlign, orient); } +// TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which +// we just have to add textOrientation support void wxGrid::DrawTextRectangle( wxDC& dc, const wxString& value, const wxRect& rect, int horizAlign, int vertAlign, - int textOrientation ) + int textOrientation ) const { wxArrayString lines; StringToLines( value, lines ); - // Forward to new API. - DrawTextRectangle( dc, - lines, - rect, - horizAlign, - vertAlign, - textOrientation ); + DrawTextRectangle(dc, lines, rect, horizAlign, vertAlign, textOrientation); } -// VZ: this should be replaced with wxDC::DrawLabel() to which we just have to -// add textOrientation support void wxGrid::DrawTextRectangle(wxDC& dc, const wxArrayString& lines, const wxRect& rect, int horizAlign, int vertAlign, - int textOrientation) + int textOrientation) const { if ( lines.empty() ) return; @@ -8246,7 +6234,7 @@ void wxGrid::EndBatch() { CalcDimensions(); m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); m_gridWin->Refresh(); } @@ -8280,8 +6268,6 @@ bool wxGrid::Enable(bool enable) void wxGrid::EnableEditing( bool edit ) { - // TODO: improve this ? - // if ( edit != m_editable ) { if (!edit) @@ -8299,11 +6285,11 @@ 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! - wxASSERT_MSG( CanEnableCellControl(), _T("can't enable editing for this cell!") ); + wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") ); // do it before ShowCellEditControl() m_cellEditCtrlEnabled = enable; @@ -8312,8 +6298,7 @@ void wxGrid::EnableCellEditControl( bool enable ) } else { - //FIXME:add veto support - SendEvent( wxEVT_GRID_EDITOR_HIDDEN ); + SendEvent(wxEVT_GRID_EDITOR_HIDDEN); HideCellEditControl(); SaveEditControlValue(); @@ -8326,8 +6311,8 @@ void wxGrid::EnableCellEditControl( bool enable ) bool wxGrid::IsCurrentCellReadOnly() const { - // const_cast - wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords); + wxGridCellAttr* + attr = const_cast(this)->GetCellAttr(m_currentCellCoords); bool readonly = attr->IsReadOnly(); attr->DecRef(); @@ -8404,7 +6389,7 @@ void wxGrid::ShowCellEditControl() wxClientDC dc( m_gridWin ); PrepareDC( dc ); wxGridCellAttr* attr = GetCellAttr(row, col); - dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); + dc.SetBrush(wxBrush(attr->GetBackgroundColour())); dc.SetPen(*wxTRANSPARENT_PEN); dc.DrawRectangle(rect); @@ -8507,11 +6492,18 @@ void wxGrid::HideCellEditControl() wxGridCellAttr *attr = GetCellAttr(row, col); wxGridCellEditor *editor = attr->GetEditor(this, row, col); + const bool editorHadFocus = editor->GetControl()->HasFocus(); editor->Show( false ); editor->DecRef(); attr->DecRef(); - m_gridWin->SetFocus(); + // return the focus to the grid itself if the editor had it + // + // note that we must not do this unconditionally to avoid stealing + // focus from the window which just received it if we are hiding the + // editor precisely because we lost focus + if ( editorHadFocus ) + m_gridWin->SetFocus(); // refresh whole row to the right wxRect rect( CellToRect(row, col) ); @@ -8538,21 +6530,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(row, col, this, 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(); } } @@ -8563,203 +6560,157 @@ 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 in O(log n) time. +// NOTE: This may not work correctly for reordered columns. +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()) - { - if ((int) i_max < nMax) - return i_max; - return clipToMinMax ? nMax - 1 : -1; - } + int maxPos = coord / defaultLineSize, + minPos = 0; - if ( i_max >= BorderArray.GetCount()) + // 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() ) { - i_max = BorderArray.GetCount() - 1; - } - else - { - if ( coord >= BorderArray[i_max]) - { - i_min = i_max; - if (minDist) - i_max = coord / minDist; - else - i_max = BorderArray.GetCount() - 1; - } + if ( maxPos < numLines ) + return maxPos; - if ( i_max >= BorderArray.GetCount()) - i_max = BorderArray.GetCount() - 1; + return clipToMinMax ? numLines - 1 : -1; } - if ( coord >= BorderArray[i_max]) - return clipToMinMax ? (int)i_max : -1; - if ( coord < BorderArray[0] ) + + // binary search is quite efficient and we can't really make any assumptions + // on where to start here since row and columns could be of size 0 if they are + // hidden. While this could be made more efficient, some profiling will be + // necessary to determine if it really is a performance bottleneck + maxPos = numLines - 1; + + // 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. -// 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 -// 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. +// return the row/col number such that the pos is near the edge of, or -1 if +// not near an edge. // -int wxGrid::YToEdgeOfRow( int y ) const +// notice that position can only possibly be near an edge if the row/column is +// large enough to still allow for an "inner" area that is _not_ near the edge +// (i.e., if the height/width is smaller than WXGRID_LABEL_EDGE_ZONE, pos will +// _never_ be considered to be near the edge). +int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { - int i; - i = internalYToRow(y); + // Get the bottom or rightmost line that could match. + 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 ) + { + // We need to find the previous visible line, so skip all the + // hidden (of size 0) ones. + do + { + line = oper.GetLineBefore(this, line); + } + while ( line >= 0 && oper.GetLineSize(this, line) == 0 ); + + // It can possibly be -1 here. + return line; + } } 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 @@ -8785,13 +6736,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; @@ -8874,7 +6825,7 @@ void wxGrid::MakeCellVisible( int row, int col ) // // Sometimes GRID_SCROLL_LINE / 2 is not enough, // so just add a full scroll unit... - ypos += m_scrollLineY; + ypos += m_yScrollPixelsPerLine; } // special handling for wide cells - show always left part of the cell! @@ -8893,15 +6844,15 @@ void wxGrid::MakeCellVisible( int row, int col ) xpos = x0 + (right - cw); // see comment for ypos above - xpos += m_scrollLineX; + xpos += m_xScrollPixelsPerLine; } if ( xpos != -1 || ypos != -1 ) { if ( xpos != -1 ) - xpos /= m_scrollLineX; + xpos /= m_xScrollPixelsPerLine; if ( ypos != -1 ) - ypos /= m_scrollLineY; + ypos /= m_yScrollPixelsPerLine; Scroll( xpos, ypos ); AdjustScrollbars(); } @@ -8912,463 +6863,197 @@ void wxGrid::MakeCellVisible( int row, int col ) // ------ Grid cursor movement functions // -bool wxGrid::MoveCursorUp( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() >= 0 ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() > 0 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow() - 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MoveCursorDown( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() < m_numRows - 1 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() < m_numRows - 1 ) - { - int row = m_currentCellCoords.GetRow() + 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MoveCursorLeft( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() >= 0 ) - { - 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(); - - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MoveCursorRight( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols ) - { - 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(); - - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MovePageUp() -{ - 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; - } - - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); - - return true; - } - - return false; -} - -bool wxGrid::MovePageDown() +bool +wxGrid::DoMoveCursor(bool expandSelection, + const wxGridDirectionOperations& diroper) { if ( m_currentCellCoords == wxGridNoCellCoords ) return false; - int row = m_currentCellCoords.GetRow(); - if ( (row + 1) < m_numRows ) + if ( expandSelection ) { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + wxGridCellCoords coords = m_selectedBlockCorner; + if ( coords == wxGridNoCellCoords ) + coords = m_currentCellCoords; - int y = GetRowTop(row); - int newRow = internalYToRow( y + ch ); - if ( newRow == row ) - { - // row < m_numRows, so newRow can't overflow here. - newRow = row + 1; - } + if ( diroper.IsAtBoundary(coords) ) + return false; - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); + diroper.Advance(coords); - return true; + UpdateBlockBeingSelected(m_currentCellCoords, coords); } - - return false; -} - -bool wxGrid::MoveCursorUpBlock( bool expandSelection ) -{ - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() > 0 ) + else // don't expand selection { - 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; - } - } - } + ClearSelection(); - 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; + diroper.Advance(coords); + + GoToCell(coords); } - return false; + return true; } -bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +bool wxGrid::MoveCursorUp(bool expandSelection) { - 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; - } - } - } + return DoMoveCursor(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::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::MoveCursorLeftBlock( bool expandSelection ) +bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() > 0 ) + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; + + 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 row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); + wxGridCellCoords coords(m_currentCellCoords); + diroper.Advance(coords); + newRow = coords.GetRow(); + } - 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) ) - { - // starting at the left of a block: find the next block - // - col--; - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the left of the block - // - while ( col > 0 ) - { - col--; - if ( m_table->IsEmptyCell(row, col) ) - { - col++; - break; - } - } - } + GoToCell(newRow, m_currentCellCoords.GetCol()); - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } + return true; +} - return true; - } +bool wxGrid::MovePageUp() +{ + return DoMoveCursorByPage( + wxGridBackwardOperations(this, wxGridRowOperations())); +} - return false; +bool wxGrid::MovePageDown() +{ + return DoMoveCursorByPage( + wxGridForwardOperations(this, wxGridRowOperations())); } -bool wxGrid::MoveCursorRightBlock( 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.GetCol() < m_numCols - 1 ) + while ( !diroper.IsAtBoundary(coords) ) { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); + diroper.Advance(coords); + if ( !m_table->IsEmpty(coords) ) + break; + } +} - 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) ) +bool +wxGrid::DoMoveCursorByBlock(bool expandSelection, + const wxGridDirectionOperations& diroper) +{ + if ( !m_table || m_currentCellCoords == wxGridNoCellCoords ) + return false; + + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; + + 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); + } + else // current cell is not empty + { + diroper.Advance(coords); + if ( m_table->IsEmpty(coords) ) { - // 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; - } + // 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 right of the block - // - while ( col < m_numCols - 1 ) + // 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::MoveCursorUpBlock(bool expandSelection) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations()) + ); +} + +bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridRowOperations()) + ); +} + +bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations()) + ); +} + +bool wxGrid::MoveCursorRightBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations()) + ); } // @@ -9448,6 +7133,7 @@ void wxGrid::SetRowLabelSize( int width ) } m_rowLabelWidth = width; + InvalidateBestSize(); CalcWindowSizes(); wxScrolledWindow::Refresh( true ); } @@ -9466,17 +7152,18 @@ 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 ); } m_colLabelHeight = height; + InvalidateBestSize(); CalcWindowSizes(); wxScrolledWindow::Refresh( true ); } @@ -9488,13 +7175,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(); } } @@ -9508,7 +7195,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } } @@ -9519,7 +7206,7 @@ void wxGrid::SetLabelFont( const wxFont& font ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9585,7 +7272,7 @@ void wxGrid::SetColLabelAlignment( int horiz, int vert ) if ( !GetBatchCount() ) { - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9602,7 +7289,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 ) @@ -9631,13 +7318,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 ); + } } } } @@ -9649,9 +7343,8 @@ void wxGrid::SetGridLineColour( const wxColour& colour ) { m_gridLineColour = colour; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9676,7 +7369,7 @@ void wxGrid::SetCellHighlightPenWidth(int width) m_cellHighlightPenWidth = width; // Just redrawing the cell highlight is not enough since that won't - // make any visible change if the the thickness is getting smaller. + // make any visible change if the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) @@ -9694,7 +7387,7 @@ void wxGrid::SetCellHighlightROPenWidth(int width) m_cellHighlightROPenWidth = width; // Just redrawing the cell highlight is not enough since that won't - // make any visible change if the the thickness is getting smaller. + // make any visible change if the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); if ( row == -1 || col == -1 || @@ -9706,25 +7399,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(); } } @@ -9735,7 +7445,7 @@ int wxGrid::GetDefaultRowSize() const int wxGrid::GetRowSize( int row ) const { - wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") ); + wxCHECK_MSG( row >= 0 && row < m_numRows, 0, wxT("invalid row index") ); return GetRowHeight(row); } @@ -9747,7 +7457,7 @@ int wxGrid::GetDefaultColSize() const int wxGrid::GetColSize( int col ) const { - wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") ); + wxCHECK_MSG( col >= 0 && col < m_numCols, 0, wxT("invalid column index") ); return GetColWidth(col); } @@ -9894,11 +7604,21 @@ bool wxGrid::GetCellOverflow( int row, int col ) const return allow; } -void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const +wxGrid::CellSpan +wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const { wxGridCellAttr *attr = GetCellAttr(row, col); attr->GetSize( num_rows, num_cols ); attr->DecRef(); + + if ( *num_rows == 1 && *num_cols == 1 ) + return CellSpan_None; // just a normal cell + + if ( *num_rows < 0 || *num_cols < 0 ) + return CellSpan_Inside; // covered by a multi-span cell + + // this cell spans multiple cells to its right/bottom + return CellSpan_Main; } wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const @@ -9957,11 +7677,18 @@ void wxGrid::ClearAttrCache() } } +void wxGrid::RefreshAttr(int row, int col) +{ + if ( m_attrCache.row == row && m_attrCache.col == col ) + ClearAttrCache(); +} + + void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const { if ( attr != NULL ) { - wxGrid *self = (wxGrid *)this; // const_cast + wxGrid * const self = const_cast(this); self->ClearAttrCache(); self->m_attrCache.row = row; @@ -10004,7 +7731,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); } } @@ -10024,11 +7751,11 @@ 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")); - wxCHECK_MSG( m_table, attr, _T("must have a table") ); + wxCHECK_MSG( canHave, attr, wxT("Cell attributes not allowed")); + wxCHECK_MSG( m_table, attr, wxT("must have a table") ); attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell); if ( !attr ) @@ -10062,7 +7789,7 @@ void wxGrid::SetColFormatFloat(int col, int width, int precision) wxString typeName = wxGRID_VALUE_FLOAT; if ( (width != -1) || (precision != -1) ) { - typeName << _T(':') << width << _T(',') << precision; + typeName << wxT(':') << width << wxT(',') << precision; } SetColFormatCustom(col, typeName); @@ -10318,6 +8045,22 @@ wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) // row/col size // ---------------------------------------------------------------------------- +void wxGrid::DoDisableLineResize(int line, wxGridFixedIndicesSet *& setFixed) +{ + if ( !setFixed ) + { + setFixed = new wxGridFixedIndicesSet; + } + + setFixed->insert(line); +} + +bool +wxGrid::DoCanResizeLine(int line, const wxGridFixedIndicesSet *setFixed) const +{ + return !setFixed || !setFixed->count(line); +} + void wxGrid::EnableDragRowSize( bool enable ) { m_canDragRowSize = enable; @@ -10355,13 +8098,77 @@ void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows ) } } +namespace +{ + +// This is a common part of SetRowSize() and SetColSize() which takes care of +// updating the height/width of a row/column depending on its current value and +// the new one. +// +// Returns the difference between the new and the old size. +int UpdateRowOrColSize(int& sizeCurrent, int sizeNew) +{ + // On input here sizeCurrent can be negative if it's currently hidden (the + // real size is its absolute value then). And sizeNew can be 0 to indicate + // that the row/column should be hidden or -1 to indicate that it should be + // shown again. + + if ( sizeNew < 0 ) + { + // We're showing back a previously hidden row/column. + wxASSERT_MSG( sizeNew == -1, wxS("New size must be positive or -1.") ); + + // If it's already visible, simply do nothing. + if ( sizeCurrent >= 0 ) + return 0; + + // Otherwise show it by restoring its old size. + sizeCurrent = -sizeCurrent; + + // This is positive which is correct. + return sizeCurrent; + } + else if ( sizeNew == 0 ) + { + // We're hiding a row/column. + + // If it's already hidden, simply do nothing. + if ( sizeCurrent <= 0 ) + return 0; + + // Otherwise hide it and also remember the shown size to be able to + // restore it later. + sizeCurrent = -sizeCurrent; + + // This is negative which is correct. + return sizeCurrent; + } + else // We're just changing the row/column size. + { + // Here it could have been hidden or not previously. + const int sizeOld = sizeCurrent < 0 ? 0 : sizeCurrent; + + sizeCurrent = sizeNew; + + return sizeCurrent - sizeOld; + } +} + +} // anonymous namespace + void wxGrid::SetRowSize( int row, int height ) { - wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") ); + // See comment in SetColSize + if ( height > 0 && height < GetRowMinimalAcceptableHeight()) + return; - // if < 0 then calculate new height from label - if ( height < 0 ) + // The value of -1 is special and means to fit the height to the row label. + if ( height == -1 ) { + // As with the columns, ignore attempts to auto-size the hidden rows. + if ( GetRowHeight(row) == 0 ) + return; + long w, h; wxArrayString lines; wxClientDC dc(m_rowLabelWin); @@ -10372,9 +8179,12 @@ void wxGrid::SetRowSize( int row, int height ) height = wxMax(h, GetRowMinimalAcceptableHeight()); } - // See comment in SetColSize - if ( height < GetRowMinimalAcceptableHeight()) - return; + DoSetRowSize(row, height); +} + +void wxGrid::DoSetRowSize( int row, int height ) +{ + wxCHECK_RET( row >= 0 && row < m_numRows, wxT("invalid row index") ); if ( m_rowHeights.IsEmpty() ) { @@ -10382,17 +8192,22 @@ void wxGrid::SetRowSize( int row, int height ) InitRowHeights(); } - int h = wxMax( 0, height ); - int diff = h - m_rowHeights[row]; + const int diff = UpdateRowOrColSize(m_rowHeights[row], height); + if ( !diff ) + return; - m_rowHeights[row] = h; for ( int i = row; i < m_numRows; i++ ) { m_rowBottoms[i] += diff; } + InvalidateBestSize(); + if ( !GetBatchCount() ) + { CalcDimensions(); + Refresh(); + } } void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols ) @@ -10415,14 +8230,26 @@ void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols ) void wxGrid::SetColSize( int col, int width ) { - wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") ); + // 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 < 0 then calculate new width from label - if ( width < 0 ) + // The value of -1 is special and means to fit the width to the column label. + if ( width == -1 ) { + // We currently don't support auto-sizing hidden columns. We could, but + // it's not clear whether this is really needed and it would make the + // code more complex. + if ( GetColWidth(col) == 0 ) + return; + long w, h; wxArrayString lines; - wxClientDC dc(m_colLabelWin); + wxClientDC dc(m_colWindow); dc.SetFont(GetLabelFont()); StringToLines(GetColLabelValue(col), lines); if ( GetColLabelTextOrientation() == wxHORIZONTAL ) @@ -10434,16 +8261,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 + DoSetColSize(col, width); +} - if ( width < GetColMinimalAcceptableWidth() ) - return; +void wxGrid::DoSetColSize( int col, int width ) +{ + wxCHECK_RET( col >= 0 && col < m_numCols, wxT("invalid column index") ); if ( m_colWidths.IsEmpty() ) { @@ -10451,17 +8274,26 @@ 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 = UpdateRowOrColSize(m_colWidths[col], width); + if ( !diff ) + return; + + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + //else: will be refreshed when the header is redrawn for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) { m_colRights[GetColAt(colPos)] += diff; } + InvalidateBestSize(); + if ( !GetBatchCount() ) + { CalcDimensions(); + Refresh(); + } } void wxGrid::SetColMinimalWidth( int col, int width ) @@ -10533,35 +8365,91 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) { const bool column = direction == wxGRID_COLUMN; + // We don't support auto-sizing hidden rows or columns, this doesn't seem + // to make much sense. + if ( column ) + { + if ( GetColWidth(colOrRow) == 0 ) + return; + } + else + { + if ( GetRowHeight(colOrRow) == 0 ) + return; + } + wxClientDC dc(m_gridWin); // cancel editing of cell HideCellEditControl(); SaveEditControlValue(); - // init both of them to avoid compiler warnings, even if we only need one - int row = -1, - col = -1; + // initialize both of them just to avoid compiler warnings even if only + // really needs to be initialized here + int row, + col; if ( column ) + { + row = -1; col = colOrRow; + } else + { row = colOrRow; + col = -1; + } wxCoord extent, extentMax = 0; int max = column ? m_numRows : m_numCols; for ( int rowOrCol = 0; rowOrCol < max; rowOrCol++ ) { if ( column ) + { row = rowOrCol; + col = colOrRow; + } else + { + row = colOrRow; col = rowOrCol; + } + + // we need to account for the cells spanning multiple columns/rows: + // while they may need a lot of space, they don't need all of it in + // this column/row + int numRows, numCols; + const CellSpan span = GetCellSize(row, col, &numRows, &numCols); + if ( span == CellSpan_Inside ) + { + // we need to get the size of the main cell, not of a cell hidden + // by it + row += numRows; + col += numCols; + + // get the size of the main cell too + GetCellSize(row, col, &numRows, &numCols); + } + // get cell ( main cell if CellSpan_Inside ) renderer best size wxGridCellAttr *attr = GetCellAttr(row, col); wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col); if ( renderer ) { wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col); extent = column ? size.x : size.y; + + if ( span != CellSpan_None ) + { + // we spread the size of a spanning cell over all the cells it + // covers evenly -- this is probably not ideal but we can't + // really do much better here + // + // notice that numCols and numRows are never 0 as they + // correspond to the size of the main cell of the span and not + // of the cell inside it + extent /= column ? numCols : numRows; + } + if ( extent > extentMax ) extentMax = extent; @@ -10577,12 +8465,12 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) if ( column ) { - dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h ); + dc.GetMultiLineTextExtent( GetColLabelValue(colOrRow), &w, &h ); if ( GetColLabelTextOrientation() == wxVERTICAL ) w = h; } else - dc.GetMultiLineTextExtent( GetRowLabelValue(row), &w, &h ); + dc.GetMultiLineTextExtent( GetRowLabelValue(colOrRow), &w, &h ); extent = column ? w : h; if ( extent > extentMax ) @@ -10609,19 +8497,26 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) // comment in SetColSize() for explanation of why this isn't done // in SetColSize(). if ( !setAsMin ) - extentMax = wxMax(extentMax, GetColMinimalWidth(col)); + extentMax = wxMax(extentMax, GetColMinimalWidth(colOrRow)); - SetColSize( col, extentMax ); + SetColSize( colOrRow, 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(colOrRow); + } + else + { + int cw, ch, dummy; + m_gridWin->GetClientSize( &cw, &ch ); + wxRect rect ( CellToRect( 0, colOrRow ) ); + rect.y = 0; + CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); + rect.width = cw - rect.x; + rect.height = m_colLabelHeight; + GetColLabelWindow()->Refresh( true, &rect ); + } } } else @@ -10630,14 +8525,14 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) // comment in SetColSize() for explanation of why this isn't done // in SetRowSize(). if ( !setAsMin ) - extentMax = wxMax(extentMax, GetRowMinimalHeight(row)); + extentMax = wxMax(extentMax, GetRowMinimalHeight(colOrRow)); - SetRowSize(row, extentMax); + SetRowSize(colOrRow, extentMax); if ( !GetBatchCount() ) { int cw, ch, dummy; m_gridWin->GetClientSize( &cw, &ch ); - wxRect rect( CellToRect( row, 0 ) ); + wxRect rect( CellToRect( colOrRow, 0 ) ); rect.x = 0; CalcScrolledPosition(0, rect.y, &dummy, &rect.y); rect.width = m_rowLabelWidth; @@ -10649,9 +8544,9 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) if ( setAsMin ) { if ( column ) - SetColMinimalWidth(col, extentMax); + SetColMinimalWidth(colOrRow, extentMax); else - SetRowMinimalHeight(row, extentMax); + SetRowMinimalHeight(colOrRow, extentMax); } } @@ -10756,10 +8651,8 @@ void wxGrid::AutoSize() // we know that we're not going to have scrollbars so disable them now to // avoid trouble in SetClientSize() which can otherwise set the correct // client size but also leave space for (not needed any more) scrollbars - SetScrollbars(0, 0, 0, 0, 0, 0, true); - - // restore the scroll rate parameters overwritten by SetScrollbars() - SetScrollRate(m_scrollLineX, m_scrollLineY); + SetScrollbars(m_xScrollPixelsPerLine, m_yScrollPixelsPerLine, + 0, 0, 0, 0, true); SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight); } @@ -10776,6 +8669,7 @@ void wxGrid::AutoSizeRowLabelSize( int row ) // autosize row height depending on label text SetRowSize(row, -1); + ForceRefresh(); } @@ -10791,23 +8685,19 @@ void wxGrid::AutoSizeColLabelSize( int col ) // autosize column width depending on label text SetColSize(col, -1); + ForceRefresh(); } wxSize wxGrid::DoGetBestSize() const { - wxGrid *self = (wxGrid *)this; // const_cast + wxGrid * const self = const_cast(this); // we do the same as in AutoSize() here with the exception that we don't // change the column/row sizes, only calculate them wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth, self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight); - // NOTE: This size should be cached, but first we need to add calls to - // InvalidateBestSize everywhere that could change the results of this - // calculation. - // CacheBestSize(size); - return wxSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight) + GetWindowBorderSize(); } @@ -10817,10 +8707,12 @@ void wxGrid::Fit() AutoSize(); } +#if WXWIN_COMPATIBILITY_2_8 wxPen& wxGrid::GetDividerPen() const { return wxNullPen; } +#endif // WXWIN_COMPATIBILITY_2_8 // ---------------------------------------------------------------------------- // cell value accessor functions @@ -10860,31 +8752,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() @@ -10900,46 +8797,41 @@ 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() || + mode == wxGrid::wxGridSelectRowsOrColumns ) { - 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 ) @@ -10951,17 +8843,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 @@ -11021,13 +8913,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(); } @@ -11158,6 +9057,57 @@ 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); + + // if it's not found return the default + if ( it == m_customSizes.end() ) + return m_sizeDefault; + + // otherwise return 0 if it's hidden, currently there is no way to get + // its size before it had been hidden + if ( it->second < 0 ) + return 0; + + return it->second; +} + // ---------------------------------------------------------------------------- // drop target // ---------------------------------------------------------------------------- @@ -11181,36 +9131,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); } @@ -11223,15 +9160,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); } @@ -11250,4 +9182,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(wxT(':'))); + 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(wxT(':')); + 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