X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1372f8ccd05daece34d4f44a07f95f45aee967de..c9c023ddeb1ba8d5c50b8a2d7718b1dd18665b2b:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 3506da69f0..1b1ed6c604 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,11 @@ #include "wx/spinctrl.h" #include "wx/tokenzr.h" #include "wx/renderer.h" +#include "wx/headerctrl.h" #include "wx/generic/gridsel.h" -const wxChar wxGridNameStr[] = wxT("grid"); +const char wxGridNameStr[] = "grid"; #if defined(__WXMOTIF__) #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier) @@ -136,6 +146,7 @@ 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_COL_SORT) DEFINE_EVENT_TYPE(wxEVT_GRID_RANGE_SELECT) DEFINE_EVENT_TYPE(wxEVT_GRID_CELL_CHANGE) DEFINE_EVENT_TYPE(wxEVT_GRID_SELECT_CELL) @@ -147,18 +158,195 @@ DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_CREATED) // private classes // ---------------------------------------------------------------------------- +// header column providing access to the column information stored in wxGrid +// via wxHeaderColumn interface +class wxGridHeaderColumn : public wxHeaderColumn +{ +public: + wxGridHeaderColumn(wxGrid *grid, int col) + : m_grid(grid), + m_col(col) + { + } + + virtual wxString GetTitle() const { return m_grid->GetColLabelValue(m_col); } + virtual wxBitmap GetBitmap() const { return wxNullBitmap; } + virtual int GetWidth() const { return m_grid->GetColSize(m_col); } + virtual int GetMinWidth() const { return 0; } + virtual wxAlignment GetAlignment() const + { + int horz, + vert; + m_grid->GetColLabelAlignment(&horz, &vert); + + return static_cast(horz); + } + + virtual int GetFlags() const + { + // we can't know in advance whether we can sort by this column or not + // with wxGrid API so suppose we can by default + int flags = wxCOL_SORTABLE; + if ( m_grid->CanDragColSize() ) + flags |= wxCOL_RESIZABLE; + if ( m_grid->CanDragColMove() ) + flags |= wxCOL_REORDERABLE; + if ( GetWidth() == 0 ) + flags |= wxCOL_HIDDEN; + + return flags; + } + + virtual bool IsSortKey() const + { + return m_grid->IsSortingBy(m_col); + } + + virtual bool IsSortOrderAscending() const + { + return m_grid->IsSortOrderAscending(); + } + +private: + // these really should be const but are not because the column needs to be + // assignable to be used in a wxVector (in STL build, in non-STL build we + // avoid the need for this) + wxGrid *m_grid; + int m_col; +}; + +// header control retreiving column information from the grid +class wxGridHeaderCtrl : public wxHeaderCtrl +{ +public: + wxGridHeaderCtrl(wxGrid *owner) + : wxHeaderCtrl(owner, + wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + wxHD_ALLOW_HIDE | + (owner->CanDragColMove() ? wxHD_ALLOW_REORDER : 0)) + { + } + +protected: + virtual const wxHeaderColumn& GetColumn(unsigned int idx) const + { + return m_columns[idx]; + } + +private: + wxGrid *GetOwner() const { return static_cast(GetParent()); } + + // override the base class method to update our m_columns array + virtual void OnColumnCountChanging(unsigned int count) + { + const unsigned countOld = m_columns.size(); + if ( count < countOld ) + { + // just discard the columns which don't exist any more (notice that + // we can't use resize() here as it would require the vector + // value_type, i.e. wxGridHeaderColumn to be default constructible, + // which it is not) + m_columns.erase(m_columns.begin() + count, m_columns.end()); + } + else // new columns added + { + // add columns for the new elements + for ( unsigned n = countOld; n < count; n++ ) + m_columns.push_back(wxGridHeaderColumn(GetOwner(), n)); + } + } + + // override to implement column auto sizing + virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle) + { + // TODO: currently grid doesn't support computing the column best width + // from its contents so we just use the best label width as is + GetOwner()->SetColSize(idx, widthTitle); + + return true; + } + + // overridden to react to the actions using the columns popup menu + virtual void UpdateColumnVisibility(unsigned int idx, bool show) + { + GetOwner()->SetColSize(idx, show ? wxGRID_AUTOSIZE : 0); + + // as this is done by the user we should notify the main program about + // it + GetOwner()->SendEvent(wxEVT_GRID_COL_SIZE, -1, idx); + } + + // overridden to react to the columns order changes in the customization + // dialog + virtual void UpdateColumnsOrder(const wxArrayInt& order) + { + GetOwner()->SetColumnsOrder(order); + } + + + // event handlers forwarding wxHeaderCtrl events to wxGrid + void OnClick(wxHeaderCtrlEvent& event) + { + GetOwner()->DoColHeaderClick(event.GetColumn()); + } + + void OnBeginResize(wxHeaderCtrlEvent& event) + { + GetOwner()->DoStartResizeCol(event.GetColumn()); + + event.Skip(); + } + + void OnResizing(wxHeaderCtrlEvent& event) + { + GetOwner()->DoUpdateResizeColWidth(event.GetWidth()); + } + + void OnEndResize(wxHeaderCtrlEvent& event) + { + GetOwner()->DoEndDragResizeCol(); + + event.Skip(); + } + + void OnBeginReorder(wxHeaderCtrlEvent& event) + { + GetOwner()->DoStartMoveCol(event.GetColumn()); + } + + void OnEndReorder(wxHeaderCtrlEvent& event) + { + GetOwner()->DoEndMoveCol(event.GetNewOrder()); + } + + wxVector m_columns; + + DECLARE_EVENT_TABLE() + DECLARE_NO_COPY_CLASS(wxGridHeaderCtrl) +}; + +BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl) + EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick) + + EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize) + EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing) + EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize) + + EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder) + EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder) +END_EVENT_TABLE() + // 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, + : wxWindow(owner, wxID_ANY, + wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | additionalStyle, name) { @@ -181,16 +369,17 @@ protected: class WXDLLIMPEXP_ADV wxGridRowLabelWindow : public wxGridSubwindow { public: - wxGridRowLabelWindow() { } - wxGridRowLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); + wxGridRowLabelWindow(wxGrid *parent) + : wxGridSubwindow(parent) + { + } + 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) }; @@ -199,16 +388,17 @@ private: class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxGridSubwindow { public: - wxGridColLabelWindow() { } - wxGridColLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); + wxGridColLabelWindow(wxGrid *parent) + : wxGridSubwindow(parent) + { + } + 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) }; @@ -217,16 +407,16 @@ private: class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxGridSubwindow { public: - wxGridCornerLabelWindow() { } - wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id, - const wxPoint &pos, const wxSize &size ); + wxGridCornerLabelWindow(wxGrid *parent) + : wxGridSubwindow(parent) + { + } 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) }; @@ -234,25 +424,19 @@ private: class WXDLLIMPEXP_ADV wxGridWindow : public wxGridSubwindow { public: - wxGridWindow() + wxGridWindow(wxGrid *parent) + : wxGridSubwindow(parent, + wxWANTS_CHARS | wxCLIP_CHILDREN, + "GridWindow") { - 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 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 ); @@ -262,7 +446,6 @@ private: void OnEraseBackground( wxEraseEvent& ); void OnFocus( wxFocusEvent& ); - DECLARE_DYNAMIC_CLASS(wxGridWindow) DECLARE_EVENT_TABLE() DECLARE_NO_COPY_CLASS(wxGridWindow) }; @@ -388,7 +571,7 @@ WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray, class WXDLLIMPEXP_ADV wxGridTypeRegistry { public: - wxGridTypeRegistry() {} + wxGridTypeRegistry() {} ~wxGridTypeRegistry(); void RegisterDataType(const wxString& typeName, @@ -414,6 +597,360 @@ private: wxGridDataTypeInfoArray m_typeinfo; }; +// ---------------------------------------------------------------------------- +// operations classes abstracting the difference between operating on rows and +// columns +// ---------------------------------------------------------------------------- + +// This class allows to write a function only once because by using its methods +// it will apply to both columns and rows. +// +// This is an abstract interface definition, the two concrete implementations +// below should be used when working with rows and columns respectively. +class wxGridOperations +{ +public: + // Returns the operations in the other direction, i.e. wxGridRowOperations + // if this object is a wxGridColumnOperations and vice versa. + virtual wxGridOperations& Dual() const = 0; + + // Return the number of rows or columns. + virtual int GetNumberOfLines(const wxGrid *grid) const = 0; + + // Return the selection mode which allows selecting rows or columns. + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const = 0; + + // Make a wxGridCellCoords from the given components: thisDir is row or + // column and otherDir is column or row + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const = 0; + + // Calculate the scrolled position of the given abscissa or ordinate. + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const = 0; + + // Selects the horizontal or vertical component from the given object. + virtual int Select(const wxGridCellCoords& coords) const = 0; + virtual int Select(const wxPoint& pt) const = 0; + virtual int Select(const wxSize& sz) const = 0; + virtual int Select(const wxRect& r) const = 0; + virtual int& Select(wxRect& r) const = 0; + + // Returns width or height of the rectangle + virtual int& SelectSize(wxRect& r) const = 0; + + // Make a wxSize such that Select() applied to it returns first component + virtual wxSize MakeSize(int first, int second) const = 0; + + // Sets the row or column component of the given cell coordinates + virtual void Set(wxGridCellCoords& coords, int line) const = 0; + + + // Draws a line parallel to the row or column, i.e. horizontal or vertical: + // pos is the horizontal or vertical position of the line and start and end + // are the coordinates of the line extremities in the other direction + virtual void + DrawParallelLine(wxDC& dc, int start, int end, int pos) const = 0; + + // Draw a horizontal or vertical line across the given rectangle + // (this is implemented in terms of above and uses Select() to extract + // start and end from the given rectangle) + void DrawParallelLineInRect(wxDC& dc, const wxRect& rect, int pos) const + { + const int posStart = Select(rect.GetPosition()); + DrawParallelLine(dc, posStart, posStart + Select(rect.GetSize()), pos); + } + + + // Return the index of the row or column at the given pixel coordinate. + virtual int + PosToLine(const wxGrid *grid, int pos, bool clip = false) const = 0; + + // Get the top/left position, in pixels, of the given row or column + virtual int GetLineStartPos(const wxGrid *grid, int line) const = 0; + + // Get the bottom/right position, in pixels, of the given row or column + virtual int GetLineEndPos(const wxGrid *grid, int line) const = 0; + + // Get the height/width of the given row/column + virtual int GetLineSize(const wxGrid *grid, int line) const = 0; + + // Get wxGrid::m_rowBottoms/m_colRights array + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const = 0; + + // Get default height row height or column width + virtual int GetDefaultLineSize(const wxGrid *grid) const = 0; + + // Return the minimal acceptable row height or column width + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const = 0; + + // Return the minimal row height or column width + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const = 0; + + // Set the row height or column width + virtual void SetLineSize(wxGrid *grid, int line, int size) const = 0; + + // True if rows/columns can be resized by user + virtual bool CanResizeLines(const wxGrid *grid) const = 0; + + + // Return the index of the line at the given position + // + // NB: currently this is always identity for the rows as reordering is only + // implemented for the lines + virtual int GetLineAt(const wxGrid *grid, int line) const = 0; + + + // Get the row or column label window + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const = 0; + + // Get the width or height of the row or column label window + virtual int GetHeaderWindowSize(wxGrid *grid) const = 0; + + + // This class is never used polymorphically but give it a virtual dtor + // anyhow to suppress g++ complaints about it + virtual ~wxGridOperations() { } +}; + +class wxGridRowOperations : public wxGridOperations +{ +public: + virtual wxGridOperations& Dual() const; + + virtual int GetNumberOfLines(const wxGrid *grid) const + { return grid->GetNumberRows(); } + + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const + { return wxGrid::wxGridSelectRows; } + + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const + { return wxGridCellCoords(thisDir, otherDir); } + + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const + { return grid->CalcScrolledPosition(wxPoint(pos, 0)).x; } + + virtual int Select(const wxGridCellCoords& c) const { return c.GetRow(); } + virtual int Select(const wxPoint& pt) const { return pt.x; } + virtual int Select(const wxSize& sz) const { return sz.x; } + virtual int Select(const wxRect& r) const { return r.x; } + virtual int& Select(wxRect& r) const { return r.x; } + virtual int& SelectSize(wxRect& r) const { return r.width; } + virtual wxSize MakeSize(int first, int second) const + { return wxSize(first, second); } + virtual void Set(wxGridCellCoords& coords, int line) const + { coords.SetRow(line); } + + virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const + { dc.DrawLine(start, pos, end, pos); } + + virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const + { return grid->YToRow(pos, clip); } + virtual int GetLineStartPos(const wxGrid *grid, int line) const + { return grid->GetRowTop(line); } + virtual int GetLineEndPos(const wxGrid *grid, int line) const + { return grid->GetRowBottom(line); } + virtual int GetLineSize(const wxGrid *grid, int line) const + { return grid->GetRowHeight(line); } + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const + { return grid->m_rowBottoms; } + virtual int GetDefaultLineSize(const wxGrid *grid) const + { return grid->GetDefaultRowSize(); } + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const + { return grid->GetRowMinimalAcceptableHeight(); } + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const + { return grid->GetRowMinimalHeight(line); } + virtual void SetLineSize(wxGrid *grid, int line, int size) const + { grid->SetRowSize(line, size); } + virtual bool CanResizeLines(const wxGrid *grid) const + { return grid->CanDragRowSize(); } + + virtual int GetLineAt(const wxGrid * WXUNUSED(grid), int line) const + { return line; } // TODO: implement row reordering + + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const + { return grid->GetGridRowLabelWindow(); } + virtual int GetHeaderWindowSize(wxGrid *grid) const + { return grid->GetRowLabelSize(); } +}; + +class wxGridColumnOperations : public wxGridOperations +{ +public: + virtual wxGridOperations& Dual() const; + + virtual int GetNumberOfLines(const wxGrid *grid) const + { return grid->GetNumberCols(); } + + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const + { return wxGrid::wxGridSelectColumns; } + + virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const + { return wxGridCellCoords(otherDir, thisDir); } + + virtual int CalcScrolledPosition(wxGrid *grid, int pos) const + { return grid->CalcScrolledPosition(wxPoint(0, pos)).y; } + + virtual int Select(const wxGridCellCoords& c) const { return c.GetCol(); } + virtual int Select(const wxPoint& pt) const { return pt.y; } + virtual int Select(const wxSize& sz) const { return sz.y; } + virtual int Select(const wxRect& r) const { return r.y; } + virtual int& Select(wxRect& r) const { return r.y; } + virtual int& SelectSize(wxRect& r) const { return r.height; } + virtual wxSize MakeSize(int first, int second) const + { return wxSize(second, first); } + virtual void Set(wxGridCellCoords& coords, int line) const + { coords.SetCol(line); } + + virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const + { dc.DrawLine(pos, start, pos, end); } + + virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const + { return grid->XToCol(pos, clip); } + virtual int GetLineStartPos(const wxGrid *grid, int line) const + { return grid->GetColLeft(line); } + virtual int GetLineEndPos(const wxGrid *grid, int line) const + { return grid->GetColRight(line); } + virtual int GetLineSize(const wxGrid *grid, int line) const + { return grid->GetColWidth(line); } + virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const + { return grid->m_colRights; } + virtual int GetDefaultLineSize(const wxGrid *grid) const + { return grid->GetDefaultColSize(); } + virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const + { return grid->GetColMinimalAcceptableWidth(); } + virtual int GetMinimalLineSize(const wxGrid *grid, int line) const + { return grid->GetColMinimalWidth(line); } + virtual void SetLineSize(wxGrid *grid, int line, int size) const + { grid->SetColSize(line, size); } + virtual bool CanResizeLines(const wxGrid *grid) const + { return grid->CanDragColSize(); } + + virtual int GetLineAt(const wxGrid *grid, int line) const + { return grid->GetColAt(line); } + + virtual wxWindow *GetHeaderWindow(wxGrid *grid) const + { return grid->GetGridColLabelWindow(); } + virtual int GetHeaderWindowSize(wxGrid *grid) const + { return grid->GetColLabelSize(); } +}; + +wxGridOperations& wxGridRowOperations::Dual() const +{ + static wxGridColumnOperations s_colOper; + + return s_colOper; +} + +wxGridOperations& wxGridColumnOperations::Dual() const +{ + static wxGridRowOperations s_rowOper; + + return s_rowOper; +} + +// This class abstracts the difference between operations going forward +// (down/right) and backward (up/left) and allows to use the same code for +// functions which differ only in the direction of grid traversal +// +// Like wxGridOperations it's an ABC with two concrete subclasses below. Unlike +// it, this is a normal object and not just a function dispatch table and has a +// non-default ctor. +// +// Note: the explanation of this discrepancy is the existence of (very useful) +// Dual() method in wxGridOperations which forces us to make wxGridOperations a +// function dispatcher only. +class wxGridDirectionOperations +{ +public: + // The oper parameter to ctor selects whether we work with rows or columns + wxGridDirectionOperations(wxGrid *grid, const wxGridOperations& oper) + : m_grid(grid), + m_oper(oper) + { + } + + // Check if the component of this point in our direction is at the + // boundary, i.e. is the first/last row/column + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const = 0; + + // Increment the component of this point in our direction + virtual void Advance(wxGridCellCoords& coords) const = 0; + + // Find the line at the given distance, in pixels, away from this one + // (this uses clipping, i.e. anything after the last line is counted as the + // last one and anything before the first one as 0) + virtual int MoveByPixelDistance(int line, int distance) const = 0; + + // This class is never used polymorphically but give it a virtual dtor + // anyhow to suppress g++ complaints about it + virtual ~wxGridDirectionOperations() { } + +protected: + wxGrid * const m_grid; + const wxGridOperations& m_oper; +}; + +class wxGridBackwardOperations : public wxGridDirectionOperations +{ +public: + wxGridBackwardOperations(wxGrid *grid, const wxGridOperations& oper) + : wxGridDirectionOperations(grid, oper) + { + } + + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const + { + wxASSERT_MSG( m_oper.Select(coords) >= 0, "invalid row/column" ); + + return m_oper.Select(coords) == 0; + } + + virtual void Advance(wxGridCellCoords& coords) const + { + wxASSERT( !IsAtBoundary(coords) ); + + m_oper.Set(coords, m_oper.Select(coords) - 1); + } + + virtual int MoveByPixelDistance(int line, int distance) const + { + int pos = m_oper.GetLineStartPos(m_grid, line); + return m_oper.PosToLine(m_grid, pos - distance + 1, true); + } +}; + +class wxGridForwardOperations : public wxGridDirectionOperations +{ +public: + wxGridForwardOperations(wxGrid *grid, const wxGridOperations& oper) + : wxGridDirectionOperations(grid, oper), + m_numLines(oper.GetNumberOfLines(grid)) + { + } + + virtual bool IsAtBoundary(const wxGridCellCoords& coords) const + { + wxASSERT_MSG( m_oper.Select(coords) < m_numLines, "invalid row/column" ); + + return m_oper.Select(coords) == m_numLines - 1; + } + + virtual void Advance(wxGridCellCoords& coords) const + { + wxASSERT( !IsAtBoundary(coords) ); + + m_oper.Set(coords, m_oper.Select(coords) + 1); + } + + virtual int MoveByPixelDistance(int line, int distance) const + { + int pos = m_oper.GetLineStartPos(m_grid, line); + return m_oper.PosToLine(m_grid, pos + distance, true); + } + +private: + const int m_numLines; +}; + // ---------------------------------------------------------------------------- // globals // ---------------------------------------------------------------------------- @@ -431,13 +968,22 @@ private: wxGridCellCoords wxGridNoCellCoords( -1, -1 ); wxRect wxGridNoCellRect( -1, -1, -1, -1 ); +namespace +{ + // scroll line size -static const size_t GRID_SCROLL_LINE_X = 15; -static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X; +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) -static const int GRID_HASH_SIZE = 100; +const int GRID_HASH_SIZE = 100; + +// the minimal distance in pixels the mouse needs to move to start a drag +// operation +const int DRAG_SENSITIVITY = 3; + +} // anonymous namespace // ---------------------------------------------------------------------------- // private helpers @@ -593,32 +1139,19 @@ bool wxGridCellEditor::IsAcceptedKey(wxKeyEvent& event) 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); - } + if ( event.GetUnicodeKey() > 127 && event.GetKeyCode() > 127 ) + return false; #else - key = event.GetKeyCode(); - keyOk = (key <= 255); + if ( event.GetKeyCode() > 255 ) + return false; #endif - return keyOk; + return true; } void wxGridCellEditor::StartingKey(wxKeyEvent& event) @@ -2651,7 +3184,7 @@ void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col) wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = FindIndex(row, col); if ( n != wxNOT_FOUND ) @@ -2761,7 +3294,7 @@ wxGridRowOrColAttrData::~wxGridRowOrColAttrData() wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; int n = m_rowsOrCols.Index(rowOrCol); if ( n != wxNOT_FOUND ) @@ -2847,7 +3380,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols wxGridCellAttrProvider::wxGridCellAttrProvider() { - m_data = (wxGridCellAttrProviderData *)NULL; + m_data = NULL; } wxGridCellAttrProvider::~wxGridCellAttrProvider() @@ -2863,7 +3396,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) @@ -3165,8 +3698,8 @@ IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject ) wxGridTableBase::wxGridTableBase() { - m_view = (wxGrid *) NULL; - m_attrProvider = (wxGridCellAttrProvider *) NULL; + m_view = NULL; + m_attrProvider = NULL; } wxGridTableBase::~wxGridTableBase() @@ -3196,7 +3729,7 @@ wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAtt if ( m_attrProvider ) return m_attrProvider->GetAttr(row, col, kind); else - return (wxGridCellAttr *)NULL; + return NULL; } void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col) @@ -3395,7 +3928,7 @@ void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col), wxGridTableMessage::wxGridTableMessage() { - m_table = (wxGridTableBase *) NULL; + m_table = NULL; m_id = -1; m_comInt1 = -1; m_comInt2 = -1; @@ -3471,15 +4004,6 @@ void wxGridStringTable::SetValue( int row, int col, const wxString& value ) m_data[row][col] = value; } -bool wxGridStringTable::IsEmptyCell( int row, int col ) -{ - wxCHECK_MSG( (row < GetNumberRows()) && (col < GetNumberCols()), - true, - _T("invalid row or column index in wxGridStringTable") ); - - return (m_data[row][col] == wxEmptyString); -} - void wxGridStringTable::Clear() { int row, col; @@ -3807,22 +4331,12 @@ void wxGridSubwindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event m_owner->CancelMouseCapture(); } -IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridRowLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent ) END_EVENT_TABLE() -wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); @@ -3849,27 +4363,18 @@ void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); + if (!m_owner->GetEventHandler()->ProcessEvent( event )) + event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridColLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent ) END_EVENT_TABLE() -wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint &pos, const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); @@ -3899,28 +4404,18 @@ void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); + if (!m_owner->GetEventHandler()->ProcessEvent( event )) + event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow ) EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent ) EVT_PAINT( wxGridCornerLabelWindow::OnPaint ) END_EVENT_TABLE() -wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size ) - : wxGridSubwindow(parent, id, pos, size) -{ - m_owner = parent; -} - void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) { wxPaintDC dc(this); @@ -3935,13 +4430,12 @@ void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent(event); + if (!m_owner->GetEventHandler()->ProcessEvent(event)) + event.Skip(); } ////////////////////////////////////////////////////////////////////// -IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow ) - BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) EVT_PAINT( wxGridWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel ) @@ -3954,21 +4448,6 @@ BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground ) END_EVENT_TABLE() -wxGridWindow::wxGridWindow( wxGrid *parent, - wxGridRowLabelWindow *rowLblWin, - wxGridColLabelWindow *colLblWin, - wxWindowID id, - const wxPoint &pos, - const wxSize &size ) - : wxGridSubwindow(parent, id, pos, size, - wxWANTS_CHARS | wxCLIP_CHILDREN, - wxT("grid window") ) -{ - m_owner = parent; - m_rowLabelWin = rowLblWin; - m_colLabelWin = colLblWin; -} - void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPaintDC dc( this ); @@ -3977,17 +4456,18 @@ void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); m_owner->DrawGridCellArea( dc, dirtyCells ); + m_owner->DrawGridSpace( dc ); + m_owner->DrawAllGridLines( dc, reg ); - m_owner->DrawGridSpace( dc ); m_owner->DrawHighlight( dc, dirtyCells ); } void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect ) { wxWindow::ScrollWindow( dx, dy, rect ); - m_rowLabelWin->ScrollWindow( 0, dy, rect ); - m_colLabelWin->ScrollWindow( dx, 0, rect ); + m_owner->GetGridRowLabelWindow()->ScrollWindow( 0, dy, rect ); + m_owner->GetGridColLabelWindow()->ScrollWindow( dx, 0, rect ); } void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) @@ -4000,7 +4480,8 @@ void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) void wxGridWindow::OnMouseWheel( wxMouseEvent& event ) { - m_owner->GetEventHandler()->ProcessEvent( event ); + if (!m_owner->GetEventHandler()->ProcessEvent( event )) + event.Skip(); } // This seems to be required for wxMotif/wxGTK otherwise the mouse @@ -4057,23 +4538,8 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) event.Skip(); } -////////////////////////////////////////////////////////////////////// - -// Internal Helper function for computing row or column from some -// (unscrolled) coordinate value, using either -// m_defaultRowHeight/m_defaultColWidth or binary search on array -// of m_rowBottoms/m_ColRights to speed up the search! - -// Internal helper macros for simpler use of that function - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax); - #define internalXToCol(x) XToCol(x, true) -#define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \ - m_minAcceptableRowHeight, \ - m_rowBottoms, m_numRows, true) +#define internalYToRow(y) YToRow(y, true) ///////////////////////////////////////////////////////////////////// @@ -4138,22 +4604,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) @@ -4175,6 +4625,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(); @@ -4242,31 +4700,11 @@ void wxGrid::Create() m_numCols = 0; m_currentCellCoords = wxGridNoCellCoords; - m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; - m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; - // subwindow components that make up the wxGrid - m_rowLabelWin = new wxGridRowLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_colLabelWin = new wxGridColLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_cornerLabelWin = new wxGridCornerLabelWindow( this, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); - - m_gridWin = new wxGridWindow( this, - m_rowLabelWin, - m_colLabelWin, - wxID_ANY, - wxDefaultPosition, - wxDefaultSize ); + m_rowLabelWin = new wxGridRowLabelWindow(this); + CreateColumnWindow(); + m_cornerLabelWin = new wxGridCornerLabelWindow(this); + m_gridWin = new wxGridWindow( this ); SetTargetWindow( m_gridWin ); @@ -4286,13 +4724,38 @@ void wxGrid::Create() m_cornerLabelWin->SetOwnBackgroundColour(lbg); m_rowLabelWin->SetOwnForegroundColour(lfg); m_rowLabelWin->SetOwnBackgroundColour(lbg); - m_colLabelWin->SetOwnForegroundColour(lfg); - m_colLabelWin->SetOwnBackgroundColour(lbg); + m_colWindow->SetOwnForegroundColour(lfg); + m_colWindow->SetOwnBackgroundColour(lbg); m_gridWin->SetOwnForegroundColour(gfg); m_gridWin->SetOwnBackgroundColour(gbg); - Init(); + m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); + m_labelTextColour = m_rowLabelWin->GetForegroundColour(); + + // now that we have the grid window, use its font to compute the default + // row height + m_defaultRowHeight = m_gridWin->GetCharHeight(); +#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl() + m_defaultRowHeight += 8; +#else + m_defaultRowHeight += 4; +#endif + +} + +void wxGrid::CreateColumnWindow() +{ + if ( m_useNativeHeader ) + { + m_colWindow = new wxGridHeaderCtrl(this); + m_colLabelHeight = m_colWindow->GetBestSize().y; + } + else // draw labels ourselves + { + m_colWindow = new wxGridColLabelWindow(this); + m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + } } bool wxGrid::CreateGrid( int numRows, int numCols, @@ -4360,6 +4823,9 @@ wxGrid::SetTable(wxGridTableBase *table, m_numRows = table->GetNumberRows(); m_numCols = table->GetNumberCols(); + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + m_table = table; m_table->SetView( this ); m_ownTable = takeOwnership; @@ -4369,22 +4835,22 @@ wxGrid::SetTable(wxGridTableBase *table, // If the newly set table is smaller than the // original one current cell and selection regions // might be invalid, - m_selectingKeyboard = wxGridNoCellCoords; + m_selectedBlockCorner = wxGridNoCellCoords; m_currentCellCoords = wxGridCellCoords(wxMin(m_numRows, m_currentCellCoords.GetRow()), wxMin(m_numCols, m_currentCellCoords.GetCol())); - if (m_selectingTopLeft.GetRow() >= m_numRows || - m_selectingTopLeft.GetCol() >= m_numCols) + if (m_selectedBlockTopLeft.GetRow() >= m_numRows || + m_selectedBlockTopLeft.GetCol() >= m_numCols) { - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; } else - m_selectingBottomRight = + m_selectedBlockBottomRight = wxGridCellCoords(wxMin(m_numRows, - m_selectingBottomRight.GetRow()), + m_selectedBlockBottomRight.GetRow()), wxMin(m_numCols, - m_selectingBottomRight.GetCol())); + m_selectedBlockBottomRight.GetCol())); } CalcDimensions(); @@ -4394,13 +4860,13 @@ wxGrid::SetTable(wxGridTableBase *table, return m_created; } -void wxGrid::InitVars() +void wxGrid::Init() { m_created = false; m_cornerLabelWin = NULL; m_rowLabelWin = NULL; - m_colLabelWin = NULL; + m_colWindow = NULL; m_gridWin = NULL; m_table = NULL; @@ -4410,24 +4876,10 @@ void wxGrid::InitVars() m_defaultCellAttr = NULL; m_typeRegistry = NULL; m_winCapture = NULL; -} -void wxGrid::Init() -{ m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; - if ( m_rowLabelWin ) - { - m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour(); - } - else - { - m_labelBackgroundColour = *wxWHITE; - } - - m_labelTextColour = *wxBLACK; - // init attr cache m_attrCache.row = -1; m_attrCache.col = -1; @@ -4444,19 +4896,15 @@ void wxGrid::Init() m_colLabelTextOrientation = wxHORIZONTAL; m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH; - m_defaultRowHeight = m_gridWin->GetCharHeight(); + m_defaultRowHeight = 0; // this will be initialized after creation m_minAcceptableColWidth = WXGRID_MIN_COL_WIDTH; m_minAcceptableRowHeight = WXGRID_MIN_ROW_HEIGHT; -#if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl() - m_defaultRowHeight += 8; -#else - m_defaultRowHeight += 4; -#endif - m_gridLineColour = wxColour( 192,192,192 ); m_gridLinesEnabled = true; + m_gridLinesClipHorz = + m_gridLinesClipVert = true; m_cellHighlightColour = *wxBLACK; m_cellHighlightPenWidth = 2; m_cellHighlightROPenWidth = 1; @@ -4464,7 +4912,7 @@ void wxGrid::Init() m_canDragColMove = false; m_cursorMode = WXGRID_CURSOR_SELECT_CELL; - m_winCapture = (wxWindow *)NULL; + m_winCapture = NULL; m_canDragRowSize = true; m_canDragColSize = true; m_canDragGridSize = true; @@ -4473,6 +4921,11 @@ void wxGrid::Init() m_dragRowOrCol = -1; m_isDragging = false; m_startDragPos = wxDefaultPosition; + + m_sortCol = wxNOT_FOUND; + m_sortIsAscending = true; + + m_useNativeHeader = m_nativeColumnLabels = false; m_waitForSlowClick = false; @@ -4482,7 +4935,9 @@ void wxGrid::Init() m_currentCellCoords = wxGridNoCellCoords; - ClearSelection(); + m_selectedBlockTopLeft = + m_selectedBlockBottomRight = + m_selectedBlockCorner = wxGridNoCellCoords; m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); @@ -4538,10 +4993,9 @@ void wxGrid::InitColWidths() m_colWidths.Add( m_defaultColWidth, m_numCols ); - int colRight = 0; for ( int i = 0; i < m_numCols; i++ ) { - colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth; + int colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth; m_colRights.Add( colRight ); } } @@ -4663,8 +5117,8 @@ void wxGrid::CalcWindowSizes() if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() ) m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight ); - if ( m_colLabelWin && m_colLabelWin->IsShown() ) - m_colLabelWin->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); + if ( m_colWindow && m_colWindow->IsShown() ) + m_colWindow->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); if ( m_rowLabelWin && m_rowLabelWin->IsShown() ) m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh ); @@ -4692,20 +5146,6 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) // cell it might want to save that stuff to might no longer exist. HideCellEditControl(); -#if 0 - // if we were using the default widths/heights so far, we must change them - // now - if ( m_colWidths.IsEmpty() ) - { - InitColWidths(); - } - - if ( m_rowHeights.IsEmpty() ) - { - InitRowHeights(); - } -#endif - switch ( msg.GetId() ) { case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: @@ -4856,6 +5296,9 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int numCols = msg.GetCommandInt2(); m_numCols += numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + if ( !m_colAt.IsEmpty() ) { //Shift the column IDs @@ -4910,7 +5353,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4921,6 +5364,8 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) int numCols = msg.GetCommandInt(); int oldNumCols = m_numCols; m_numCols += numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -4963,7 +5408,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -4974,6 +5419,8 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) size_t pos = msg.GetCommandInt(); int numCols = msg.GetCommandInt2(); m_numCols -= numCols; + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); if ( !m_colAt.IsEmpty() ) { @@ -5038,7 +5485,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } result = true; @@ -5188,9 +5635,8 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); // find the cells within these bounds - // - int row, col; - for ( row = internalYToRow(top); row < m_numRows; row++ ) + wxArrayInt cols; + for ( int row = internalYToRow(top); row < m_numRows; row++ ) { if ( GetRowBottom(row) <= top ) continue; @@ -5198,19 +5644,23 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const if ( GetRowTop(row) > bottom ) break; - int colPos; - for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ ) + // add all dirty cells in this row: notice that the columns which + // are dirty don't depend on the row so we compute them only once + // for the first dirty row and then reuse for all the next ones + if ( cols.empty() ) { - col = GetColAt( colPos ); + // do determine the dirty columns + for ( int pos = XToPos(left); pos <= XToPos(right); pos++ ) + cols.push_back(GetColAt(pos)); - if ( GetColRight(col) <= left ) - continue; - - if ( GetColLeft(col) > right ) + // if there are no dirty columns at all, nothing to do + if ( cols.empty() ) break; - - cellsExposed.Add( wxGridCellCoords( row, col ) ); } + + const size_t count = cols.size(); + for ( size_t n = 0; n < count; n++ ) + cellsExposed.Add(wxGridCellCoords(row, cols[n])); } ++iter; @@ -5264,13 +5714,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) if ( (row = YToRow( y )) >= 0 ) { if ( m_selection ) - { - m_selection->SelectRow( row, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->SelectRow(row, event); } } break; @@ -5321,22 +5765,16 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) { if ( event.ShiftDown() ) { - m_selection->SelectBlock( m_currentCellCoords.GetRow(), - 0, - row, - GetNumberCols() - 1, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectBlock + ( + m_currentCellCoords.GetRow(), 0, + row, GetNumberCols() - 1, + event + ); } else { - m_selection->SelectRow( row, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectRow(row, event); } } @@ -5370,7 +5808,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // adjust row height depending on label text AutoSizeRowLabelSize( row ); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } } @@ -5438,21 +5876,107 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) } } +void wxGrid::UpdateColumnSortingIndicator(int col) +{ + wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" ); + + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + else if ( m_nativeColumnLabels ) + m_colWindow->Refresh(); + //else: sorting indicator display not yet implemented in grid version +} + +void wxGrid::SetSortingColumn(int col, bool ascending) +{ + if ( col == m_sortCol ) + { + // we are already using this column for sorting (or not sorting at all) + // but we might still change the sorting order, check for it + if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending ) + { + m_sortIsAscending = ascending; + + UpdateColumnSortingIndicator(m_sortCol); + } + } + else // we're changing the column used for sorting + { + const int sortColOld = m_sortCol; + + // change it before updating the column as we want GetSortingColumn() + // to return the correct new value + m_sortCol = col; + + if ( sortColOld != wxNOT_FOUND ) + UpdateColumnSortingIndicator(sortColOld); + + if ( m_sortCol != wxNOT_FOUND ) + { + m_sortIsAscending = ascending; + UpdateColumnSortingIndicator(m_sortCol); + } + } +} + +void wxGrid::DoColHeaderClick(int col) +{ + // we consider that the grid was resorted if this event is processed and + // not vetoed + if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 ) + { + SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true); + Refresh(); + } +} + +void wxGrid::DoStartResizeCol(int col) +{ + m_dragRowOrCol = col; + m_dragLastPos = -1; + DoUpdateResizeColWidth(GetColWidth(m_dragRowOrCol)); +} + +void wxGrid::DoUpdateResizeCol(int x) +{ + int cw, ch, dummy, top; + m_gridWin->GetClientSize( &cw, &ch ); + CalcUnscrolledPosition( 0, 0, &dummy, &top ); + + wxClientDC dc( m_gridWin ); + PrepareDC( dc ); + + x = wxMax( x, GetColLeft(m_dragRowOrCol) + GetColMinimalWidth(m_dragRowOrCol)); + dc.SetLogicalFunction(wxINVERT); + if ( m_dragLastPos >= 0 ) + { + dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); + } + dc.DrawLine( x, top, x, top + ch ); + m_dragLastPos = x; +} + +void wxGrid::DoUpdateResizeColWidth(int w) +{ + DoUpdateResizeCol(GetColLeft(m_dragRowOrCol) + w); +} + void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { - int x, y, col; + int x, y; wxPoint pos( event.GetPosition() ); CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); + int col = XToCol(x); if ( event.Dragging() ) { if (!m_isDragging) { m_isDragging = true; - m_colLabelWin->CaptureMouse(); + GetColLabelWindow()->CaptureMouse(); - if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL ) - m_dragRowOrCol = XToCol( x ); + if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 ) + DoStartMoveCol(col); } if ( event.LeftIsDown() ) @@ -5460,78 +5984,45 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - { - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); - - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - - x = wxMax( x, GetColLeft(m_dragRowOrCol) + - GetColMinimalWidth(m_dragRowOrCol)); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - } - dc.DrawLine( x, top, x, top + ch ); - m_dragLastPos = x; - } + DoUpdateResizeCol(x); break; case WXGRID_CURSOR_SELECT_COL: { - if ( (col = XToCol( x )) >= 0 ) + if ( col != -1 ) { if ( m_selection ) - { - m_selection->SelectCol( col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } + m_selection->SelectCol(col, event); } } break; case WXGRID_CURSOR_MOVE_COL: { - if ( x < 0 ) - m_moveToCol = GetColAt( 0 ); - else - m_moveToCol = XToCol( x ); + int posNew = XToPos(x); + int colNew = GetColAt(posNew); + // determine the position of the drop marker int markerX; - - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); - else if ( x >= (GetColLeft( m_moveToCol ) + (GetColWidth(m_moveToCol) / 2)) ) - { - m_moveToCol = GetColAt( GetColPos( m_moveToCol ) + 1 ); - if ( m_moveToCol < 0 ) - markerX = GetColRight( GetColAt( m_numCols - 1 ) ); - else - markerX = GetColLeft( m_moveToCol ); - } + if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) ) + markerX = GetColRight(colNew); else - markerX = GetColLeft( m_moveToCol ); + markerX = GetColLeft(colNew); if ( markerX != m_dragLastPos ) { - wxClientDC dc( m_colLabelWin ); + wxClientDC dc( GetColLabelWindow() ); DoPrepareDC(dc); int cw, ch; - m_colLabelWin->GetClientSize( &cw, &ch ); + GetColLabelWindow()->GetClientSize( &cw, &ch ); markerX++; //Clean up the last indicator if ( m_dragLastPos >= 0 ) { - wxPen pen( m_colLabelWin->GetBackgroundColour(), 2 ); + wxPen pen( GetColLabelWindow()->GetBackgroundColour(), 2 ); dc.SetPen(pen); dc.DrawLine( m_dragLastPos + 1, 0, m_dragLastPos + 1, ch ); dc.SetPen(wxNullPen); @@ -5542,9 +6033,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) const wxColour *color; //Moving to the same place? Don't draw a marker - if ( (m_moveToCol == m_dragRowOrCol) - || (GetColPos( m_moveToCol ) == GetColPos( m_dragRowOrCol ) + 1) - || (m_moveToCol < 0 && m_dragRowOrCol == GetColAt( m_numCols - 1 ))) + if ( colNew == m_dragRowOrCol ) color = wxLIGHT_GREY; else color = wxBLUE; @@ -5576,8 +6065,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) if (m_isDragging) { - if (m_colLabelWin->HasCapture()) - m_colLabelWin->ReleaseMouse(); + if (GetColLabelWindow()->HasCapture()) + GetColLabelWindow()->ReleaseMouse(); m_isDragging = false; } @@ -5585,7 +6074,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( event.Entering() || event.Leaving() ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); } // ------------ Left button pressed @@ -5598,21 +6087,20 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( XToEdgeOfCol(x) < 0 ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) ) { if ( m_canDragColMove ) { //Show button as pressed - wxClientDC dc( m_colLabelWin ); + wxClientDC dc( GetColLabelWindow() ); int colLeft = GetColLeft( col ); int colRight = GetColRight( col ) - 1; - dc.SetPen( wxPen( m_colLabelWin->GetBackgroundColour(), 1 ) ); + dc.SetPen( wxPen( GetColLabelWindow()->GetBackgroundColour(), 1 ) ); dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 ); dc.DrawLine( colLeft, 1, colRight, 1 ); - ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, GetColLabelWindow()); } else { @@ -5622,25 +6110,20 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { if ( event.ShiftDown() ) { - m_selection->SelectBlock( 0, - m_currentCellCoords.GetCol(), - GetNumberRows() - 1, col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectBlock + ( + 0, m_currentCellCoords.GetCol(), + GetNumberRows() - 1, col, + event + ); } else { - m_selection->SelectCol( col, - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); + m_selection->SelectCol(col, event); } } - ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, GetColLabelWindow()); } } } @@ -5649,7 +6132,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // starting to drag-resize a col // if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); } } @@ -5657,10 +6140,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( event.LeftDClick() ) { - col = XToEdgeOfCol(x); - if ( col < 0 ) + const int colEdge = XToEdgeOfCol(x); + if ( colEdge == -1 ) { - col = XToCol(x); if ( col >= 0 && ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) ) { @@ -5670,9 +6152,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) else { // adjust column width depending on label text - AutoSizeColLabelSize( col ); + AutoSizeColLabelSize( colEdge ); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } } @@ -5685,28 +6167,32 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { case WXGRID_CURSOR_RESIZE_COL: DoEndDragResizeCol(); - - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event ); break; case WXGRID_CURSOR_MOVE_COL: - DoEndDragMoveCol(); - - SendEvent( wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol, event ); + if ( m_dragLastPos == -1 || col == m_dragRowOrCol ) + { + // the column didn't actually move anywhere + if ( col != -1 ) + DoColHeaderClick(col); + m_colWindow->Refresh(); // "unpress" the column + } + else + { + DoEndMoveCol(XToPos(x)); + } break; case WXGRID_CURSOR_SELECT_COL: case WXGRID_CURSOR_SELECT_CELL: case WXGRID_CURSOR_RESIZE_ROW: case WXGRID_CURSOR_SELECT_ROW: - // nothing to do (?) + if ( col != -1 ) + DoColHeaderClick(col); break; } - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; } @@ -5714,7 +6200,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDown() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) ) { @@ -5726,7 +6211,6 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDClick() ) { - col = XToCol(x); if ( col >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) ) { @@ -5745,12 +6229,12 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { // don't capture the cursor yet if ( CanDragColSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow(), false); } } } @@ -5793,6 +6277,8 @@ void wxGrid::CancelMouseCapture() if ( m_winCapture ) { m_isDragging = false; + m_startDragPos = wxDefaultPosition; + m_cursorMode = WXGRID_CURSOR_SELECT_CELL; m_winCapture->SetCursor( *wxSTANDARD_CURSOR ); m_winCapture = NULL; @@ -5819,9 +6305,9 @@ void wxGrid::ChangeCursorMode(CursorMode mode, wxLogTrace(_T("grid"), _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"), - win == m_colLabelWin ? _T("colLabelWin") - : win ? _T("rowLabelWin") - : _T("gridWin"), + win == m_colWindow ? _T("colLabelWin") + : win ? _T("rowLabelWin") + : _T("gridWin"), cursorModes[m_cursorMode], cursorModes[mode]); #endif @@ -5838,9 +6324,8 @@ void wxGrid::ChangeCursorMode(CursorMode mode, if ( m_winCapture ) { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = (wxWindow *)NULL; + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; } m_cursorMode = mode; @@ -5875,797 +6360,673 @@ void wxGrid::ChangeCursorMode(CursorMode mode, } } -void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) -{ - int x, y; - wxPoint pos( event.GetPosition() ); - CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); +// ---------------------------------------------------------------------------- +// grid mouse event processing +// ---------------------------------------------------------------------------- - wxGridCellCoords coords; - XYToCell( x, y, coords ); +void +wxGrid::DoGridCellDrag(wxMouseEvent& event, + const wxGridCellCoords& coords, + bool isFirstDrag) +{ + if ( coords == wxGridNoCellCoords ) + return; // we're outside any valid cell - int cell_rows, cell_cols; - bool isFirstDrag = !m_isDragging; - GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); - if ((cell_rows < 0) || (cell_cols < 0)) + // Hide the edit control, so it won't interfere with drag-shrinking. + if ( IsCellEditControlShown() ) { - coords.SetRow(coords.GetRow() + cell_rows); - coords.SetCol(coords.GetCol() + cell_cols); + HideCellEditControl(); + SaveEditControlValue(); } - if ( event.Dragging() ) + switch ( event.GetModifiers() ) { - //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol()); - - // Don't start doing anything until the mouse has been dragged at - // least 3 pixels in any direction... - if (! m_isDragging) - { - if (m_startDragPos == wxDefaultPosition) - { - m_startDragPos = pos; - return; - } - if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4) - return; - } - - m_isDragging = true; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - // Hide the edit control, so it - // won't interfere with drag-shrinking. - if ( IsCellEditControlShown() ) - { - HideCellEditControl(); - SaveEditControlValue(); - } + case wxMOD_CMD: + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; + UpdateBlockBeingSelected(m_selectedBlockCorner, coords); + break; - if ( coords != wxGridNoCellCoords ) + case wxMOD_NONE: + if ( CanDragCell() ) { - if ( event.CmdDown() ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - HighlightBlock( m_selectingKeyboard, coords ); - } - else if ( CanDragCell() ) - { - if ( isFirstDrag ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords) - m_selectingKeyboard = coords; - - SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG, - coords.GetRow(), - coords.GetCol(), - event ); - return; - } - } - else + if ( isFirstDrag ) { - if ( !IsSelection() ) - { - HighlightBlock( coords, coords ); - } - else - { - HighlightBlock( m_currentCellCoords, coords ); - } - } + if ( m_selectedBlockCorner == wxGridNoCellCoords) + m_selectedBlockCorner = coords; - if (! IsVisible(coords)) - { - MakeCellVisible(coords); - // TODO: need to introduce a delay or something here. The - // scrolling is way too fast, at least under MSW and GTK. + SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event); + return; } } - // Have we captured the mouse yet? - if (! m_winCapture) - { - m_winCapture = m_gridWin; - m_winCapture->CaptureMouse(); - } + UpdateBlockBeingSelected(m_currentCellCoords, coords); + break; - } - else if ( event.LeftIsDown() && - m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) - { - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); + default: + // we don't handle the other key modifiers + event.Skip(); + } +} - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - y = wxMax( y, GetRowTop(m_dragRowOrCol) + - GetRowMinimalHeight(m_dragRowOrCol) ); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos ); - } - dc.DrawLine( left, y, left+cw, y ); - m_dragLastPos = y; - } - else if ( event.LeftIsDown() && - m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) - { - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); +void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper) +{ + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - x = wxMax( x, GetColLeft(m_dragRowOrCol) + - GetColMinimalWidth(m_dragRowOrCol) ); - dc.SetLogicalFunction(wxINVERT); - if ( m_dragLastPos >= 0 ) - { - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - } - dc.DrawLine( x, top, x, top + ch ); - m_dragLastPos = x; - } + const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)), + m_gridWin->GetClientSize()); - return; - } + // erase the previously drawn line, if any + if ( m_dragLastPos >= 0 ) + oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); - m_isDragging = false; - m_startDragPos = wxDefaultPosition; + // we need the vertical position for rows and horizontal for columns here + m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition())); - // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL - // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under - // wxGTK -#if 0 - if ( event.Entering() || event.Leaving() ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - m_gridWin->SetCursor( *wxSTANDARD_CURSOR ); - } - else -#endif // 0 + // 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; - // ------------ Left button pressed - // - if ( event.LeftDown() && coords != wxGridNoCellCoords ) + // and draw it at the new position + oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); +} + +void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords) +{ + if ( !m_isDragging ) { - if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) + // Don't start doing anything until the mouse has been dragged far + // enough + const wxPoint& pt = event.GetPosition(); + if ( m_startDragPos == wxDefaultPosition ) { - 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 ); - - if ( event.CmdDown() ) - { - if ( m_selection ) - { - m_selection->ToggleCellSelection( coords.GetRow(), - coords.GetCol(), - event.ControlDown(), - event.ShiftDown(), - event.AltDown(), - event.MetaDown() ); - } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; - m_selectingKeyboard = coords; - } - else - { - m_waitForSlowClick = m_currentCellCoords == coords && - coords != wxGridNoCellCoords; - SetCurrentCell( coords ); - } - } + m_startDragPos = pt; + return; } + + if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY && + abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY ) + return; } - // ------------ Left double click - // - else if ( event.LeftDClick() && coords != wxGridNoCellCoords ) - { - DisableCellEditControl(); + const bool isFirstDrag = !m_isDragging; + m_isDragging = true; - if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 ) - { - 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; - } - } - } - - // ------------ Left button released - // - else if ( event.LeftUp() ) + switch ( m_cursorMode ) { - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if (m_winCapture) - { - if (m_winCapture->HasCapture()) - m_winCapture->ReleaseMouse(); - m_winCapture = NULL; - } - - 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_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() ); - } - - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = 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(); + case WXGRID_CURSOR_SELECT_CELL: + DoGridCellDrag(event, coords, isFirstDrag); + break; - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); - } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeCol(); + case WXGRID_CURSOR_RESIZE_ROW: + DoGridLineDrag(event, wxGridRowOperations()); + break; - // Note: we are ending the event *after* doing - // default processing in this case - // - SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event ); - } + case WXGRID_CURSOR_RESIZE_COL: + DoGridLineDrag(event, wxGridColumnOperations()); + break; - m_dragLastPos = -1; + default: + event.Skip(); } - // ------------ Right button down - // - else if ( event.RightDown() && coords != wxGridNoCellCoords ) + if ( isFirstDrag ) { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) - { - // no default action at the moment - } + m_winCapture = m_gridWin; + m_winCapture->CaptureMouse(); } +} - // ------------ Right double click - // - else if ( event.RightDClick() && coords != wxGridNoCellCoords ) +void +wxGrid::DoGridCellLeftDown(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) +{ + if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) ) { - DisableCellEditControl(); - if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK, - coords.GetRow(), - coords.GetCol(), - event ) ) - { - // no default action at the moment - } + // event handled by user code, no need to do anything here + return; } - // ------------ Moving and no button action - // - else if ( event.Moving() && !event.IsButton() ) - { - if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) - { - // out of grid cell area - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - return; - } - - int dragRow = YToEdgeOfRow( y ); - int dragCol = XToEdgeOfCol( x ); + if ( !event.CmdDown() ) + ClearSelection(); - // Dragging on the corner of a cell to resize in both - // directions is not implemented yet... - // - if ( dragRow >= 0 && dragCol >= 0 ) + if ( event.ShiftDown() ) + { + if ( m_selection ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - return; + m_selection->SelectBlock(m_currentCellCoords, coords, event); + m_selectedBlockCorner = coords; } + } + else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) + { + DisableCellEditControl(); + MakeCellVisible( coords ); - if ( dragRow >= 0 ) + if ( event.CmdDown() ) { - m_dragRowOrCol = dragRow; - - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) + if ( m_selection ) { - if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); + m_selection->ToggleCellSelection(coords, event); } - } - else if ( dragCol >= 0 ) - { - m_dragRowOrCol = dragCol; - if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) - { - if ( CanDragColSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); - } + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = coords; } - else // Neither on a row or col edge + else { - if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) - { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - } + m_waitForSlowClick = m_currentCellCoords == coords && + coords != wxGridNoCellCoords; + SetCurrentCell( coords ); } } } -void wxGrid::DoEndDragResizeRow() +void +wxGrid::DoGridCellLeftDClick(wxMouseEvent& event, + const wxGridCellCoords& coords, + const wxPoint& pos) { - if ( m_dragLastPos >= 0 ) + if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 ) { - // erase the last line and resize the row - // - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); - - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( left, m_dragLastPos, left + cw, m_dragLastPos ); - HideCellEditControl(); - SaveEditControlValue(); - - int rowTop = GetRowTop(m_dragRowOrCol); - SetRowSize( m_dragRowOrCol, - wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) ); - - if ( !GetBatchCount() ) + if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) ) { - // 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; - - // 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 ); + // 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; } - - ShowCellEditControl(); } } - -void wxGrid::DoEndDragResizeCol() +void +wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) { - if ( m_dragLastPos >= 0 ) + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - // erase the last line and resize the col - // - int cw, ch, dummy, top; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &dummy, &top ); + if (m_winCapture) + { + m_winCapture->ReleaseMouse(); + m_winCapture = NULL; + } - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - dc.SetLogicalFunction( wxINVERT ); - dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch ); - HideCellEditControl(); - SaveEditControlValue(); + if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) + { + ClearSelection(); + EnableCellEditControl(); - int colLeft = GetColLeft(m_dragRowOrCol); - SetColSize( m_dragRowOrCol, - wxMax( m_dragLastPos - colLeft, - GetColMinimalWidth(m_dragRowOrCol) ) ); + wxGridCellAttr *attr = GetCellAttr(coords); + wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol()); + editor->StartingClick(); + editor->DecRef(); + attr->DecRef(); - if ( !GetBatchCount() ) + m_waitForSlowClick = false; + } + else if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) { - // 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) + if ( m_selection ) { - int i, cell_rows, cell_cols, subtract_cols = 0; - int topRow = YToRow(top); - int bottomRow = internalYToRow(top + cw); - if (topRow >= 0) - { - for (i=topRow; iSelectBlock( m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event ); } - m_gridWin->Refresh( false, &rect ); + 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(); - ShowCellEditControl(); + // Note: we are ending the event *after* doing + // default processing in this case + // + SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event ); } + else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + DoEndDragResizeCol(); + } + + m_dragLastPos = -1; } -void wxGrid::DoEndDragMoveCol() +void +wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), + const wxGridCellCoords& coords, + const wxPoint& pos) { - //The user clicked on the column but didn't actually drag - if ( m_dragLastPos < 0 ) + if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) { - m_colLabelWin->Refresh(); //Do this to "unpress" the column + // out of grid cell area + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); return; } - int newPos; - if ( m_moveToCol == -1 ) - newPos = m_numCols - 1; - else + 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... + // + if ( dragRow >= 0 && dragCol >= 0 ) { - newPos = GetColPos( m_moveToCol ); - if ( newPos > GetColPos( m_dragRowOrCol ) ) - newPos--; + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + return; } - SetColPos( m_dragRowOrCol, newPos ); -} - -void wxGrid::SetColPos( int colID, int newPos ) -{ - if ( m_colAt.IsEmpty() ) + if ( dragRow >= 0 ) { - m_colAt.Alloc( m_numCols ); + m_dragRowOrCol = dragRow; - int i; - for ( i = 0; i < m_numCols; i++ ) + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - m_colAt.Add( i ); + if ( CanDragRowSize() && CanDragGridSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); } } - - int oldPos = GetColPos( colID ); - - //Reshuffle the m_colAt array - if ( newPos > oldPos ) + // 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 ) { - int i; - for ( i = oldPos; i < newPos; i++ ) + m_dragRowOrCol = dragCol; + + if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - m_colAt[i] = m_colAt[i+1]; + if ( CanDragColSize() && CanDragGridSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); } } - else + else // Neither on a row or col edge { - int i; - for ( i = oldPos; i > newPos; i-- ) + if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - m_colAt[i] = m_colAt[i-1]; + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); } } +} - m_colAt[newPos] = colID; +void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) +{ + const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) + // coordinates of the cell under mouse + wxGridCellCoords coords = XYToCell(pos); + + int cell_rows, cell_cols; + GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); + if ( (cell_rows < 0) || (cell_cols < 0) ) { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) - { - int colID = GetColAt( colPos ); + coords.SetRow(coords.GetRow() + cell_rows); + coords.SetCol(coords.GetCol() + cell_cols); + } - colRight += m_colWidths[colID]; - m_colRights[colID] = colRight; - } + if ( event.Dragging() ) + { + if ( event.LeftIsDown() ) + DoGridDragEvent(event, coords); + else + event.Skip(); + return; } - m_colLabelWin->Refresh(); - m_gridWin->Refresh(); -} + m_isDragging = false; + m_startDragPos = wxDefaultPosition; + // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL + // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under + // wxGTK +#if 0 + if ( event.Entering() || event.Leaving() ) + { + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + m_gridWin->SetCursor( *wxSTANDARD_CURSOR ); + } +#endif // 0 + + // deal with various button presses + if ( event.IsButton() ) + { + if ( coords != wxGridNoCellCoords ) + { + DisableCellEditControl(); + 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); + } -void wxGrid::EnableDragColMove( bool enable ) + // this one should be called even if we're not over any cell + if ( event.LeftUp() ) + { + DoGridCellLeftUp(event, coords); + } + } + else if ( event.Moving() ) + { + DoGridMouseMoveEvent(event, coords, pos); + } + else // unknown mouse event? + { + event.Skip(); + } +} + +void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) { - if ( m_canDragColMove == enable ) + if ( m_dragLastPos == -1 ) return; - m_canDragColMove = enable; + const wxGridOperations& doper = oper.Dual(); + + const wxSize size = m_gridWin->GetClientSize(); + + const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0)); + + // erase the last line we drew + wxClientDC dc(m_gridWin); + PrepareDC(dc); + dc.SetLogicalFunction(wxINVERT); - if ( !m_canDragColMove ) + const int posLineStart = oper.Select(ptOrigin); + const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size); + + oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos); + + // temporarily hide the edit control before resizing + HideCellEditControl(); + SaveEditControlValue(); + + // do resize the line + const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol); + oper.SetLineSize(this, m_dragRowOrCol, + wxMax(m_dragLastPos - lineStart, + oper.GetMinimalLineSize(this, m_dragRowOrCol))); + + m_dragLastPos = -1; + + // refresh now if we're not frozen + if ( !GetBatchCount() ) { - m_colAt.Clear(); + // we need to refresh everything beyond the resized line in the header + // window + + // get the position from which to refresh in the other direction + wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0))); + rect.SetPosition(CalcScrolledPosition(rect.GetPosition())); + + // 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; + + wxRect rectHeader(rect.GetPosition(), + oper.MakeSize + ( + oper.GetHeaderWindowSize(this), + doper.Select(size) - doper.Select(rect) + )); - //Recalculate the column rights - if ( !m_colWidths.IsEmpty() ) + oper.GetHeaderWindow(this)->Refresh(true, &rectHeader); + + + // also refresh the grid window: extend the rectangle + if ( m_table ) { - int colRight = 0; - int colPos; - for ( colPos = 0; colPos < m_numCols; colPos++ ) + oper.SelectSize(rect) = oper.Select(size); + + int subtractLines = 0; + const int lineStart = oper.PosToLine(this, posLineStart); + if ( lineStart >= 0 ) { - colRight += m_colWidths[colPos]; - m_colRights[colPos] = colRight; + // ensure that if we have a multi-cell block we redraw all of + // it by increasing the refresh area to cover it entirely if a + // part of it is affected + const int lineEnd = oper.PosToLine(this, posLineEnd, true); + for ( int line = lineStart; line < lineEnd; line++ ) + { + int cellLines = oper.Select( + GetCellSize(oper.MakeCoords(m_dragRowOrCol, line))); + if ( cellLines < subtractLines ) + subtractLines = cellLines; + } } - } - m_colLabelWin->Refresh(); - m_gridWin->Refresh(); + int startPos = + oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines); + startPos = doper.CalcScrolledPosition(this, startPos); + + doper.Select(rect) = startPos; + doper.SelectSize(rect) = doper.Select(size) - startPos; + + m_gridWin->Refresh(false, &rect); + } } -} + // show the edit control back again + ShowCellEditControl(); +} -// -// ------ interaction with data model -// -bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg ) +void wxGrid::DoEndDragResizeRow() { - switch ( msg.GetId() ) - { - case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES: - return GetModelValues(); + DoEndDragResizeLine(wxGridRowOperations()); +} - case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES: - return SetModelValues(); +void wxGrid::DoEndDragResizeCol(wxMouseEvent *event) +{ + DoEndDragResizeLine(wxGridColumnOperations()); - 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 ); + // Note: we are ending the event *after* doing + // default processing in this case + // + if ( event ) + SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, *event ); + else + SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol ); +} - default: - return false; - } +void wxGrid::DoStartMoveCol(int col) +{ + m_dragRowOrCol = col; } -// 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() +void wxGrid::DoEndMoveCol(int pos) { - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); + wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" ); - m_table->Clear(); - if (!GetBatchCount()) - m_gridWin->Refresh(); - } + if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 ) + SetColPos(m_dragRowOrCol, pos); + //else: vetoed by user + + m_dragRowOrCol = -1; } -bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) +void wxGrid::RefreshAfterColPosChange() { - if ( !m_created ) + // 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() ) { - wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") ); - return false; + int colRight = 0; + for ( int colPos = 0; colPos < m_numCols; colPos++ ) + { + int colID = GetColAt( colPos ); + + colRight += m_colWidths[colID]; + m_colRights[colID] = colRight; + } } - if ( m_table ) + // and make the changes visible + if ( m_useNativeHeader ) { - 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 + if ( m_colAt.empty() ) + GetGridColHeader()->ResetColumnsOrder(); + else + GetGridColHeader()->SetColumnsOrder(m_colAt); + } + else + { + m_colWindow->Refresh(); } + m_gridWin->Refresh(); +} - return false; +void wxGrid::SetColumnsOrder(const wxArrayInt& order) +{ + m_colAt = order; + + RefreshAfterColPosChange(); } -bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) ) +void wxGrid::SetColPos(int idx, int pos) { - if ( !m_created ) + // we're going to need m_colAt now, initialize it if needed + if ( m_colAt.empty() ) { - wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") ); - return false; + m_colAt.reserve(m_numCols); + for ( int i = 0; i < m_numCols; i++ ) + m_colAt.push_back(i); } - if ( m_table ) - { - bool done = m_table && m_table->AppendRows( numRows ); - return done; + wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos); - // the table will have sent the results of the append row - // operation to this view object as a grid table message - } + RefreshAfterColPosChange(); +} - return false; +void wxGrid::ResetColPos() +{ + m_colAt.clear(); + + RefreshAfterColPosChange(); } -bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) ) +void wxGrid::EnableDragColMove( bool enable ) { - if ( !m_created ) + if ( m_canDragColMove == enable ) + return; + + if ( m_useNativeHeader ) { - wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") ); - return false; + // update all columns to make them [not] reorderable + GetGridColHeader()->SetColumnCount(m_numCols); } - if ( m_table ) + m_canDragColMove = enable; + + // 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 +} + + +// +// ------ interaction with data model +// +bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg ) +{ + switch ( msg.GetId() ) { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); + case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES: + return GetModelValues(); + + 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 ); - 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 + default: + return false; } - - return false; } -bool wxGrid::InsertCols( int pos, int numCols, 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() { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") ); - return false; - } - if ( m_table ) { if (IsCellEditControlEnabled()) DisableCellEditControl(); - bool done = m_table->InsertCols( pos, numCols ); - return done; - // the table will have sent the results of the insert col - // operation to this view object as a grid table message + m_table->Clear(); + if (!GetBatchCount()) + m_gridWin->Refresh(); } - - return false; } -bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t), + int pos, int num, bool WXUNUSED(updateLabels) ) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") ); + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); + + 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 - } + if ( IsCellEditControlEnabled() ) + DisableCellEditControl(); - return false; + return (m_table->*funcModify)(pos, num); + + // the table will have sent the results of the insert row + // operation to this view object as a grid table message } -bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) ) +bool +wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t), + int num, bool WXUNUSED(updateLabels)) { - if ( !m_created ) - { - wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") ); - return false; - } + wxCHECK_MSG( m_created, false, "must finish creating the grid first" ); - if ( m_table ) - { - if (IsCellEditControlEnabled()) - DisableCellEditControl(); - - bool done = m_table->DeleteCols( pos, numCols ); - return done; - // the table will have sent the results of the delete col - // operation to this view object as a grid table message - } + if ( !m_table ) + return false; - return false; + return (m_table->*funcAppend)(num); } // // ----- event handlers // -// Generate a grid event based on a mouse event and -// return the result of ProcessEvent() -// -int wxGrid::SendEvent( const wxEventType type, - int row, int col, - wxMouseEvent& mouseEv ) +// Generate a grid event based on a mouse event and return: +// -1 if the event was vetoed +// +1 if the event was processed (but not vetoed) +// 0 if the event wasn't handled +int +wxGrid::SendEvent(const wxEventType type, + int row, int col, + wxMouseEvent& mouseEv) { bool claimed, vetoed; @@ -6679,10 +7040,7 @@ int wxGrid::SendEvent( const wxEventType type, rowOrCol, mouseEv.GetX() + GetRowLabelSize(), mouseEv.GetY() + GetColLabelSize(), - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); @@ -6693,13 +7051,10 @@ int wxGrid::SendEvent( const wxEventType type, wxGridRangeSelectEvent gridEvt( GetId(), type, this, - m_selectingTopLeft, - m_selectingBottomRight, + m_selectedBlockTopLeft, + m_selectedBlockBottomRight, true, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); @@ -6723,10 +7078,7 @@ int wxGrid::SendEvent( const wxEventType type, pos.x, pos.y, false, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); } @@ -6739,10 +7091,7 @@ int wxGrid::SendEvent( const wxEventType type, mouseEv.GetX() + GetRowLabelSize(), mouseEv.GetY() + GetColLabelSize(), false, - mouseEv.ControlDown(), - mouseEv.ShiftDown(), - mouseEv.AltDown(), - mouseEv.MetaDown() ); + mouseEv); claimed = GetEventHandler()->ProcessEvent(gridEvt); vetoed = !gridEvt.IsAllowed(); } @@ -6754,11 +7103,9 @@ 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) { bool claimed, vetoed; @@ -6854,7 +7201,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) if ( width_cell > 0 && height_label > 0 ) { wxRect anotherrect(x, rect_y, width_cell, height_label); - m_colLabelWin->Refresh(eraseb, &anotherrect); + m_colWindow->Refresh(eraseb, &anotherrect); } // Paint row labels part intersecting rect. @@ -6874,7 +7221,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) else { m_cornerLabelWin->Refresh(eraseb, NULL); - m_colLabelWin->Refresh(eraseb, NULL); + m_colWindow->Refresh(eraseb, NULL); m_rowLabelWin->Refresh(eraseb, NULL); m_gridWin->Refresh(eraseb, NULL); } @@ -7001,8 +7348,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) case WXK_HOME: if ( event.ControlDown() ) { - MakeCellVisible( 0, 0 ); - SetCurrentCell( 0, 0 ); + GoToCell(0, 0); } else { @@ -7013,8 +7359,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) case WXK_END: if ( event.ControlDown() ) { - MakeCellVisible( m_numRows - 1, m_numCols - 1 ); - SetCurrentCell( m_numRows - 1, m_numCols - 1 ); + GoToCell(m_numRows - 1, m_numCols - 1); } else { @@ -7076,26 +7421,21 @@ void wxGrid::OnKeyUp( wxKeyEvent& event ) // if ( event.GetKeyCode() == WXK_SHIFT ) { - if ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords ) + if ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords ) { if ( m_selection ) { m_selection->SelectBlock( - m_selectingTopLeft.GetRow(), - m_selectingTopLeft.GetCol(), - m_selectingBottomRight.GetRow(), - m_selectingBottomRight.GetCol(), - event.ControlDown(), - true, - event.AltDown(), - event.MetaDown() ); + m_selectedBlockTopLeft, + m_selectedBlockBottomRight, + event); } } - m_selectingTopLeft = wxGridNoCellCoords; - m_selectingBottomRight = wxGridNoCellCoords; - m_selectingKeyboard = wxGridNoCellCoords; + m_selectedBlockTopLeft = wxGridNoCellCoords; + m_selectedBlockBottomRight = wxGridNoCellCoords; + m_selectedBlockCorner = wxGridNoCellCoords; } } @@ -7144,12 +7484,12 @@ void wxGrid::OnEraseBackground(wxEraseEvent&) { } -void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) +bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) { - if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) ) + if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 ) { - // the event has been intercepted - do nothing - return; + // the event has been vetoed - do nothing + return false; } #if !defined(__WXMAC__) @@ -7194,35 +7534,62 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) DrawCellHighlight( dc, attr ); #endif attr->DecRef(); + + return true; } -void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol) +void +wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, + int bottomRow, int rightCol) { - wxGridCellCoords updateTopLeft, updateBottomRight; - if ( m_selection ) { - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows ) + switch ( m_selection->GetSelectionMode() ) { - leftCol = 0; - rightCol = GetNumberCols() - 1; - } - else if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns ) - { - topRow = 0; - bottomRow = GetNumberRows() - 1; + default: + wxFAIL_MSG( "unknown selection mode" ); + // fall through + + case wxGridSelectCells: + // arbitrary blocks selection allowed so just use the cell + // coordinates as is + break; + + case wxGridSelectRows: + // only full rows selection allowd, ensure that we do select + // full rows + leftCol = 0; + rightCol = GetNumberCols() - 1; + break; + + case wxGridSelectColumns: + // same as above but for columns + topRow = 0; + bottomRow = GetNumberRows() - 1; + break; + + case wxGridSelectRowsOrColumns: + // in this mode we can select only full rows or full columns so + // it doesn't make sense to select blocks at all (and we can't + // extend the block because there is no preferred direction, we + // could only extend it to cover the entire grid but this is + // not useful) + return; } } + m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol); + MakeCellVisible(m_selectedBlockCorner); + EnsureFirstLessThanSecond(topRow, bottomRow); EnsureFirstLessThanSecond(leftCol, rightCol); - updateTopLeft = wxGridCellCoords( topRow, leftCol ); - updateBottomRight = wxGridCellCoords( bottomRow, rightCol ); + wxGridCellCoords updateTopLeft = wxGridCellCoords(topRow, leftCol), + updateBottomRight = wxGridCellCoords(bottomRow, rightCol); // First the case that we selected a completely new area - if ( m_selectingTopLeft == wxGridNoCellCoords || - m_selectingBottomRight == wxGridNoCellCoords ) + if ( m_selectedBlockTopLeft == wxGridNoCellCoords || + m_selectedBlockBottomRight == wxGridNoCellCoords ) { wxRect rect; rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ), @@ -7231,8 +7598,8 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol } // Now handle changing an existing selection area. - else if ( m_selectingTopLeft != updateTopLeft || - m_selectingBottomRight != updateBottomRight ) + else if ( m_selectedBlockTopLeft != updateTopLeft || + m_selectedBlockBottomRight != updateBottomRight ) { // Compute two optimal update rectangles: // Either one rectangle is a real subset of the @@ -7246,10 +7613,10 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol int i; // Store intermediate values - wxCoord oldLeft = m_selectingTopLeft.GetCol(); - wxCoord oldTop = m_selectingTopLeft.GetRow(); - wxCoord oldRight = m_selectingBottomRight.GetCol(); - wxCoord oldBottom = m_selectingBottomRight.GetRow(); + wxCoord oldLeft = m_selectedBlockTopLeft.GetCol(); + wxCoord oldTop = m_selectedBlockTopLeft.GetRow(); + wxCoord oldRight = m_selectedBlockBottomRight.GetCol(); + wxCoord oldBottom = m_selectedBlockBottomRight.GetRow(); // Determine the outer/inner coordinates. EnsureFirstLessThanSecond(oldLeft, leftCol); @@ -7308,8 +7675,8 @@ void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol } // change selection - m_selectingTopLeft = updateTopLeft; - m_selectingBottomRight = updateBottomRight; + m_selectedBlockTopLeft = updateTopLeft; + m_selectedBlockBottomRight = updateBottomRight; } // @@ -7589,27 +7956,6 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect); } - -#if 0 - // VZ: my experiments with 3D borders... - - // how to properly set colours for arbitrary bg? - wxCoord x1 = rect.x, - y1 = rect.y, - x2 = rect.x + rect.width - 1, - y2 = rect.y + rect.height - 1; - - dc.SetPen(*wxWHITE_PEN); - dc.DrawLine(x1, y1, x2, y1); - dc.DrawLine(x1, y1, x1, y2); - - dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1); - dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2); - - dc.SetPen(*wxBLACK_PEN); - dc.DrawLine(x1, y2, x2, y2); - dc.DrawLine(x2, y1, x2, y2 + 1); -#endif } wxPen wxGrid::GetDefaultGridLinePen() @@ -7706,7 +8052,7 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) // void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) { - if ( !m_gridLinesEnabled || !m_numRows || !m_numCols ) + if ( !m_gridLinesEnabled ) return; int top, bottom, left, right; @@ -7717,9 +8063,25 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) CalcUnscrolledPosition( cw, ch, &right, &bottom ); // avoid drawing grid lines past the last row and col - // - right = wxMin( right, GetColRight(GetColAt( m_numCols - 1 )) ); - bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) ); + if ( m_gridLinesClipHorz ) + { + if ( !m_numCols ) + return; + + const int lastColRight = GetColRight(GetColAt(m_numCols - 1)); + if ( right > lastColRight ) + right = lastColRight; + } + + if ( m_gridLinesClipVert ) + { + if ( !m_numRows ) + return; + + const int lastRowBottom = GetRowBottom(m_numRows - 1); + if ( bottom > lastRowBottom ) + bottom = lastRowBottom; + } // no gridlines inside multicells, clip them out int leftCol = GetColPos( internalXToCol(left) ); @@ -7841,8 +8203,26 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); } +void wxGrid::UseNativeColHeader(bool native) +{ + if ( native == m_useNativeHeader ) + return; + + delete m_colWindow; + m_useNativeHeader = native; + + CreateColumnWindow(); + + if ( m_useNativeHeader ) + GetGridColHeader()->SetColumnCount(m_numCols); + CalcWindowSizes(); +} + void wxGrid::SetUseNativeColLabels( bool native ) { + wxASSERT_MSG( !m_useNativeHeader, + "doesn't make sense when using native header" ); + m_nativeColumnLabels = native; if (native) { @@ -7850,7 +8230,7 @@ void wxGrid::SetUseNativeColLabels( bool native ) SetColLabelSize( height ); } - m_colLabelWin->Refresh(); + GetColLabelWindow()->Refresh(); m_cornerLabelWin->Refresh(); } @@ -7902,7 +8282,18 @@ void wxGrid::DrawColLabel(wxDC& dc, int col) if ( m_nativeColumnLabels ) { - wxRendererNative::Get().DrawHeaderButton(m_colLabelWin, dc, rect, 0); + wxRendererNative::Get().DrawHeaderButton + ( + GetColLabelWindow(), + dc, + rect, + 0, + IsSortingBy(col) + ? IsSortOrderAscending() + ? wxHDR_SORT_ICON_UP + : wxHDR_SORT_ICON_DOWN + : wxHDR_SORT_ICON_NONE + ); } else { @@ -8118,7 +8509,7 @@ void wxGrid::EndBatch() { CalcDimensions(); m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); m_gridWin->Refresh(); } @@ -8152,8 +8543,6 @@ bool wxGrid::Enable(bool enable) void wxGrid::EnableEditing( bool edit ) { - // TODO: improve this ? - // if ( edit != m_editable ) { if (!edit) @@ -8171,7 +8560,7 @@ void wxGrid::EnableCellEditControl( bool enable ) { if ( enable ) { - if (SendEvent( wxEVT_GRID_EDITOR_SHOWN) <0) + if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 ) return; // this should be checked by the caller! @@ -8185,7 +8574,7 @@ void wxGrid::EnableCellEditControl( bool enable ) else { //FIXME:add veto support - SendEvent( wxEVT_GRID_EDITOR_HIDDEN ); + SendEvent(wxEVT_GRID_EDITOR_HIDDEN); HideCellEditControl(); SaveEditControlValue(); @@ -8424,9 +8813,7 @@ void wxGrid::SaveEditControlValue() if (changed) { - if ( SendEvent( wxEVT_GRID_CELL_CHANGE, - m_currentCellCoords.GetRow(), - m_currentCellCoords.GetCol() ) < 0 ) + if ( SendEvent(wxEVT_GRID_CELL_CHANGE) == -1 ) { // Event has been vetoed, set the data back. SetCellValue(row, col, oldval); @@ -8442,203 +8829,168 @@ void wxGrid::SaveEditControlValue() // coordinates for mouse events etc. // -void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) const +wxGridCellCoords wxGrid::XYToCell(int x, int y) const { int row = YToRow(y); int col = XToCol(x); - if ( row == -1 || col == -1 ) - { - coords = wxGridNoCellCoords; - } - else - { - coords.Set( row, col ); - } + return row == -1 || col == -1 ? wxGridNoCellCoords + : wxGridCellCoords(row, col); } -// Internal Helper function for computing row or column from some -// (unscrolled) coordinate value, using either -// m_defaultRowHeight/m_defaultColWidth or binary search on array -// of m_rowBottoms/m_ColRights to speed up the search! - -static int CoordToRowOrCol(int coord, int defaultDist, int minDist, - const wxArrayInt& BorderArray, int nMax, - bool clipToMinMax) +// compute row or column from some (unscrolled) coordinate value, using either +// m_defaultRowHeight/m_defaultColWidth or binary search on array of +// m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used +// for large grids) +int wxGrid::PosToLinePos(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { - if (coord < 0) - return clipToMinMax && (nMax > 0) ? 0 : -1; + const int numLines = oper.GetNumberOfLines(this); + + if ( coord < 0 ) + return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND; - if (!defaultDist) - defaultDist = 1; + const int defaultLineSize = oper.GetDefaultLineSize(this); + wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); - size_t i_max = coord / defaultDist, - i_min = 0; + int maxPos = coord / defaultLineSize, + minPos = 0; - if (BorderArray.IsEmpty()) + // check for the simplest case: if we have no explicit line sizes + // configured, then we already know the line this position falls in + const wxArrayInt& lineEnds = oper.GetLineEnds(this); + if ( lineEnds.empty() ) { - if ((int) i_max < nMax) - return i_max; - return clipToMinMax ? nMax - 1 : -1; + if ( maxPos < numLines ) + return maxPos; + + return clipToMinMax ? numLines - 1 : -1; } - if ( i_max >= BorderArray.GetCount()) + + // adjust maxPos before starting the binary search + if ( maxPos >= numLines ) { - i_max = BorderArray.GetCount() - 1; + maxPos = numLines - 1; } else { - if ( coord >= BorderArray[i_max]) + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)]) { - i_min = i_max; - if (minDist) - i_max = coord / minDist; + minPos = maxPos; + const int minDist = oper.GetMinimalAcceptableLineSize(this); + if ( minDist ) + maxPos = coord / minDist; else - i_max = BorderArray.GetCount() - 1; + maxPos = numLines - 1; } - if ( i_max >= BorderArray.GetCount()) - i_max = BorderArray.GetCount() - 1; + if ( maxPos >= numLines ) + maxPos = numLines - 1; } - if ( coord >= BorderArray[i_max]) - return clipToMinMax ? (int)i_max : -1; - if ( coord < BorderArray[0] ) + // check if the position is beyond the last column + const int lineAtMaxPos = oper.GetLineAt(this, maxPos); + if ( coord >= lineEnds[lineAtMaxPos] ) + return clipToMinMax ? maxPos : -1; + + // or before the first one + const int lineAt0 = oper.GetLineAt(this, 0); + if ( coord < lineEnds[lineAt0] ) return 0; - while ( i_max - i_min > 0 ) + + // finally do perform the binary search + while ( minPos < maxPos ) { - wxCHECK_MSG(BorderArray[i_min] <= coord && coord < BorderArray[i_max], - 0, _T("wxGrid: internal error in CoordToRowOrCol")); - if (coord >= BorderArray[ i_max - 1]) - return i_max; + wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord && + coord < lineEnds[oper.GetLineAt(this, maxPos)], + -1, + "wxGrid: internal error in PosToLinePos()" ); + + if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] ) + return maxPos; else - i_max--; - int median = i_min + (i_max - i_min + 1) / 2; - if (coord < BorderArray[median]) - i_max = median; + maxPos--; + + const int median = minPos + (maxPos - minPos + 1) / 2; + if ( coord < lineEnds[oper.GetLineAt(this, median)] ) + maxPos = median; else - i_min = median; + minPos = median; } - return i_max; + return maxPos; } -int wxGrid::YToRow( int y ) const +int +wxGrid::PosToLine(int coord, + bool clipToMinMax, + const wxGridOperations& oper) const { - return CoordToRowOrCol(y, m_defaultRowHeight, - m_minAcceptableRowHeight, m_rowBottoms, m_numRows, false); + int pos = PosToLinePos(coord, clipToMinMax, oper); + + return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos); } -int wxGrid::XToCol( int x, bool clipToMinMax ) const +int wxGrid::YToRow(int y, bool clipToMinMax) const { - if (x < 0) - return clipToMinMax && (m_numCols > 0) ? GetColAt( 0 ) : -1; - - wxASSERT_MSG(m_defaultColWidth > 0, wxT("Default column width can not be zero")); - - int maxPos = x / m_defaultColWidth; - int minPos = 0; - - if (m_colRights.IsEmpty()) - { - if(maxPos < m_numCols) - return GetColAt( maxPos ); - return clipToMinMax ? GetColAt( m_numCols - 1 ) : -1; - } - - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; - else - { - if ( x >= m_colRights[GetColAt( maxPos )]) - { - minPos = maxPos; - if (m_minAcceptableColWidth) - maxPos = x / m_minAcceptableColWidth; - else - maxPos = m_numCols - 1; - } - if ( maxPos >= m_numCols) - maxPos = m_numCols - 1; - } - - //X is beyond the last column - if ( x >= m_colRights[GetColAt( maxPos )]) - return clipToMinMax ? GetColAt( maxPos ) : -1; - - //X is before the first column - if ( x < m_colRights[GetColAt( 0 )] ) - return GetColAt( 0 ); + return PosToLine(y, clipToMinMax, wxGridRowOperations()); +} - //Perform a binary search - while ( maxPos - minPos > 0 ) - { - wxCHECK_MSG(m_colRights[GetColAt( minPos )] <= x && x < m_colRights[GetColAt( maxPos )], - 0, _T("wxGrid: internal error in XToCol")); +int wxGrid::XToCol(int x, bool clipToMinMax) const +{ + return PosToLine(x, clipToMinMax, wxGridColumnOperations()); +} - if (x >= m_colRights[GetColAt( maxPos - 1 )]) - return GetColAt( maxPos ); - else - maxPos--; - int median = minPos + (maxPos - minPos + 1) / 2; - if (x < m_colRights[GetColAt( median )]) - maxPos = median; - else - minPos = median; - } - return GetColAt( maxPos ); +int wxGrid::XToPos(int x) const +{ + return PosToLinePos(x, true /* clip */, wxGridColumnOperations()); } -// return the row number that that the y coord is near -// the edge of, or -1 if not near an edge. +// return the row number that that the y coord is near the edge of, or -1 if +// not near an edge. +// // coords can only possibly be near an edge if // (a) the row/column is large enough to still allow for an "inner" area -// that is _not_ nead the edge (i.e., if the height/width is smaller +// that is _not_ near the edge (i.e., if the height/width is smaller // than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be // near the edge). // and // (b) resizing rows/columns (the thing for which edge detection is // relevant at all) is enabled. // -int wxGrid::YToEdgeOfRow( int y ) const +int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { - int i; - i = internalYToRow(y); + if ( !oper.CanResizeLines(this) ) + return -1; - if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE && CanDragRowSize() ) + const int line = oper.PosToLine(this, pos, true); + + if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE ) { - // We know that we are in row i, test whether we are - // close enough to lower or upper border, respectively. - if ( abs(GetRowBottom(i) - y) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && y - GetRowTop(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; + // We know that we are in this line, test whether we are close enough + // to start or end border, respectively. + if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE ) + return line; + else if ( line > 0 && + pos - oper.GetLineStartPos(this, + line) < WXGRID_LABEL_EDGE_ZONE ) + return line - 1; } return -1; } -// return the col number that that the x coord is near the edge of, or -// -1 if not near an edge -// See comment at YToEdgeOfRow for conditions on edge detection. -// -int wxGrid::XToEdgeOfCol( int x ) const +int wxGrid::YToEdgeOfRow(int y) const { - int i; - i = internalXToCol(x); - - if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE && CanDragColSize() ) - { - // We know that we are in column i; test whether we are - // close enough to right or left border, respectively. - if ( abs(GetColRight(i) - x) < WXGRID_LABEL_EDGE_ZONE ) - return i; - else if ( i > 0 && x - GetColLeft(i) < WXGRID_LABEL_EDGE_ZONE ) - return i - 1; - } + return PosToEdgeOfLine(y, wxGridRowOperations()); +} - return -1; +int wxGrid::XToEdgeOfCol(int x) const +{ + return PosToEdgeOfLine(x, wxGridColumnOperations()); } wxRect wxGrid::CellToRect( int row, int col ) const @@ -8791,463 +9143,197 @@ void wxGrid::MakeCellVisible( int row, int col ) // ------ Grid cursor movement functions // -bool wxGrid::MoveCursorUp( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() >= 0 ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() > 0 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow() - 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} - -bool wxGrid::MoveCursorDown( bool expandSelection ) +bool +wxGrid::DoMoveCursor(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() < m_numRows ) - { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetRow() < m_numRows - 1 ) - { - m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( m_currentCellCoords.GetRow() < m_numRows - 1 ) - { - int row = m_currentCellCoords.GetRow() + 1; - int col = m_currentCellCoords.GetCol(); - ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else - return false; - - return true; - } - - return false; -} + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; -bool wxGrid::MoveCursorLeft( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() >= 0 ) + if ( expandSelection ) { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() > 0 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() - 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) - 1 ); - ClearSelection(); + wxGridCellCoords coords = m_selectedBlockCorner; + if ( coords == wxGridNoCellCoords ) + coords = m_currentCellCoords; - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else + if ( diroper.IsAtBoundary(coords) ) return false; - return true; - } - - return false; -} + diroper.Advance(coords); -bool wxGrid::MoveCursorRight( bool expandSelection ) -{ - if ( m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols ) + UpdateBlockBeingSelected(m_currentCellCoords, coords); + } + else // don't expand selection { - if ( expandSelection ) - { - if ( m_selectingKeyboard == wxGridNoCellCoords ) - m_selectingKeyboard = m_currentCellCoords; - if ( m_selectingKeyboard.GetCol() < m_numCols - 1 ) - { - m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() + 1 ); - MakeCellVisible( m_selectingKeyboard.GetRow(), - m_selectingKeyboard.GetCol() ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - } - else if ( GetColPos( m_currentCellCoords.GetCol() ) < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) + 1 ); - ClearSelection(); + ClearSelection(); - MakeCellVisible( row, col ); - SetCurrentCell( row, col ); - } - else + if ( diroper.IsAtBoundary(m_currentCellCoords) ) return false; - return true; + wxGridCellCoords coords = m_currentCellCoords; + diroper.Advance(coords); + + GoToCell(coords); } - return false; + return true; } -bool wxGrid::MovePageUp() +bool wxGrid::MoveCursorUp(bool expandSelection) { - if ( m_currentCellCoords == wxGridNoCellCoords ) - return false; - - int row = m_currentCellCoords.GetRow(); - if ( row > 0 ) - { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - - int y = GetRowTop(row); - int newRow = internalYToRow( y - ch + 1 ); - - if ( newRow == row ) - { - // row > 0, so newRow can never be less than 0 here. - newRow = row - 1; - } - - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); - - return true; - } - - return false; + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations())); } -bool wxGrid::MovePageDown() +bool wxGrid::MoveCursorDown(bool expandSelection) { - if ( m_currentCellCoords == wxGridNoCellCoords ) - return false; - - int row = m_currentCellCoords.GetRow(); - if ( (row + 1) < m_numRows ) - { - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - - int y = GetRowTop(row); - int newRow = internalYToRow( y + ch ); - if ( newRow == row ) - { - // row < m_numRows, so newRow can't overflow here. - newRow = row + 1; - } - - MakeCellVisible( newRow, m_currentCellCoords.GetCol() ); - SetCurrentCell( newRow, m_currentCellCoords.GetCol() ); - - return true; - } + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridRowOperations())); +} - return false; +bool wxGrid::MoveCursorLeft(bool expandSelection) +{ + return DoMoveCursor(expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations())); } -bool wxGrid::MoveCursorUpBlock( bool expandSelection ) +bool wxGrid::MoveCursorRight(bool expandSelection) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetRow() > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); + return DoMoveCursor(expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations())); +} - 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; - } - } - } +bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper) +{ + if ( m_currentCellCoords == wxGridNoCellCoords ) + return false; - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } + if ( diroper.IsAtBoundary(m_currentCellCoords) ) + return false; - return true; + const int oldRow = m_currentCellCoords.GetRow(); + int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y); + if ( newRow == oldRow ) + { + wxGridCellCoords coords(m_currentCellCoords); + diroper.Advance(coords); + newRow = coords.GetRow(); } - return false; + GoToCell(newRow, m_currentCellCoords.GetCol()); + + return true; } -bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +bool wxGrid::MovePageUp() { - 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 DoMoveCursorByPage( + 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::MovePageDown() +{ + return DoMoveCursorByPage( + wxGridForwardOperations(this, wxGridRowOperations())); +} - return true; +// 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) +{ + while ( !diroper.IsAtBoundary(coords) ) + { + diroper.Advance(coords); + if ( !m_table->IsEmpty(coords) ) + break; } - - return false; } -bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +bool +wxGrid::DoMoveCursorByBlock(bool expandSelection, + const wxGridDirectionOperations& diroper) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() > 0 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); + if ( !m_table || m_currentCellCoords == wxGridNoCellCoords ) + return false; - 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) ) + 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 left of a block: find the next block - // - col--; - while ( col > 0 ) - { - col--; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } + // we started at the end of a block, find the next one + AdvanceToNextNonEmpty(coords, diroper); } - else + else // we're in a middle of a block { - // starting within a block: find the left of the block - // - while ( col > 0 ) + // go to the end of it, i.e. find the last cell before the next + // empty one + while ( !diroper.IsAtBoundary(coords) ) { - col--; - if ( m_table->IsEmptyCell(row, col) ) - { - col++; + wxGridCellCoords coordsNext(coords); + diroper.Advance(coordsNext); + if ( m_table->IsEmpty(coordsNext) ) break; - } - } - } - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); + coords = coordsNext; + } } + } - return true; + if ( expandSelection ) + { + UpdateBlockBeingSelected(m_currentCellCoords, coords); + } + else + { + ClearSelection(); + GoToCell(coords); } - return false; + return true; } -bool wxGrid::MoveCursorRightBlock( bool expandSelection ) +bool wxGrid::MoveCursorUpBlock(bool expandSelection) { - if ( m_table && - m_currentCellCoords != wxGridNoCellCoords && - m_currentCellCoords.GetCol() < m_numCols - 1 ) - { - int row = m_currentCellCoords.GetRow(); - int col = m_currentCellCoords.GetCol(); - - if ( m_table->IsEmptyCell(row, col) ) - { - // starting in an empty cell: find the next block of - // non-empty cells - // - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else if ( m_table->IsEmptyCell(row, col + 1) ) - { - // starting at the right of a block: find the next block - // - col++; - while ( col < m_numCols - 1 ) - { - col++; - if ( !(m_table->IsEmptyCell(row, col)) ) - break; - } - } - else - { - // starting within a block: find the right of the block - // - while ( col < m_numCols - 1 ) - { - col++; - if ( m_table->IsEmptyCell(row, col) ) - { - col--; - break; - } - } - } + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridRowOperations()) + ); +} - MakeCellVisible( row, col ); - if ( expandSelection ) - { - m_selectingKeyboard = wxGridCellCoords( row, col ); - HighlightBlock( m_currentCellCoords, m_selectingKeyboard ); - } - else - { - ClearSelection(); - SetCurrentCell( row, col ); - } +bool wxGrid::MoveCursorDownBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridRowOperations()) + ); +} - return true; - } +bool wxGrid::MoveCursorLeftBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridBackwardOperations(this, wxGridColumnOperations()) + ); +} - return false; +bool wxGrid::MoveCursorRightBlock( bool expandSelection ) +{ + return DoMoveCursorByBlock( + expandSelection, + wxGridForwardOperations(this, wxGridColumnOperations()) + ); } // @@ -9345,12 +9431,12 @@ void wxGrid::SetColLabelSize( int height ) { if ( height == 0 ) { - m_colLabelWin->Show( false ); + m_colWindow->Show( false ); m_cornerLabelWin->Show( false ); } else if ( m_colLabelHeight == 0 ) { - m_colLabelWin->Show( true ); + m_colWindow->Show( true ); if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( true ); } @@ -9367,13 +9453,13 @@ void wxGrid::SetLabelBackgroundColour( const wxColour& colour ) { m_labelBackgroundColour = colour; m_rowLabelWin->SetBackgroundColour( colour ); - m_colLabelWin->SetBackgroundColour( colour ); + m_colWindow->SetBackgroundColour( colour ); m_cornerLabelWin->SetBackgroundColour( colour ); if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); m_cornerLabelWin->Refresh(); } } @@ -9387,7 +9473,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } } @@ -9398,7 +9484,7 @@ void wxGrid::SetLabelFont( const wxFont& font ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9464,7 +9550,7 @@ void wxGrid::SetColLabelAlignment( int horiz, int vert ) if ( !GetBatchCount() ) { - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } } @@ -9481,7 +9567,7 @@ void wxGrid::SetColLabelTextOrientation( int textOrientation ) m_colLabelTextOrientation = textOrientation; if ( !GetBatchCount() ) - m_colLabelWin->Refresh(); + m_colWindow->Refresh(); } void wxGrid::SetRowLabelValue( int row, const wxString& s ) @@ -9510,13 +9596,20 @@ void wxGrid::SetColLabelValue( int col, const wxString& s ) m_table->SetColLabelValue( col, s ); if ( !GetBatchCount() ) { - wxRect rect = CellToRect( 0, col ); - if ( rect.width > 0 ) + if ( m_useNativeHeader ) { - CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y); - rect.y = 0; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); + GetGridColHeader()->UpdateColumn(col); + } + else + { + wxRect rect = CellToRect( 0, col ); + if ( rect.width > 0 ) + { + CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y); + rect.y = 0; + rect.height = m_colLabelHeight; + GetColLabelWindow()->Refresh( true, &rect ); + } } } } @@ -9528,9 +9621,8 @@ void wxGrid::SetGridLineColour( const wxColour& colour ) { m_gridLineColour = colour; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9585,25 +9677,42 @@ void wxGrid::SetCellHighlightROPenWidth(int width) } } +void wxGrid::RedrawGridLines() +{ + // the lines will be redrawn when the window is thawn + if ( GetBatchCount() ) + return; + + if ( GridLinesEnabled() ) + { + wxClientDC dc( m_gridWin ); + PrepareDC( dc ); + DrawAllGridLines( dc, wxRegion() ); + } + else // remove the grid lines + { + m_gridWin->Refresh(); + } +} + void wxGrid::EnableGridLines( bool enable ) { if ( enable != m_gridLinesEnabled ) { m_gridLinesEnabled = enable; - if ( !GetBatchCount() ) - { - if ( enable ) - { - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); - } - else - { - m_gridWin->Refresh(); - } - } + RedrawGridLines(); + } +} + +void wxGrid::DoClipGridLines(bool& var, bool clip) +{ + if ( clip != var ) + { + var = clip; + + if ( GridLinesEnabled() ) + RedrawGridLines(); } } @@ -9883,7 +9992,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const if ( !LookupAttr(row, col, &attr) ) { attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any) - : (wxGridCellAttr *)NULL; + : NULL; CacheAttr(row, col, attr); } } @@ -9903,7 +10012,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const { - wxGridCellAttr *attr = (wxGridCellAttr *)NULL; + wxGridCellAttr *attr = NULL; bool canHave = ((wxGrid*)this)->CanHaveAttributes(); wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed")); @@ -10301,7 +10410,7 @@ void wxGrid::SetColSize( int col, int width ) { long w, h; wxArrayString lines; - wxClientDC dc(m_colLabelWin); + wxClientDC dc(m_colWindow); dc.SetFont(GetLabelFont()); StringToLines(GetColLabelValue(col), lines); if ( GetColLabelTextOrientation() == wxHORIZONTAL ) @@ -10313,15 +10422,12 @@ void wxGrid::SetColSize( int col, int width ) width = wxMax(width, GetColMinimalAcceptableWidth()); } - // should we check that it's bigger than GetColMinimalWidth(col) here? - // (VZ) - // No, because it is reasonable to assume the library user know's - // what he is doing. However we should test against the weaker - // constraint of minimalAcceptableWidth, as this breaks rendering - // - // This test then fixes sf.net bug #645734 - - if ( width < GetColMinimalAcceptableWidth() ) + // we intentionally don't test whether the width is less than + // GetColMinimalWidth() here but we do compare it with + // GetColMinimalAcceptableWidth() as otherwise things currently break (see + // #651) -- and we also always allow the width of 0 as it has the special + // sense of hiding the column + if ( width > 0 && width < GetColMinimalAcceptableWidth() ) return; if ( m_colWidths.IsEmpty() ) @@ -10330,9 +10436,11 @@ void wxGrid::SetColSize( int col, int width ) InitColWidths(); } - int w = wxMax( 0, width ); - int diff = w - m_colWidths[col]; - m_colWidths[col] = w; + const int diff = width - m_colWidths[col]; + m_colWidths[col] = width; + if ( m_useNativeHeader ) + GetGridColHeader()->UpdateColumn(col); + //else: will be refreshed when the header is redrawn for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) { @@ -10340,7 +10448,10 @@ void wxGrid::SetColSize( int col, int width ) } if ( !GetBatchCount() ) + { CalcDimensions(); + Refresh(); + } } void wxGrid::SetColMinimalWidth( int col, int width ) @@ -10493,14 +10604,21 @@ wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) SetColSize( col, extentMax ); if ( !GetBatchCount() ) { - int cw, ch, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - wxRect rect ( CellToRect( 0, col ) ); - rect.y = 0; - CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); - rect.width = cw - rect.x; - rect.height = m_colLabelHeight; - m_colLabelWin->Refresh( true, &rect ); + if ( m_useNativeHeader ) + { + GetGridColHeader()->UpdateColumn(col); + } + else + { + int cw, ch, dummy; + m_gridWin->GetClientSize( &cw, &ch ); + wxRect rect ( CellToRect( 0, col ) ); + rect.y = 0; + CalcScrolledPosition(rect.x, 0, &rect.x, &dummy); + rect.width = cw - rect.x; + rect.height = m_colLabelHeight; + GetColLabelWindow()->Refresh( true, &rect ); + } } } else @@ -10739,31 +10857,36 @@ void wxGrid::SetCellValue( int row, int col, const wxString& s ) void wxGrid::SelectRow( int row, bool addToSelected ) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectRow( row, false, addToSelected ); + m_selection->SelectRow(row); } void wxGrid::SelectCol( int col, bool addToSelected ) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectCol( col, false, addToSelected ); + m_selection->SelectCol(col); } -void wxGrid::SelectBlock( int topRow, int leftCol, int bottomRow, int rightCol, - bool addToSelected ) +void wxGrid::SelectBlock(int topRow, int leftCol, int bottomRow, int rightCol, + bool addToSelected) { - if ( IsSelection() && !addToSelected ) + if ( !m_selection ) + return; + + if ( !addToSelected ) ClearSelection(); - if ( m_selection ) - m_selection->SelectBlock( topRow, leftCol, bottomRow, rightCol, - false, addToSelected ); + m_selection->SelectBlock(topRow, leftCol, bottomRow, rightCol); } void wxGrid::SelectAll() @@ -10779,46 +10902,40 @@ void wxGrid::SelectAll() // cell, row and col deselection // ---------------------------------------------------------------------------- -void wxGrid::DeselectRow( int row ) +void wxGrid::DeselectLine(int line, const wxGridOperations& oper) { if ( !m_selection ) return; - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows ) + const wxGridSelectionModes mode = m_selection->GetSelectionMode(); + if ( mode == oper.GetSelectionMode() ) { - if ( m_selection->IsInSelection(row, 0 ) ) - m_selection->ToggleCellSelection(row, 0); + const wxGridCellCoords c(oper.MakeCoords(line, 0)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } - else + else if ( mode != oper.Dual().GetSelectionMode() ) { - int nCols = GetNumberCols(); - for ( int i = 0; i < nCols; i++ ) + const int nOther = oper.Dual().GetNumberOfLines(this); + for ( int i = 0; i < nOther; i++ ) { - if ( m_selection->IsInSelection(row, i ) ) - m_selection->ToggleCellSelection(row, i); + const wxGridCellCoords c(oper.MakeCoords(line, i)); + if ( m_selection->IsInSelection(c) ) + m_selection->ToggleCellSelection(c); } } + //else: can only select orthogonal lines so no lines in this direction + // could have been selected anyhow } -void wxGrid::DeselectCol( int col ) +void wxGrid::DeselectRow(int row) { - if ( !m_selection ) - return; + DeselectLine(row, wxGridRowOperations()); +} - if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns ) - { - if ( m_selection->IsInSelection(0, col ) ) - m_selection->ToggleCellSelection(0, col); - } - else - { - int nRows = GetNumberRows(); - for ( int i = 0; i < nRows; i++ ) - { - if ( m_selection->IsInSelection(i, col ) ) - m_selection->ToggleCellSelection(i, col); - } - } +void wxGrid::DeselectCol(int col) +{ + DeselectLine(col, wxGridColumnOperations()); } void wxGrid::DeselectCell( int row, int col ) @@ -10830,17 +10947,17 @@ void wxGrid::DeselectCell( int row, int col ) bool wxGrid::IsSelection() const { return ( m_selection && (m_selection->IsSelection() || - ( m_selectingTopLeft != wxGridNoCellCoords && - m_selectingBottomRight != wxGridNoCellCoords) ) ); + ( m_selectedBlockTopLeft != wxGridNoCellCoords && + m_selectedBlockBottomRight != wxGridNoCellCoords) ) ); } bool wxGrid::IsInSelection( int row, int col ) const { return ( m_selection && (m_selection->IsInSelection( row, col ) || - ( row >= m_selectingTopLeft.GetRow() && - col >= m_selectingTopLeft.GetCol() && - row <= m_selectingBottomRight.GetRow() && - col <= m_selectingBottomRight.GetCol() )) ); + ( row >= m_selectedBlockTopLeft.GetRow() && + col >= m_selectedBlockTopLeft.GetCol() && + row <= m_selectedBlockBottomRight.GetRow() && + col <= m_selectedBlockBottomRight.GetCol() )) ); } wxGridCellCoordsArray wxGrid::GetSelectedCells() const @@ -10900,13 +11017,18 @@ 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; + wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft, + m_selectedBlockBottomRight); + wxRect r2 = BlockToDeviceRect(m_currentCellCoords, + m_selectedBlockCorner); + + m_selectedBlockTopLeft = + m_selectedBlockBottomRight = + m_selectedBlockCorner = wxGridNoCellCoords; + Refresh( false, &r1 ); Refresh( false, &r2 ); + if ( m_selection ) m_selection->ClearSelection(); } @@ -11060,36 +11182,23 @@ IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent ) wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj, int row, int col, int x, int y, bool sel, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) { - m_row = row; - m_col = col; - m_x = x; - m_y = y; - m_selecting = sel; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + Init(row, col, x, y, sel); SetEventObject(obj); } - IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent ) wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj, int rowOrCol, int x, int y, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) { - m_rowOrCol = rowOrCol; - m_x = x; - m_y = y; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + Init(rowOrCol, x, y); SetEventObject(obj); } @@ -11102,15 +11211,10 @@ wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObjec const wxGridCellCoords& bottomRight, bool sel, bool control, bool shift, bool alt, bool meta ) - : wxNotifyEvent( type, id ) -{ - m_topLeft = topLeft; - m_bottomRight = bottomRight; - m_selecting = sel; - m_control = control; - m_shift = shift; - m_alt = alt; - m_meta = meta; + : wxNotifyEvent( type, id ), + wxKeyboardState(control, shift, alt, meta) +{ + Init(topLeft, bottomRight, sel); SetEventObject(obj); }