From 04f7f1fd32a9f061bb5ad41d709a9e9ea7fe76c8 Mon Sep 17 00:00:00 2001 From: lucian-rotariu Date: Wed, 3 Jul 2019 23:07:30 +0300 Subject: [PATCH] Add support for freezing columns and/or rows of wxGrid Add wxGrid::FreezeTo() method which allows to freeze the given number of columns and/or rows at the beginning of the grid, i.e. keep them pinned in place while the rest of the grid is scrolled. The main wxGridWindow (m_gridWin) now corresponds to the non-frozen part of the grid, with up to 3 new similar windows for the frozen rows/columns and the frozen corner cells (which only exist if both rows and columns are frozen) being additionally used. Doing this involved adding "wxGridWindow*" parameter to many functions that previously only worked with m_gridWin itself and addressing additional complications, such as mouse events that can now cross different windows. See https://github.com/wxWidgets/wxWidgets/pull/952 for the original version of the changes. --- include/wx/generic/grid.h | 157 +++- include/wx/generic/private/grid.h | 93 +- interface/wx/grid.h | 240 ++++- samples/grid/griddemo.cpp | 36 +- samples/grid/griddemo.h | 4 + src/generic/grid.cpp | 1356 ++++++++++++++++++++++------- 6 files changed, 1477 insertions(+), 409 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 38905b2623..01a02393b3 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1021,14 +1021,19 @@ public: int GetNumberRows() const { return m_numRows; } int GetNumberCols() const { return m_numCols; } + int GetNumberFrozenRows() const { return m_numFrozenRows; } + int GetNumberFrozenCols() const { return m_numFrozenCols; } // ------ display update functions // - wxArrayInt CalcRowLabelsExposed( const wxRegion& reg ) const; - - wxArrayInt CalcColLabelsExposed( const wxRegion& reg ) const; - wxGridCellCoordsArray CalcCellsExposed( const wxRegion& reg ) const; + wxArrayInt CalcRowLabelsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; + wxArrayInt CalcColLabelsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; + wxGridCellCoordsArray CalcCellsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; + void PrepareDCFor(wxDC &dc, wxGridWindow *gridWindow); void ClearGrid(); bool InsertRows(int pos = 0, int numRows = 1, bool updateLabels = true) @@ -1062,12 +1067,24 @@ public: pos, numCols, updateLabels); } + bool FreezeTo(int row, int col); + bool FreezeTo(const wxGridCellCoords& coords) + { + return FreezeTo(coords.GetRow(), coords.GetCol()); + } + + bool IsFrozen() const; + void DrawGridCellArea( wxDC& dc , const wxGridCellCoordsArray& cells ); - void DrawGridSpace( wxDC& dc ); + void DrawGridSpace( wxDC& dc, wxGridWindow *gridWindow ); void DrawCellBorder( wxDC& dc, const wxGridCellCoords& ); - void DrawAllGridLines( wxDC& dc, const wxRegion & reg ); + void DrawAllGridLines(); + void DrawAllGridWindowLines( wxDC& dc, const wxRegion & reg , wxGridWindow *gridWindow); void DrawCell( wxDC& dc, const wxGridCellCoords& ); void DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells); + void DrawFrozenBorder( wxDC& dc, wxGridWindow *gridWindow ); + void DrawLabelFrozenBorder( wxDC& dc, wxWindow *window, bool isRow ); + void ScrollWindow( int dx, int dy, const wxRect *rect ) wxOVERRIDE; void UpdateGridWindows() const; @@ -1165,11 +1182,15 @@ public: // grid cells and labels so you will need to convert from device // coordinates for mouse events etc. // - wxGridCellCoords XYToCell(int x, int y) const; - void XYToCell(int x, int y, wxGridCellCoords& coords) const - { coords = XYToCell(x, y); } - wxGridCellCoords XYToCell(const wxPoint& pos) const - { return XYToCell(pos.x, pos.y); } + wxGridCellCoords XYToCell(int x, int y, wxGridWindow *gridWindow = NULL) const; + void XYToCell(int x, int y, + wxGridCellCoords& coords, + wxGridWindow *gridWindow = NULL) const + { coords = XYToCell(x, y, gridWindow); } + + wxGridCellCoords XYToCell(const wxPoint& pos, + wxGridWindow *gridWindow = NULL) const + { return XYToCell(pos.x, pos.y, gridWindow); } // these functions return the index of the row/columns corresponding to the // given logical position in pixels @@ -1177,8 +1198,8 @@ public: // if clipToMinMax is false (default, wxNOT_FOUND is returned if the // position is outside any row/column, otherwise the first/last element is // returned in this case - int YToRow( int y, bool clipToMinMax = false ) const; - int XToCol( int x, bool clipToMinMax = false ) const; + int YToRow( int y, bool clipToMinMax = false, wxGridWindow *gridWindow = NULL ) const; + int XToCol( int x, bool clipToMinMax = false, wxGridWindow *gridWindow = NULL ) const; int YToEdgeOfRow( int y ) const; int XToEdgeOfCol( int x ) const; @@ -1187,12 +1208,32 @@ public: wxRect CellToRect( const wxGridCellCoords& coords ) const { return CellToRect( coords.GetRow(), coords.GetCol() ); } + wxGridWindow* CellToGridWindow( int row, int col ) const; + wxGridWindow* CellToGridWindow( const wxGridCellCoords& coords ) const + { return CellToGridWindow( coords.GetRow(), coords.GetCol() ); } + const wxGridCellCoords& GetGridCursorCoords() const { return m_currentCellCoords; } int GetGridCursorRow() const { return m_currentCellCoords.GetRow(); } int GetGridCursorCol() const { return m_currentCellCoords.GetCol(); } + void GetGridWindowOffset(const wxGridWindow *gridWindow, int &x, int &y) const; + wxPoint GetGridWindowOffset(const wxGridWindow *gridWindow) const; + + wxGridWindow* DevicePosToGridWindow(wxPoint pos) const; + wxGridWindow* DevicePosToGridWindow(int x, int y) const; + + void CalcGridWindowUnscrolledPosition(int x, int y, int *xx, int *yy, + const wxGridWindow *gridWindow) const; + wxPoint CalcGridWindowUnscrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const; + + void CalcGridWindowScrolledPosition(int x, int y, int *xx, int *yy, + const wxGridWindow *gridWindow) const; + wxPoint CalcGridWindowScrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const; + // check to see if a cell is either wholly visible (the default arg) or // at least partially visible in the grid window // @@ -1256,9 +1297,11 @@ public: wxColour GetCellHighlightColour() const { return m_cellHighlightColour; } int GetCellHighlightPenWidth() const { return m_cellHighlightPenWidth; } int GetCellHighlightROPenWidth() const { return m_cellHighlightROPenWidth; } + wxColor GetGridFrozenBorderColour() const { return m_gridFrozenBorderColour; } + int GetGridFrozenBorderPenWidth() const { return m_gridFrozenBorderPenWidth; } // this one will use wxHeaderCtrl for the column labels - void UseNativeColHeader(bool native = true); + bool UseNativeColHeader(bool native = true); // this one will still draw them manually but using the native renderer // instead of using the same appearance as for the row labels @@ -1280,9 +1323,10 @@ public: void SetColLabelValue( int col, const wxString& ); void SetCornerLabelValue( const wxString& ); void SetCellHighlightColour( const wxColour& ); - void SetCellHighlightPenWidth(int width); - void SetCellHighlightROPenWidth(int width); - + void SetCellHighlightPenWidth( int width ); + void SetCellHighlightROPenWidth( int width ); + void SetGridFrozenBorderColour( const wxColour& ); + void SetGridFrozenBorderPenWidth( int width ); // interactive grid mouse operations control // ----------------------------------------- @@ -1309,7 +1353,7 @@ public: { return m_canDragColSize && DoCanResizeLine(col, m_setFixedCols); } // interactive column reordering (disabled by default) - void EnableDragColMove( bool enable = true ); + bool EnableDragColMove( bool enable = true ); void DisableDragColMove() { EnableDragColMove( false ); } bool CanDragColMove() const { return m_canDragColMove; } @@ -1635,7 +1679,8 @@ public: // to the client size of the grid window. // wxRect BlockToDeviceRect( const wxGridCellCoords & topLeft, - const wxGridCellCoords & bottomRight ) const; + const wxGridCellCoords & bottomRight, + const wxGridWindow *gridWindow = NULL) const; // Access or update the selection fore/back colours wxColour GetSelectionBackground() const @@ -1671,6 +1716,9 @@ public: // Accessors for component windows wxWindow* GetGridWindow() const { return (wxWindow*)m_gridWin; } + wxWindow* GetFrozenCornerGridWindow()const { return (wxWindow*)m_frozenCornerGridWin; } + wxWindow* GetFrozenRowGridWindow() const { return (wxWindow*)m_frozenRowGridWin; } + wxWindow* GetFrozenColGridWindow() const { return (wxWindow*)m_frozenColGridWin; } wxWindow* GetGridRowLabelWindow() const { return (wxWindow*)m_rowLabelWin; } wxWindow* GetGridColLabelWindow() const { return m_colLabelWin; } wxWindow* GetGridCornerLabelWindow() const { return (wxWindow*)m_cornerLabelWin; } @@ -1904,13 +1952,18 @@ protected: bool m_created; wxGridWindow *m_gridWin; + wxGridWindow *m_frozenColGridWin; + wxGridWindow *m_frozenRowGridWin; + wxGridWindow *m_frozenCornerGridWin; wxGridCornerLabelWindow *m_cornerLabelWin; wxGridRowLabelWindow *m_rowLabelWin; + wxGridRowLabelWindow *m_rowFrozenLabelWin; // the real type of the column window depends on m_useNativeHeader value: // if it is true, its dynamic type is wxHeaderCtrl, otherwise it is // wxGridColLabelWindow, use accessors below when the real type matters wxWindow *m_colLabelWin; + wxWindow *m_colFrozenLabelWin; wxGridColLabelWindow *GetColLabelWindow() const { @@ -1925,6 +1978,10 @@ protected: int m_numRows; int m_numCols; + // Number of frozen rows/columns in the beginning of the grid, 0 if none. + int m_numFrozenRows; + int m_numFrozenCols; + wxGridCellCoords m_currentCellCoords; // the corners of the block being currently selected or wxGridNoCellCoords @@ -2014,7 +2071,8 @@ protected: wxColour m_cellHighlightColour; int m_cellHighlightPenWidth; int m_cellHighlightROPenWidth; - + wxColour m_gridFrozenBorderColour; + int m_gridFrozenBorderPenWidth; // common part of AutoSizeColumn/Row() and GetBestSize() int SetOrCalcColumnSizes(bool calcOnly, bool setAsMin = true); @@ -2200,6 +2258,9 @@ protected: { UpdateBlockBeingSelected(topLeft.GetRow(), topLeft.GetCol(), bottomRight.GetRow(), bottomRight.GetCol()); } + virtual bool ShouldScrollToChildOnFocus(wxWindow* WXUNUSED(win)) wxOVERRIDE + { return false; } + friend class WXDLLIMPEXP_FWD_CORE wxGridSelection; friend class wxGridRowOperations; friend class wxGridColumnOperations; @@ -2218,6 +2279,10 @@ private: // implement wxScrolledWindow method to return m_gridWin size virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size) wxOVERRIDE; + // depending on the values of m_numFrozenRows and m_numFrozenCols, it will + // create and initialize or delete the frozen windows + void InitializeFrozenWindows(); + // redraw the grid lines, should be called after changing their attributes void RedrawGridLines(); @@ -2265,8 +2330,7 @@ private: // // this always returns a valid position, even if the coordinate is out of // bounds (in which case first/last column is returned) - int XToPos(int x) const; - + int XToPos(int x, wxGridWindow *gridWindow) const; // event handlers and their helpers // -------------------------------- @@ -2277,14 +2341,27 @@ private: bool isFirstDrag); // process row/column resizing drag event - void DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper); + void DoGridLineDrag(int pos, + const wxGridOperations& oper, + wxGridWindow* gridWindow); // process mouse drag event in the grid window, return false if starting // dragging was vetoed by the user-defined wxEVT_GRID_CELL_BEGIN_DRAG // handler bool DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords, - bool isFirstDrag); + bool isFirstDrag, + wxGridWindow* gridWindow); + + void DrawGridDragLine(wxPoint position, + const wxGridOperations& oper, + wxGridWindow* gridWindow); + + // return the current grid windows involved in the drag process + void GetDragGridWindows(int pos, + const wxGridOperations& oper, + wxGridWindow*& firstGridWindow, + wxGridWindow*& secondGridWindow); // process different clicks on grid cells void DoGridCellLeftDown(wxMouseEvent& event, @@ -2293,30 +2370,38 @@ private: void DoGridCellLeftDClick(wxMouseEvent& event, const wxGridCellCoords& coords, const wxPoint& pos); - void DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords); + void DoGridCellLeftUp(wxMouseEvent& event, + const wxGridCellCoords& coords, + wxGridWindow* gridWindow); // process movement (but not dragging) event in the grid cell area void DoGridMouseMoveEvent(wxMouseEvent& event, const wxGridCellCoords& coords, - const wxPoint& pos); + const wxPoint& pos, + wxGridWindow* gridWindow); // process mouse events in the grid window - void ProcessGridCellMouseEvent(wxMouseEvent& event); + void ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow* gridWindow); // process mouse events in the row/column labels/corner windows - void ProcessRowLabelMouseEvent(wxMouseEvent& event); - void ProcessColLabelMouseEvent(wxMouseEvent& event); + void ProcessRowLabelMouseEvent(wxMouseEvent& event, + wxGridRowLabelWindow* rowLabelWin); + void ProcessColLabelMouseEvent(wxMouseEvent& event, + wxGridColLabelWindow* colLabelWin); void ProcessCornerLabelMouseEvent(wxMouseEvent& event); void DoColHeaderClick(int col); void DoStartResizeCol(int col); - void DoUpdateResizeCol(int x); void DoUpdateResizeColWidth(int w); void DoStartMoveCol(int col); - void DoEndDragResizeRow(const wxMouseEvent& event); - void DoEndDragResizeCol(const wxMouseEvent& event); + void DoEndDragResizeRow(const wxMouseEvent& event, wxGridWindow *gridWindow); + void DoEndDragResizeCol(const wxMouseEvent& event, wxGridWindow *gridWindow); + void DoEndDragResizeCol(const wxMouseEvent& event) + { + DoEndDragResizeCol(event, m_gridWin); + } void DoEndMoveCol(int pos); // process a TAB keypress @@ -2324,11 +2409,13 @@ private: // common implementations of methods defined for both rows and columns void DeselectLine(int line, const wxGridOperations& oper); - bool DoEndDragResizeLine(const wxGridOperations& oper); + bool DoEndDragResizeLine(const wxGridOperations& oper, wxGridWindow *gridWindow); int PosToLinePos(int pos, bool clipToMinMax, - const wxGridOperations& oper) const; + const wxGridOperations& oper, + wxGridWindow *gridWindow) const; int PosToLine(int pos, bool clipToMinMax, - const wxGridOperations& oper) const; + const wxGridOperations& oper, + wxGridWindow *gridWindow) const; int PosToEdgeOfLine(int pos, const wxGridOperations& oper) const; bool DoMoveCursor(bool expandSelection, diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 0fb81f91ac..053a750f70 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -158,9 +158,9 @@ protected: return m_columns[idx]; } -private: wxGrid *GetOwner() const { return static_cast(GetParent()); } +private: static wxMouseEvent GetDummyMouseEvent() { // make up a dummy event for the grid event to use -- unfortunately we @@ -327,6 +327,7 @@ public: { } + virtual bool IsFrozen() const { return false; } private: void OnPaint( wxPaintEvent& event ); @@ -338,6 +339,18 @@ private: }; +class wxGridRowFrozenLabelWindow : public wxGridRowLabelWindow +{ +public: + wxGridRowFrozenLabelWindow(wxGrid *parent) + : wxGridRowLabelWindow(parent) + { + } + + virtual bool IsFrozen() const { return true; } +}; + + class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxGridSubwindow { public: @@ -346,6 +359,7 @@ public: { } + virtual bool IsFrozen() const { return false; } private: void OnPaint( wxPaintEvent& event ); @@ -357,6 +371,18 @@ private: }; +class wxGridColFrozenLabelWindow : public wxGridColLabelWindow +{ +public: + wxGridColFrozenLabelWindow(wxGrid *parent) + : wxGridColLabelWindow(parent) + { + } + + virtual bool IsFrozen() const { return true; } +}; + + class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxGridSubwindow { public: @@ -377,10 +403,21 @@ private: class WXDLLIMPEXP_ADV wxGridWindow : public wxGridSubwindow { public: - wxGridWindow(wxGrid *parent) + // grid window variants for scrolling possibilities + enum wxGridWindowType + { + wxGridWindowNormal = 0, + wxGridWindowFrozenCol = 1, + wxGridWindowFrozenRow = 2, + wxGridWindowFrozenCorner = wxGridWindowFrozenCol | + wxGridWindowFrozenRow + }; + + wxGridWindow(wxGrid *parent, wxGridWindowType type) : wxGridSubwindow(parent, wxWANTS_CHARS | wxCLIP_CHILDREN, - "GridWindow") + "GridWindow"), + m_type(type) { } @@ -389,7 +426,11 @@ public: virtual bool AcceptsFocus() const wxOVERRIDE { return true; } + wxGridWindowType GetType() const { return m_type; } + private: + const wxGridWindowType m_type; + void OnPaint( wxPaintEvent &event ); void OnMouseWheel( wxMouseEvent& event ); void OnMouseEvent( wxMouseEvent& event ); @@ -467,8 +508,14 @@ public: // 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 total number of rows or columns. + virtual int GetTotalNumberOfLines(const wxGrid *grid) const = 0; + + // Return the current number of rows or columns of a grid window. + virtual int GetNumberOfLines(const wxGrid *grid, wxGridWindow *gridWindow) const = 0; + + // Return the first line for this grid type. + virtual int GetFirstLine(const wxGrid *grid, wxGridWindow *gridWindow) const = 0; // Return the selection mode which allows selecting rows or columns. virtual wxGrid::wxGridSelectionModes GetSelectionMode() const = 0; @@ -515,7 +562,7 @@ public: // 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; + PosToLine(const wxGrid *grid, int pos, wxGridWindow *gridWindow, 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; @@ -565,6 +612,8 @@ public: // Get the width or height of the row or column label window virtual int GetHeaderWindowSize(wxGrid *grid) const = 0; + // Get the row or column frozen grid window + virtual wxGridWindow *GetFrozenGrid(wxGrid* grid) const = 0; // This class is never used polymorphically but give it a virtual dtor // anyhow to suppress g++ complaints about it @@ -576,9 +625,13 @@ class wxGridRowOperations : public wxGridOperations public: virtual wxGridOperations& Dual() const wxOVERRIDE; - virtual int GetNumberOfLines(const wxGrid *grid) const wxOVERRIDE + virtual int GetTotalNumberOfLines(const wxGrid *grid) const wxOVERRIDE { return grid->GetNumberRows(); } + virtual int GetNumberOfLines(const wxGrid *grid, wxGridWindow *gridWindow) const wxOVERRIDE; + + virtual int GetFirstLine(const wxGrid *grid, wxGridWindow *gridWindow) const wxOVERRIDE; + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const wxOVERRIDE { return wxGrid::wxGridSelectRows; } @@ -602,8 +655,8 @@ public: virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const wxOVERRIDE { dc.DrawLine(start, pos, end, pos); } - virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const wxOVERRIDE - { return grid->YToRow(pos, clip); } + virtual int PosToLine(const wxGrid *grid, int pos, wxGridWindow *gridWindow , bool clip = false) const wxOVERRIDE + { return grid->YToRow(pos, clip, gridWindow); } virtual int GetLineStartPos(const wxGrid *grid, int line) const wxOVERRIDE { return grid->GetRowTop(line); } virtual int GetLineEndPos(const wxGrid *grid, int line) const wxOVERRIDE @@ -635,6 +688,9 @@ public: { return grid->GetGridRowLabelWindow(); } virtual int GetHeaderWindowSize(wxGrid *grid) const wxOVERRIDE { return grid->GetRowLabelSize(); } + + virtual wxGridWindow *GetFrozenGrid(wxGrid* grid) const wxOVERRIDE + { return (wxGridWindow*)grid->GetFrozenRowGridWindow(); } }; class wxGridColumnOperations : public wxGridOperations @@ -642,9 +698,13 @@ class wxGridColumnOperations : public wxGridOperations public: virtual wxGridOperations& Dual() const wxOVERRIDE; - virtual int GetNumberOfLines(const wxGrid *grid) const wxOVERRIDE + virtual int GetTotalNumberOfLines(const wxGrid *grid) const wxOVERRIDE { return grid->GetNumberCols(); } + virtual int GetNumberOfLines(const wxGrid *grid, wxGridWindow *gridWindow) const wxOVERRIDE; + + virtual int GetFirstLine(const wxGrid *grid, wxGridWindow *gridWindow) const wxOVERRIDE; + virtual wxGrid::wxGridSelectionModes GetSelectionMode() const wxOVERRIDE { return wxGrid::wxGridSelectColumns; } @@ -668,8 +728,8 @@ public: virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const wxOVERRIDE { dc.DrawLine(pos, start, pos, end); } - virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const wxOVERRIDE - { return grid->XToCol(pos, clip); } + virtual int PosToLine(const wxGrid *grid, int pos, wxGridWindow *gridWindow, bool clip = false) const wxOVERRIDE + { return grid->XToCol(pos, clip, gridWindow); } virtual int GetLineStartPos(const wxGrid *grid, int line) const wxOVERRIDE { return grid->GetColLeft(line); } virtual int GetLineEndPos(const wxGrid *grid, int line) const wxOVERRIDE @@ -704,6 +764,9 @@ public: { return grid->GetGridColLabelWindow(); } virtual int GetHeaderWindowSize(wxGrid *grid) const wxOVERRIDE { return grid->GetColLabelSize(); } + + virtual wxGridWindow *GetFrozenGrid(wxGrid* grid) const wxOVERRIDE + { return (wxGridWindow*)grid->GetFrozenColGridWindow(); } }; // This class abstracts the difference between operations going forward @@ -828,7 +891,7 @@ public: virtual int MoveByPixelDistance(int line, int distance) const wxOVERRIDE { int pos = m_oper.GetLineStartPos(m_grid, line); - return m_oper.PosToLine(m_grid, pos - distance + 1, true); + return m_oper.PosToLine(m_grid, pos - distance + 1, NULL, true); } }; @@ -839,7 +902,7 @@ class wxGridForwardOperations : public wxGridDirectionOperations public: wxGridForwardOperations(wxGrid *grid, const wxGridOperations& oper) : wxGridDirectionOperations(grid, oper), - m_numLines(oper.GetNumberOfLines(grid)) + m_numLines(oper.GetTotalNumberOfLines(grid)) { } @@ -878,7 +941,7 @@ public: virtual int MoveByPixelDistance(int line, int distance) const wxOVERRIDE { int pos = m_oper.GetLineStartPos(m_grid, line); - return m_oper.PosToLine(m_grid, pos + distance, true); + return m_oper.PosToLine(m_grid, pos + distance, NULL, true); } private: diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 2cd8ec92b9..3d460ea68c 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -2687,8 +2687,13 @@ public: Also note that currently @c wxEVT_GRID_LABEL_RIGHT_DCLICK event is not generated for the column labels if the native columns header is used (but this limitation could possibly be lifted in the future). + + Finally, please note that using the native control is currently + incompatible with freezing columns in the grid (see FreezeTo()) and + this function will return @false, without doing anything, if it's + called on a grid in which any columns are frozen. */ - void UseNativeColHeader(bool native = true); + bool UseNativeColHeader(bool native = true); //@} @@ -3767,8 +3772,14 @@ public: /** Enables or disables column moving by dragging with the mouse. + + Note that reordering columns by dragging them is currently not + supported when the grid has any frozen columns (see FreezeTo()) and if + this method is called with @a enable equal to @true in this situation, + it returns @false without doing anything. Otherwise it returns @true to + indicate that it was successful. */ - void EnableDragColMove(bool enable = true); + bool EnableDragColMove(bool enable = true); /** Enables or disables column sizing by dragging with the mouse. @@ -4280,10 +4291,13 @@ public: limited by @a topLeft and @a bottomRight cell in device coords and clipped to the client size of the grid window. + @since 3.1.3 Parameter @a gridWindow has been added. + @see CellToRect() */ wxRect BlockToDeviceRect(const wxGridCellCoords& topLeft, - const wxGridCellCoords& bottomRight) const; + const wxGridCellCoords& bottomRight, + const wxGridWindow *gridWindow = NULL) const; /** Return the rectangle corresponding to the grid cell's size and position @@ -4301,7 +4315,78 @@ public: wxRect CellToRect(const wxGridCellCoords& coords) const; /** - Returns the column at the given pixel position. + Returns the grid window that contains the cell. + + In a grid without frozen rows or columns (see FreezeTo()), this will + always return the same window as GetGridWindow(), however if some parts + of the grid are frozen, this function returns the window containing the + given cell. + + @since 3.1.3 + */ + wxGridWindow* CellToGridWindow( int row, int col ) const; + + /// @overload + wxGridWindow* CellToGridWindow( const wxGridCellCoords& coords ) const + + /** + Returns the grid window that includes the input coordinates. + + @since 3.1.3 + */ + wxGridWindow* DevicePosToGridWindow(wxPoint pos) const; + + /// @overload + wxGridWindow* DevicePosToGridWindow(int x, int y) const; + + /** + Returns the grid window's offset from the grid starting position taking + into account the frozen cells. + + If there are no frozen cells, returns (0, 0). + + @since 3.1.3 + + @see FreezeTo() + */ + void GetGridWindowOffset(const wxGridWindow *gridWindow, int &x, int &y) const; + + /// @overload + wxPoint GetGridWindowOffset(const wxGridWindow *gridWindow) const; + + /** + Translates the device coordinates to the logical ones, taking into + account the grid window type. + + @since 3.1.3 + + @see wxScrolled::CalcUnscrolledPosition() + */ + void CalcGridWindowUnscrolledPosition(int x, int y, + int *xx, int *yy, + const wxGridWindow *gridWindow) const; + /// @overload + wxPoint CalcGridWindowUnscrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const; + + /** + Translates the logical coordinates to the device ones, taking into + account the grid window type. + + @since 3.1.3 + + @see wxScrolled::CalcScrolledPosition() + */ + void CalcGridWindowScrolledPosition(int x, int y, + int *xx, int *yy, + const wxGridWindow *gridWindow) const; + + /// @overload + wxPoint CalcGridWindowScrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const; + + /** + Returns the column at the given pixel position depending on the window. @param x The x position to evaluate. @@ -4309,10 +4394,15 @@ public: If @true, rather than returning @c wxNOT_FOUND, it returns either the first or last column depending on whether @a x is too far to the left or right respectively. + @param gridWindow + The associated grid window that limits the search (note that this + parameter is only available since wxWidgets 3.1.3). + If @a gridWindow is @NULL, it will consider all the cells, no matter + which grid they belong to. @return The column index or @c wxNOT_FOUND. */ - int XToCol(int x, bool clipToMinMax = false) const; + int XToCol(int x, bool clipToMinMax = false, wxGridWindow *gridWindow = NULL) const; /** Returns the column whose right hand edge is close to the given logical @@ -4330,20 +4420,22 @@ public: the mouse position, which is expressed in device coordinates, to logical ones. - @see XToCol(), YToRow() - */ - wxGridCellCoords XYToCell(int x, int y) const; - /** - Translates logical pixel coordinates to the grid cell coordinates. + The parameter @a gridWindow is new since wxWidgets 3.1.3. If it is + specified, i.e. non-@NULL, the coordinates must be in this window + coordinate system and only the cells of this window are considered, + i.e. the function returns @c wxNOT_FOUND if the coordinates are out of + bounds. - Notice that this function expects logical coordinates on input so if - you use this function in a mouse event handler you need to translate - the mouse position, which is expressed in device coordinates, to - logical ones. + If @a gridWindow is @NULL, coordinates are relative to the main grid + window and all cells are considered. @see XToCol(), YToRow() */ - wxGridCellCoords XYToCell(const wxPoint& pos) const; + wxGridCellCoords XYToCell(int x, int y, wxGridWindow *gridWindow = NULL) const; + + /// @overload + wxGridCellCoords XYToCell(const wxPoint& pos, wxGridWindow *gridWindow = NULL) const; + // XYToCell(int, int, wxGridCellCoords&) overload is intentionally // undocumented, using it is ugly and non-const reference parameters are // not used in wxWidgets API @@ -4359,9 +4451,16 @@ public: /** Returns the grid row that corresponds to the logical @a y coordinate. - Returns @c wxNOT_FOUND if there is no row at the @a y position. + + The parameter @a gridWindow is new since wxWidgets 3.1.3. If it is + specified, i.e. non-@NULL, only the cells of this window are + considered, i.e. the function returns @c wxNOT_FOUND if @a y is out of + bounds. + + If @a gridWindow is @NULL, the function returns @c wxNOT_FOUND only if + there is no row at all at the @a y position. */ - int YToRow(int y, bool clipToMinMax = false) const; + int YToRow(int y, bool clipToMinMax = false, wxGridWindow *gridWindow = NULL) const; //@} @@ -4489,6 +4588,31 @@ public: */ bool DeleteRows(int pos = 0, int numRows = 1, bool updateLabels = true); + /** + Sets or resets the frozen columns and rows. + + @param row + The number of rows to freeze, 0 means to unfreeze all rows. + @param col + The number of columns to freeze, 0 means to unfreeze all columns. + @return @true on success or @false if it failed. + + Note that this method doesn't do anything, and returns @false, if any + of the following conditions are true: + - Either @a row or @a col are out of range + - Size of the frozen area would be bigger than the current viewing area + - There are any merged cells in the area to be frozen + - Grid uses a native header control (see UseNativeColHeader()) + + (some of these limitations could be lifted in the future). + + @since 3.1.3 + */ + bool FreezeTo(unsigned row, unsigned col); + + /// @overload + bool FreezeTo(const wxGridCellCoords& coords); + /** Decrements the grid's batch count. @@ -4535,6 +4659,28 @@ public: */ int GetNumberRows() const; + /** + Returns the number of frozen grid columns. + + If there are no frozen columns, returns 0. + + @since 3.1.3 + + @see FreezeTo() + */ + int GetNumberFrozenCols() const; + + /** + Returns the number of frozen grid rows. + + If there are no frozen rows, returns 0. + + @since 3.1.3 + + @see FreezeTo() + */ + int GetNumberFrozenRows() const; + /** Returns the attribute for the given cell creating one if necessary. @@ -4693,9 +4839,12 @@ public: void SetRowAttr(int row, wxGridCellAttr* attr); - wxArrayInt CalcRowLabelsExposed( const wxRegion& reg ); - wxArrayInt CalcColLabelsExposed( const wxRegion& reg ); - wxGridCellCoordsArray CalcCellsExposed( const wxRegion& reg ); + wxArrayInt CalcRowLabelsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; + wxArrayInt CalcColLabelsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; + wxGridCellCoordsArray CalcCellsExposed( const wxRegion& reg, + wxGridWindow *gridWindow = NULL) const; //@} @@ -4776,15 +4925,19 @@ public: Return the various child windows of wxGrid. - wxGrid is an empty parent window for 4 children representing the column - labels window (top), the row labels window (left), the corner window - (top left) and the main grid window. It may be necessary to use these - individual windows and not the wxGrid window itself if you need to - handle events for them (this can be done using wxEvtHandler::Connect() - or wxWindow::PushEventHandler()) or do something else requiring the use - of the correct window pointer. Notice that you should not, however, - change these windows (e.g. reposition them or draw over them) because - they are managed by wxGrid itself. + wxGrid is an empty parent window for at least 4 children representing + the column labels window (top), the row labels window (left), the + corner window (top left) and the main grid window. It may be necessary + to use these individual windows and not the wxGrid window itself if you + need to handle events for them (using wxEvtHandler::Bind()) or do + something else requiring the use of the correct window pointer. Notice + that you should not, however, change these windows (e.g. reposition + them or draw over them) because they are managed by wxGrid itself. + + When parts of the grid are frozen using FreezeTo() function, the main + grid window contains only the unfrozen part and additional windows are + used for the parts containing frozen rows and/or columns and the corner + window if both some rows and some columns are frozen. */ //@{ @@ -4795,6 +4948,33 @@ public: */ wxWindow *GetGridWindow() const; + /** + Return the corner grid window containing frozen cells. + + This window is shown only when there are frozen rows and columns. + + @since 3.1.3 + */ + wxWindow* GetFrozenCornerGridWindow() const; + + /** + Return the rows grid window containing row frozen cells. + + This window is shown only when there are frozen rows. + + @since 3.1.3 + */ + wxWindow* GetFrozenRowGridWindow() const; + + /** + Return the columns grid window containing column frozen cells. + + This window is shown only when there are frozen columns. + + @since 3.1.3 + */ + wxWindow* GetFrozenColGridWindow() const; + /** Return the row labels window. @@ -4864,6 +5044,8 @@ public: void SetCellHighlightColour( const wxColour& ); void SetCellHighlightPenWidth(int width); void SetCellHighlightROPenWidth(int width); + void SetGridFrozenBorderColour( const wxColour& ); + void SetGridFrozenBorderPenWidth( int width ); protected: diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index 943d8ee76d..651fd3de0a 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -189,6 +189,8 @@ wxBEGIN_EVENT_TABLE( GridFrame, wxFrame ) EVT_MENU( ID_SELCOLS, GridFrame::SelectCols ) EVT_MENU( ID_SELROWSORCOLS, GridFrame::SelectRowsOrCols ) + EVT_MENU( ID_FREEZE_OR_THAW, GridFrame::FreezeOrThaw ) + EVT_MENU( ID_SET_CELL_FG_COLOUR, GridFrame::SetCellFgColour ) EVT_MENU( ID_SET_CELL_BG_COLOUR, GridFrame::SetCellBgColour ) @@ -370,6 +372,8 @@ GridFrame::GridFrame() editMenu->Append( ID_CLEARGRID, "Cl&ear grid cell contents" ); editMenu->Append( ID_SETCORNERLABEL, "&Set corner label..." ); + editMenu->AppendCheckItem( ID_FREEZE_OR_THAW, "Freeze up to cursor\tCtrl-F" ); + wxMenu *selectMenu = new wxMenu; selectMenu->Append( ID_SELECT_UNSELECT, "Add new cells to the selection", "When off, old selection is deselected before " @@ -573,10 +577,10 @@ GridFrame::GridFrame() "This takes two cells", "Another choice", }; - grid->SetCellEditor(4, 0, new wxGridCellChoiceEditor(WXSIZEOF(choices), choices)); - grid->SetCellSize(4, 0, 1, 2); - grid->SetCellValue(4, 0, choices[0]); - grid->SetCellOverflow(4, 0, false); + grid->SetCellEditor(4, 2, new wxGridCellChoiceEditor(WXSIZEOF(choices), choices)); + grid->SetCellSize(4, 2, 1, 2); + grid->SetCellValue(4, 2, choices[0]); + grid->SetCellOverflow(4, 2, false); grid->SetCellSize(7, 1, 3, 4); grid->SetCellAlignment(7, 1, wxALIGN_CENTRE, wxALIGN_CENTRE); @@ -1209,6 +1213,30 @@ void GridFrame::SelectRowsOrCols( wxCommandEvent& WXUNUSED(ev) ) grid->SetSelectionMode( wxGrid::wxGridSelectRowsOrColumns ); } +void GridFrame::FreezeOrThaw(wxCommandEvent& ev) +{ + if ( ev.IsChecked() ) + { + if ( !grid->FreezeTo(grid->GetGridCursorCoords()) ) + { + wxLogMessage("Failed to freeze the grid."); + GetMenuBar()->Check(ID_FREEZE_OR_THAW, false); + return; + } + + wxLogMessage("Grid is now frozen"); + } + else + { + // This never fails. + grid->FreezeTo(0, 0); + + wxLogMessage("Grid is now thawed"); + } + + GetMenuBar()->Enable( ID_TOGGLECOLMOVING, !grid->IsFrozen() ); +} + void GridFrame::SetCellFgColour( wxCommandEvent& WXUNUSED(ev) ) { wxColour col = wxGetColourFromUser(this); diff --git a/samples/grid/griddemo.h b/samples/grid/griddemo.h index c8abd6d453..4b4d5e83c3 100644 --- a/samples/grid/griddemo.h +++ b/samples/grid/griddemo.h @@ -75,6 +75,8 @@ class GridFrame : public wxFrame void SelectCols( wxCommandEvent& ); void SelectRowsOrCols( wxCommandEvent& ); + void FreezeOrThaw( wxCommandEvent& ); + void DeselectCell(wxCommandEvent& event); void DeselectCol(wxCommandEvent& event); void DeselectRow(wxCommandEvent& event); @@ -204,6 +206,8 @@ public: ID_SIZE_LABELS_ROW, ID_SIZE_GRID, + ID_FREEZE_OR_THAW, + ID_SET_HIGHLIGHT_WIDTH, ID_SET_RO_HIGHLIGHT_WIDTH, diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 844dc150bb..6fb374a048 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -201,6 +201,44 @@ wxGridOperations& wxGridColumnOperations::Dual() const return s_rowOper; } +int wxGridRowOperations::GetNumberOfLines(const wxGrid *grid, wxGridWindow *gridWindow) const +{ + if ( !gridWindow ) + return grid->GetNumberRows(); + + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow ) + return grid->GetNumberFrozenRows(); + + return grid->GetNumberRows() - grid->GetNumberFrozenRows(); +} + +int wxGridColumnOperations::GetNumberOfLines(const wxGrid *grid, wxGridWindow *gridWindow) const +{ + if ( !gridWindow ) + return grid->GetNumberCols(); + + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol ) + return grid->GetNumberFrozenCols(); + + return grid->GetNumberCols() - grid->GetNumberFrozenCols(); +} + +int wxGridRowOperations::GetFirstLine(const wxGrid *grid, wxGridWindow *gridWindow) const +{ + if ( !gridWindow || gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow ) + return 0; + + return grid->GetNumberFrozenRows(); +} + +int wxGridColumnOperations::GetFirstLine(const wxGrid *grid, wxGridWindow *gridWindow) const +{ + if ( !gridWindow || gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol ) + return 0; + + return grid->GetNumberFrozenCols(); +} + // ---------------------------------------------------------------------------- // wxGridCellWorker is an (almost) empty common base class for // wxGridCellRenderer and wxGridCellEditor managing ref counting @@ -1700,17 +1738,23 @@ void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) // m_owner->PrepareDC( dc ); int x, y; - m_owner->CalcUnscrolledPosition( 0, 0, &x, &y ); + wxGridWindow *gridWindow = IsFrozen() ? m_owner->m_frozenRowGridWin : + m_owner->m_gridWin; + m_owner->GetGridWindowOffset(gridWindow, x, y); + m_owner->CalcGridWindowUnscrolledPosition( x, y, &x, &y, gridWindow ); wxPoint pt = dc.GetDeviceOrigin(); dc.SetDeviceOrigin( pt.x, pt.y-y ); - wxArrayInt rows = m_owner->CalcRowLabelsExposed( GetUpdateRegion() ); + wxArrayInt rows = m_owner->CalcRowLabelsExposed( GetUpdateRegion(), gridWindow); m_owner->DrawRowLabels( dc, rows ); + + if ( IsFrozen() ) + m_owner->DrawLabelFrozenBorder(dc, this, true); } void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event ) { - m_owner->ProcessRowLabelMouseEvent( event ); + m_owner->ProcessRowLabelMouseEvent( event, this ); } void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) @@ -1738,17 +1782,23 @@ void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) // m_owner->PrepareDC( dc ); int x, y; - m_owner->CalcUnscrolledPosition( 0, 0, &x, &y ); + wxGridWindow *gridWindow = IsFrozen() ? m_owner->m_frozenColGridWin : + m_owner->m_gridWin; + m_owner->GetGridWindowOffset(gridWindow, x, y); + m_owner->CalcGridWindowUnscrolledPosition( x, y, &x, &y, gridWindow ); wxPoint pt = dc.GetDeviceOrigin(); dc.SetDeviceOrigin( pt.x-x, pt.y ); - wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() ); + wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion(), gridWindow ); m_owner->DrawColLabels( dc, cols ); + + if ( IsFrozen() ) + m_owner->DrawLabelFrozenBorder(dc, this, false); } void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event ) { - m_owner->ProcessColLabelMouseEvent( event ); + m_owner->ProcessColLabelMouseEvent( event, this ); } void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) @@ -1800,14 +1850,18 @@ wxEND_EVENT_TABLE() void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPaintDC dc( this ); - m_owner->PrepareDC( dc ); + m_owner->PrepareDCFor( dc, this ); wxRegion reg = GetUpdateRegion(); - wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg ); + + wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg , this ); m_owner->DrawGridCellArea( dc, dirtyCells ); - m_owner->DrawGridSpace( dc ); + m_owner->DrawGridSpace( dc, this ); - m_owner->DrawAllGridLines( dc, reg ); + m_owner->DrawAllGridWindowLines( dc, reg, this ); + + if ( m_type != wxGridWindow::wxGridWindowNormal ) + m_owner->DrawFrozenBorder( dc, this ); m_owner->DrawHighlight( dc, dirtyCells ); } @@ -2120,6 +2174,11 @@ void wxGrid::ScrollWindow( int dx, int dy, const wxRect *rect ) // wxGridWindow::ScrollWindow() calls this method back. m_gridWin->wxWindow::ScrollWindow( dx, dy, rect ); + if ( m_frozenColGridWin ) + m_frozenColGridWin->wxWindow::ScrollWindow( 0, dy, rect ); + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->wxWindow::ScrollWindow( dx, 0, rect ); + m_rowLabelWin->ScrollWindow( 0, dy, rect ); m_colLabelWin->ScrollWindow( dx, 0, rect ); } @@ -2129,7 +2188,7 @@ void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) if (event.ButtonDown(wxMOUSE_BTN_LEFT) && FindFocus() != this) SetFocus(); - m_owner->ProcessGridCellMouseEvent( event ); + m_owner->ProcessGridCellMouseEvent( event, this ); } void wxGridWindow::OnMouseWheel( wxMouseEvent& event ) @@ -2184,7 +2243,7 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) const wxGridCellCoords cursorCoords(m_owner->GetGridCursorRow(), m_owner->GetGridCursorCol()); const wxRect cursor = - m_owner->BlockToDeviceRect(cursorCoords, cursorCoords); + m_owner->BlockToDeviceRect(cursorCoords, cursorCoords, this); if (cursor != wxGridNoCellRect) Refresh(true, &cursor); } @@ -2193,8 +2252,8 @@ void wxGridWindow::OnFocus(wxFocusEvent& event) event.Skip(); } -#define internalXToCol(x) XToCol(x, true) -#define internalYToRow(y) YToRow(y, true) +#define internalXToCol(x, gridWindowPtr) XToCol(x, true, gridWindowPtr) +#define internalYToRow(y, gridWindowPtr) YToRow(y, true, gridWindowPtr) ///////////////////////////////////////////////////////////////////// @@ -2304,13 +2363,15 @@ void wxGrid::Create() m_numRows = 0; m_numCols = 0; + m_numFrozenRows = 0; + m_numFrozenCols = 0; m_currentCellCoords = wxGridNoCellCoords; // subwindow components that make up the wxGrid m_rowLabelWin = new wxGridRowLabelWindow(this); CreateColumnWindow(); m_cornerLabelWin = new wxGridCornerLabelWindow(this); - m_gridWin = new wxGridWindow( this ); + m_gridWin = new wxGridWindow(this, wxGridWindow::wxGridWindowNormal); SetTargetWindow( m_gridWin ); @@ -2425,6 +2486,8 @@ wxGrid::SetTable(wxGridTableBase *table, m_ownTable = false; m_numRows = 0; m_numCols = 0; + m_numFrozenRows = 0; + m_numFrozenCols = 0; checkSelection = true; // kill row and column size arrays @@ -2487,8 +2550,13 @@ void wxGrid::Init() m_cornerLabelWin = NULL; m_rowLabelWin = NULL; + m_rowFrozenLabelWin = NULL; m_colLabelWin = NULL; + m_colFrozenLabelWin = NULL; m_gridWin = NULL; + m_frozenColGridWin = NULL; + m_frozenRowGridWin = NULL; + m_frozenCornerGridWin = NULL; m_table = NULL; m_ownTable = false; @@ -2535,6 +2603,8 @@ void wxGrid::Init() m_cellHighlightColour = *wxBLACK; m_cellHighlightPenWidth = 2; m_cellHighlightROPenWidth = 1; + m_gridFrozenBorderColour = *wxBLACK; + m_gridFrozenBorderPenWidth = 2; m_canDragColMove = false; @@ -2681,6 +2751,10 @@ int wxGrid::GetRowBottom(int row) const void wxGrid::CalcDimensions() { + // if our OnSize() hadn't been called (it would if we have scrollbars), we + // still must reposition the children + CalcWindowSizes(); + // compute the size of the scrollable area int w = m_numCols > 0 ? GetColRight(GetColAt(m_numCols - 1)) : 0; int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0; @@ -2711,6 +2785,10 @@ void wxGrid::CalcDimensions() attr->DecRef(); } + wxPoint offset = GetGridWindowOffset(m_gridWin); + w -= offset.x; + h -= offset.y; + // preserve (more or less) the previous position int x, y; GetViewStart( &x, &y ); @@ -2725,17 +2803,14 @@ void wxGrid::CalcDimensions() m_gridWin->SetVirtualSize(w, h); Scroll(x, y); AdjustScrollbars(); - - // if our OnSize() hadn't been called (it would if we have scrollbars), we - // still must reposition the children - CalcWindowSizes(); } wxSize wxGrid::GetSizeAvailableForScrollTarget(const wxSize& size) { + wxPoint offset = GetGridWindowOffset(m_gridWin); wxSize sizeGridWin(size); - sizeGridWin.x -= m_rowLabelWidth; - sizeGridWin.y -= m_colLabelHeight; + sizeGridWin.x -= m_rowLabelWidth - offset.x; + sizeGridWin.y -= m_colLabelHeight - offset.y; return sizeGridWin; } @@ -2750,10 +2825,19 @@ void wxGrid::CalcWindowSizes() int cw, ch; GetClientSize( &cw, &ch ); + // frozen rows and cols windows size + int fgw = 0, fgh = 0; + + for ( int i = 0; i < m_numFrozenRows; i++ ) + fgh += m_rowHeights[i]; + + for ( int i = 0; i < m_numFrozenCols; i++ ) + fgw += m_colWidths[i]; + // the grid may be too small to have enough space for the labels yet, don't // size the windows to negative sizes in this case - int gw = cw - m_rowLabelWidth; - int gh = ch - m_colLabelHeight; + int gw = cw - m_rowLabelWidth - fgw; + int gh = ch - m_colLabelHeight - fgh; if (gw < 0) gw = 0; if (gh < 0) @@ -2762,14 +2846,29 @@ void wxGrid::CalcWindowSizes() if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() ) m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight ); + if ( m_colFrozenLabelWin && m_colFrozenLabelWin->IsShown() ) + m_colFrozenLabelWin->SetSize( m_rowLabelWidth, 0, fgw, m_colLabelHeight); + if ( m_colLabelWin && m_colLabelWin->IsShown() ) - m_colLabelWin->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); + m_colLabelWin->SetSize( m_rowLabelWidth + fgw, 0, gw, m_colLabelHeight ); + + if ( m_rowFrozenLabelWin && m_rowFrozenLabelWin->IsShown() ) + m_rowFrozenLabelWin->SetSize ( 0, m_colLabelHeight, m_rowLabelWidth, fgh); if ( m_rowLabelWin && m_rowLabelWin->IsShown() ) - m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh ); + m_rowLabelWin->SetSize( 0, m_colLabelHeight + fgh, m_rowLabelWidth, gh ); + + if ( m_frozenCornerGridWin && m_frozenCornerGridWin->IsShown() ) + m_frozenCornerGridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, fgw, fgh ); + + if ( m_frozenColGridWin && m_frozenColGridWin->IsShown() ) + m_frozenColGridWin->SetSize( m_rowLabelWidth, m_colLabelHeight + fgh, fgw, gh); + + if ( m_frozenRowGridWin && m_frozenRowGridWin->IsShown() ) + m_frozenRowGridWin->SetSize( m_rowLabelWidth + fgw, m_colLabelHeight, gw, fgh); if ( m_gridWin && m_gridWin->IsShown() ) - m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, gw, gh ); + m_gridWin->SetSize( m_rowLabelWidth + fgw, m_colLabelHeight + fgh, gw, gh ); } // this is called when the grid table sends a message @@ -3142,12 +3241,12 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) InvalidateBestSize(); if (result && !GetBatchCount() ) - m_gridWin->Refresh(); + Refresh(); return result; } -wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const +wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg, wxGridWindow *gridWindow ) const { wxRegionIterator iter( reg ); wxRect r; @@ -3158,6 +3257,7 @@ wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const while ( iter ) { r = iter.GetRect(); + r.Offset(GetGridWindowOffset(gridWindow)); // TODO: remove this when we can... // There is a bug in wxMotif that gives garbage update @@ -3175,13 +3275,13 @@ wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const // logical bounds of update region // int dummy; - CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top ); - CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom ); + CalcGridWindowUnscrolledPosition( 0, r.GetTop(), &dummy, &top, gridWindow ); + CalcGridWindowUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom, gridWindow ); // find the row labels within these bounds // int row; - for ( row = internalYToRow(top); row < m_numRows; row++ ) + for ( row = internalYToRow(top, gridWindow); row < m_numRows; row++ ) { if ( GetRowBottom(row) < top ) continue; @@ -3198,7 +3298,7 @@ wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const return rowlabels; } -wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const +wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg, wxGridWindow *gridWindow ) const { wxRegionIterator iter( reg ); wxRect r; @@ -3209,6 +3309,7 @@ wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const while ( iter ) { r = iter.GetRect(); + r.Offset( GetGridWindowOffset(gridWindow) ); // TODO: remove this when we can... // There is a bug in wxMotif that gives garbage update @@ -3226,13 +3327,13 @@ wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const // logical bounds of update region // int dummy; - CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy ); - CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy ); + CalcGridWindowUnscrolledPosition( r.GetLeft(), 0, &left, &dummy, gridWindow ); + CalcGridWindowUnscrolledPosition( r.GetRight(), 0, &right, &dummy, gridWindow ); // find the cells within these bounds // - int colPos; - for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ ) + int colPos = GetColPos( internalXToCol(left, gridWindow) ); + for ( ; colPos < m_numCols; colPos++ ) { int col; col = GetColAt( colPos ); @@ -3252,7 +3353,8 @@ wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const return colLabels; } -wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const +wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg, + wxGridWindow *gridWindow) const { wxRect r; @@ -3262,6 +3364,7 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const for ( wxRegionIterator iter(reg); iter; ++iter ) { r = iter.GetRect(); + r.Offset(GetGridWindowOffset(gridWindow)); // Skip 0-height cells, they're invisible anyhow, don't waste time // getting their rectangles and so on. @@ -3274,22 +3377,25 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const // scrollbar with middle button. This is a work-around // #if defined(__WXMOTIF__) - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - if ( r.GetTop() > ch ) r.SetTop( 0 ); - if ( r.GetLeft() > cw ) r.SetLeft( 0 ); - r.SetRight( wxMin( r.GetRight(), cw ) ); - r.SetBottom( wxMin( r.GetBottom(), ch ) ); + if ( gridWindow ) + { + int cw, ch; + gridWindow->GetClientSize( &cw, &ch ); + if ( r.GetTop() > ch ) r.SetTop( 0 ); + if ( r.GetLeft() > cw ) r.SetLeft( 0 ); + r.SetRight( wxMin( r.GetRight(), cw ) ); + r.SetBottom( wxMin( r.GetBottom(), ch ) ); + } #endif // logical bounds of update region // - CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top ); - CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); + CalcGridWindowUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top, gridWindow ); + CalcGridWindowUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom, gridWindow ); // find the cells within these bounds wxArrayInt cols; - for ( int row = internalYToRow(top); row < m_numRows; row++ ) + for ( int row = internalYToRow(top, gridWindow); row < m_numRows; row++ ) { if ( GetRowBottom(row) <= top ) continue; @@ -3303,7 +3409,7 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const if ( cols.empty() ) { // do determine the dirty columns - for ( int pos = XToPos(left); pos <= XToPos(right); pos++ ) + for ( int pos = XToPos(left, gridWindow); pos <= XToPos(right, gridWindow); pos++ ) cols.push_back(GetColAt(pos)); // if there are no dirty columns at all, nothing to do @@ -3320,12 +3426,32 @@ wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const return cellsExposed; } - -void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) +void wxGrid::PrepareDCFor(wxDC &dc, wxGridWindow *gridWindow) { - int x, y, row; - wxPoint pos( event.GetPosition() ); - CalcUnscrolledPosition( pos.x, pos.y, &x, &y ); + wxScrolledWindow::PrepareDC( dc ); + + wxPoint dcOrigin = dc.GetDeviceOrigin() - GetGridWindowOffset(gridWindow); + + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol ) + dcOrigin.x = 0; + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow ) + dcOrigin.y = 0; + + dc.SetDeviceOrigin(dcOrigin.x, dcOrigin.y); +} + +void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindow* rowLabelWin ) +{ + wxGridWindow *gridWindow = rowLabelWin->IsFrozen() ? m_frozenRowGridWin : m_gridWin; + + event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow)); + + // for drag, we could be moving from the window sending the event to the other + if ( rowLabelWin->IsFrozen() && event.GetPosition().y > rowLabelWin->GetClientSize().y ) + gridWindow = m_gridWin; + + wxPoint pos = CalcGridWindowUnscrolledPosition(event.GetPosition(), gridWindow); + int row; if ( event.Dragging() ) { @@ -3338,28 +3464,13 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) { case WXGRID_CURSOR_RESIZE_ROW: { - int cw, ch, left, dummy; - m_gridWin->GetClientSize( &cw, &ch ); - CalcUnscrolledPosition( 0, 0, &left, &dummy ); - - 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; + DrawGridDragLine(event.GetPosition(), wxGridRowOperations(), gridWindow); } break; case WXGRID_CURSOR_SELECT_ROW: { - if ( (row = YToRow( y )) >= 0 ) + if ( (row = YToRow( pos.y )) >= 0 ) { if ( m_selection ) m_selection->SelectRow(row, event); @@ -3386,21 +3497,21 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // if ( event.Entering() || event.Leaving() ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, rowLabelWin); } // ------------ Left button pressed // else if ( event.LeftDown() ) { - row = YToEdgeOfRow(y); + row = YToEdgeOfRow(pos.y); if ( row != wxNOT_FOUND && CanDragRowSize(row) ) { - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin); } else // not a request to start resizing { - row = YToRow(y); + row = YToRow(pos.y); if ( row >= 0 && !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) ) { @@ -3423,7 +3534,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) } } - ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, rowLabelWin); } } } @@ -3432,7 +3543,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if (event.LeftDClick() ) { - row = YToEdgeOfRow(y); + row = YToEdgeOfRow(pos.y); if ( row != wxNOT_FOUND && CanDragRowSize(row) ) { // adjust row height depending on label text @@ -3447,7 +3558,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) } else // not on row separator or it's not resizable { - row = YToRow(y); + row = YToRow(pos.y); if ( row >=0 && !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) ) { @@ -3461,9 +3572,9 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) else if ( event.LeftUp() ) { if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) - DoEndDragResizeRow(event); + DoEndDragResizeRow(event, gridWindow); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, rowLabelWin); m_dragLastPos = -1; } @@ -3471,7 +3582,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDown() ) { - row = YToRow(y); + row = YToRow(pos.y); if ( row >=0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) ) { @@ -3483,7 +3594,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.RightDClick() ) { - row = YToRow(y); + row = YToRow(pos.y); if ( row >= 0 && !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) ) { @@ -3495,18 +3606,18 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) // else if ( event.Moving() ) { - m_dragRowOrCol = YToEdgeOfRow( y ); + m_dragRowOrCol = YToEdgeOfRow( pos.y ); if ( m_dragRowOrCol != wxNOT_FOUND ) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { if ( CanDragRowSize(m_dragRowOrCol) ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin, false); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, rowLabelWin, false); } } } @@ -3572,34 +3683,25 @@ void wxGrid::DoStartResizeCol(int col) 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); + wxPoint pt(GetColLeft(m_dragRowOrCol) + w, 0); + + DrawGridDragLine(pt, wxGridColumnOperations(), m_gridWin); } -void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) +void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindow* colLabelWin ) { int x; - CalcUnscrolledPosition( event.GetPosition().x, 0, &x, NULL ); + wxGridWindow *gridWindow = colLabelWin->IsFrozen() ? m_frozenColGridWin : m_gridWin; + + event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow)); + + // for drag, we could be moving from the window sending the event to the other + if (colLabelWin->IsFrozen() && event.GetPosition().x > colLabelWin->GetClientSize().x) + gridWindow = m_gridWin; + + CalcGridWindowUnscrolledPosition(event.GetPosition().x, 0, &x, NULL, gridWindow); int col = XToCol(x); if ( event.Dragging() ) @@ -3617,7 +3719,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - DoUpdateResizeCol(x); + DrawGridDragLine(event.GetPosition(), wxGridColumnOperations(), gridWindow); break; case WXGRID_CURSOR_SELECT_COL: @@ -3632,7 +3734,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) case WXGRID_CURSOR_MOVE_COL: { - int posNew = XToPos(x); + int posNew = XToPos(x, NULL); int colNew = GetColAt(posNew); // determine the position of the drop marker @@ -3703,7 +3805,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // if ( event.Entering() || event.Leaving() ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, colLabelWin); } // ------------ Left button pressed @@ -3713,7 +3815,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) int colEdge = XToEdgeOfCol(x); if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) ) { - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow()); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin); } else // not a request to start resizing { @@ -3753,7 +3855,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) } } - ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, GetColLabelWindow()); + ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, colLabelWin); } } } @@ -3782,7 +3884,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, colEdge, event); - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, colLabelWin); m_dragLastPos = -1; } } @@ -3794,7 +3896,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) switch ( m_cursorMode ) { case WXGRID_CURSOR_RESIZE_COL: - DoEndDragResizeCol(event); + DoEndDragResizeCol(event, gridWindow); break; case WXGRID_CURSOR_MOVE_COL: @@ -3808,7 +3910,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) else { // get the position of the column we're over - int pos = XToPos(x); + int pos = XToPos(x, NULL); // insert the column being dragged either before or after // it, depending on where exactly it was dropped, so @@ -3881,12 +3983,12 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { if ( CanDragColSize(m_dragRowOrCol) ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin, false); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow(), false); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, colLabelWin, false); } } } @@ -4081,35 +4183,91 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event, return performDefault; } -void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper) +void wxGrid::GetDragGridWindows(int pos, + const wxGridOperations& oper, + wxGridWindow*& firstGridWindow, + wxGridWindow*& secondGridWindow) { - wxClientDC dc(m_gridWin); - PrepareDC(dc); - dc.SetLogicalFunction(wxINVERT); + int numFrozenLines = oper.Select(wxPoint(m_numFrozenRows, m_numFrozenCols)); - const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)), - m_gridWin->GetClientSize()); + if ( numFrozenLines > 0 ) + { + int lineEnd = oper.GetLineEndPos(this, numFrozenLines - 1); - // erase the previously drawn line, if any - if ( m_dragLastPos >= 0 ) - oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); + // check if it is within frozen windows space + if ( pos < lineEnd ) + { + firstGridWindow = m_frozenCornerGridWin; + secondGridWindow = oper.GetFrozenGrid(this);; + } + else + { + firstGridWindow = oper.Dual().GetFrozenGrid(this); + secondGridWindow = m_gridWin; + } + } + else + { + firstGridWindow = m_gridWin; + secondGridWindow = NULL; + } +} +void wxGrid::DrawGridDragLine(wxPoint position, + const wxGridOperations& oper, + wxGridWindow* gridWindow) +{ // we need the vertical position for rows and horizontal for columns here - m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition())); + int pos = oper.Dual().Select(CalcGridWindowUnscrolledPosition(position, gridWindow)); // 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; + if ( pos < posMin ) + pos = posMin; + + // erase the previously drawn line, if any + if ( m_dragLastPos >= 0 ) + { + wxGridWindow* prevGridWindows[2] = { NULL, NULL }; + GetDragGridWindows(m_dragLastPos, oper, prevGridWindows[0], prevGridWindows[1]); + + DoGridLineDrag(m_dragLastPos, oper, prevGridWindows[0]); + DoGridLineDrag(m_dragLastPos, oper, prevGridWindows[1]); + } // and draw it at the new position - oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); + wxGridWindow* currGridWindows[2] = { NULL, NULL }; + GetDragGridWindows(pos, oper, currGridWindows[0], currGridWindows[1]); + + DoGridLineDrag(pos, oper, currGridWindows[0]); + DoGridLineDrag(pos, oper, currGridWindows[1]); + + m_dragLastPos = pos; +} + +void wxGrid::DoGridLineDrag(int pos, + const wxGridOperations& oper, + wxGridWindow* gridWindow) +{ + if ( !gridWindow ) + return; + + wxClientDC dc(gridWindow); + PrepareDCFor(dc, gridWindow); + dc.SetLogicalFunction(wxINVERT); + + wxPoint offset = GetGridWindowOffset(gridWindow); + const wxRect rectWin(CalcGridWindowUnscrolledPosition(offset, gridWindow), + gridWindow->GetClientSize()); + + oper.DrawParallelLineInRect(dc, rectWin, pos); } bool wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords, - bool isFirstDrag) + bool isFirstDrag, + wxGridWindow *gridWindow) { switch ( m_cursorMode ) { @@ -4117,11 +4275,11 @@ bool wxGrid::DoGridDragEvent(wxMouseEvent& event, return DoGridCellDrag(event, coords, isFirstDrag); case WXGRID_CURSOR_RESIZE_ROW: - DoGridLineDrag(event, wxGridRowOperations()); + DrawGridDragLine(event.GetPosition(), wxGridRowOperations(), gridWindow); break; case WXGRID_CURSOR_RESIZE_COL: - DoGridLineDrag(event, wxGridColumnOperations()); + DrawGridDragLine(event.GetPosition(), wxGridColumnOperations(), gridWindow); break; default: @@ -4218,7 +4376,9 @@ wxGrid::DoGridCellLeftDClick(wxMouseEvent& event, } void -wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) +wxGrid::DoGridCellLeftUp(wxMouseEvent& event, + const wxGridCellCoords& coords, + wxGridWindow* gridWindow) { if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { @@ -4256,12 +4416,12 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) { ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeRow(event); + DoEndDragResizeRow(event, gridWindow); } else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) { ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); - DoEndDragResizeCol(event); + DoEndDragResizeCol(event, gridWindow); } m_dragLastPos = -1; @@ -4270,7 +4430,8 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords) void wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), const wxGridCellCoords& coords, - const wxPoint& pos) + const wxPoint& pos, + wxGridWindow* gridWindow) { if ( coords.GetRow() < 0 || coords.GetCol() < 0 ) { @@ -4287,7 +4448,7 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), // if ( dragRow >= 0 && dragCol >= 0 ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); return; } @@ -4296,7 +4457,7 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { m_dragRowOrCol = dragRow; - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, gridWindow, false); } } // When using the native header window we can only resize the columns by @@ -4308,24 +4469,36 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event), if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { m_dragRowOrCol = dragCol; - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, gridWindow, false); } } else // Neither on a row or col edge { if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL); + ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, gridWindow, false); } } } -void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) +void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventGridWindow ) { - const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); + // the window receiving the event might not be the same as the one under + // the mouse (e.g. in the case of a dragging event started in one window, + // but continuing over another one) + wxGridWindow *gridWindow = + DevicePosToGridWindow(event.GetPosition() + eventGridWindow->GetPosition()); + + if ( !gridWindow ) + gridWindow = eventGridWindow; + + event.SetPosition(event.GetPosition() + eventGridWindow->GetPosition() - + wxPoint(m_rowLabelWidth, m_colLabelHeight)); + + wxPoint pos = CalcGridWindowUnscrolledPosition(event.GetPosition(), gridWindow); // coordinates of the cell under mouse - wxGridCellCoords coords = XYToCell(pos); + wxGridCellCoords coords = XYToCell(pos, gridWindow); int cell_rows, cell_cols; GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols ); @@ -4342,7 +4515,7 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) // Note that we must call this one first, before resetting the // drag-related data, as it relies on m_cursorMode being still set and // EndDraggingIfNecessary() resets it. - DoGridCellLeftUp(event, coords); + DoGridCellLeftUp(event, coords, gridWindow); EndDraggingIfNecessary(); return; @@ -4356,7 +4529,16 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) if ( m_isDragging ) { if ( isDraggingWithLeft ) - DoGridDragEvent(event, coords, false /* not first drag */); + DoGridDragEvent(event, coords, false /* not first drag */, gridWindow); + + if ( m_winCapture != gridWindow ) + { + if ( m_winCapture ) + m_winCapture->ReleaseMouse(); + + m_winCapture = gridWindow; + m_winCapture->CaptureMouse(); + } return; } @@ -4377,11 +4559,11 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY ) return; - if ( DoGridDragEvent(event, coords, true /* first drag */) ) + if ( DoGridDragEvent(event, coords, true /* first drag */, gridWindow) ) { wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" ); - m_winCapture = m_gridWin; + m_winCapture = gridWindow; m_winCapture->CaptureMouse(); m_isDragging = true; @@ -4413,7 +4595,7 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) } else if ( event.Moving() ) { - DoGridMouseMoveEvent(event, coords, pos); + DoGridMouseMoveEvent(event, coords, pos, gridWindow); } else // unknown mouse event? { @@ -4422,31 +4604,40 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) } // this function returns true only if the size really changed -bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) +bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper, wxGridWindow *gridWindow) { if ( m_dragLastPos == -1 ) return false; 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); + const wxSize size = gridWindow->GetClientSize(); + wxPoint offset = GetGridWindowOffset(gridWindow); + const wxPoint ptOrigin = CalcGridWindowUnscrolledPosition(offset, gridWindow); const int posLineStart = oper.Select(ptOrigin); const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size); - oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos); + // erase the last line we drew + wxGridWindow* endDragGridWindows[2] = { NULL, NULL }; + GetDragGridWindows(m_dragLastPos, oper, endDragGridWindows[0], endDragGridWindows[1]); + + DoGridLineDrag(m_dragLastPos, oper, endDragGridWindows[0]); + DoGridLineDrag(m_dragLastPos, oper, endDragGridWindows[1]); // temporarily hide the edit control before resizing HideCellEditControl(); SaveEditControlValue(); + // increase the line size based on device, not logical position for frozen lines, + // so that it won't increase in size more that the window when scrolled + if ( m_dragRowOrCol < oper.Select(wxPoint(m_numFrozenRows, m_numFrozenCols)) ) + { + wxPoint dragLastPoint(m_dragLastPos, m_dragLastPos); + dragLastPoint = CalcGridWindowScrolledPosition(dragLastPoint, gridWindow); + + m_dragLastPos = doper.Select(dragLastPoint); + } + // do resize the line const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol); const int lineSizeOld = oper.GetLineSize(this, m_dragRowOrCol); @@ -4488,13 +4679,13 @@ bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) oper.SelectSize(rect) = oper.Select(size); int subtractLines = 0; - int line = doper.PosToLine(this, posLineStart); + int line = doper.PosToLine(this, posLineStart, NULL); if ( line >= 0 ) { // ensure that if we have a multi-cell block we redraw all of // it by increasing the refresh area to cover it entirely if a // part of it is affected - const int lineEnd = doper.PosToLine(this, posLineEnd, true); + const int lineEnd = doper.PosToLine(this, posLineEnd, NULL, true); for ( ; line < lineEnd; line++ ) { int cellLines = oper.Select( @@ -4511,7 +4702,7 @@ bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) doper.Select(rect) = startPos; doper.SelectSize(rect) = doper.Select(size) - startPos; - m_gridWin->Refresh(false, &rect); + Refresh(false, &rect); } } @@ -4521,19 +4712,19 @@ bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper) return sizeChanged; } -void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event) +void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event, wxGridWindow* gridWindow) { // TODO: generate RESIZING event, see #10754 - if ( DoEndDragResizeLine(wxGridRowOperations()) ) + if ( DoEndDragResizeLine(wxGridRowOperations(), gridWindow) ) SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event); } -void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event) +void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event, wxGridWindow* gridWindow) { // TODO: generate RESIZING event, see #10754 - if ( DoEndDragResizeLine(wxGridColumnOperations()) ) + if ( DoEndDragResizeLine(wxGridColumnOperations(), gridWindow) ) SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event); } @@ -4615,15 +4806,21 @@ void wxGrid::ResetColPos() RefreshAfterColPosChange(); } -void wxGrid::EnableDragColMove( bool enable ) +bool wxGrid::EnableDragColMove( bool enable ) { - if ( m_canDragColMove == enable ) - return; + if ( m_canDragColMove == enable || + (enable && m_colFrozenLabelWin) ) + return false; if ( m_useNativeHeader ) { - // update all columns to make them [not] reorderable - GetGridColHeader()->SetColumnCount(m_numCols); + wxHeaderCtrl *header = GetGridColHeader(); + long setFlags = header->GetWindowStyleFlag(); + + if ( enable ) + header->SetWindowStyleFlag(setFlags | wxHD_ALLOW_REORDER); + else + header->SetWindowStyleFlag(setFlags & ~wxHD_ALLOW_REORDER); } m_canDragColMove = enable; @@ -4632,11 +4829,149 @@ void wxGrid::EnableDragColMove( bool enable ) // 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 + return true; +} + +void wxGrid::InitializeFrozenWindows() +{ + // frozen row windows + if ( m_numFrozenRows > 0 && !m_frozenRowGridWin ) + { + m_frozenRowGridWin = new wxGridWindow(this, wxGridWindow::wxGridWindowFrozenRow); + m_rowFrozenLabelWin = new wxGridRowFrozenLabelWindow(this); + + m_frozenRowGridWin->SetOwnForegroundColour(m_gridWin->GetForegroundColour()); + m_frozenRowGridWin->SetOwnBackgroundColour(m_gridWin->GetBackgroundColour()); + m_rowFrozenLabelWin->SetOwnForegroundColour(m_labelTextColour); + m_rowFrozenLabelWin->SetOwnBackgroundColour(m_labelBackgroundColour); + } + else if ( m_numFrozenRows == 0 && m_frozenRowGridWin ) + { + delete m_frozenRowGridWin; + delete m_rowFrozenLabelWin; + m_frozenRowGridWin = NULL; + m_rowFrozenLabelWin = NULL; + } + + // frozen column windows + if ( m_numFrozenCols > 0 && !m_frozenColGridWin ) + { + m_frozenColGridWin = new wxGridWindow(this, wxGridWindow::wxGridWindowFrozenCol); + m_colFrozenLabelWin = new wxGridColFrozenLabelWindow(this); + + m_frozenColGridWin->SetOwnForegroundColour(m_gridWin->GetForegroundColour()); + m_frozenColGridWin->SetOwnBackgroundColour(m_gridWin->GetBackgroundColour()); + m_colFrozenLabelWin->SetOwnForegroundColour(m_labelTextColour); + m_colFrozenLabelWin->SetOwnBackgroundColour(m_labelBackgroundColour); + } + else if ( m_numFrozenCols == 0 && m_frozenColGridWin ) + { + delete m_frozenColGridWin; + delete m_colFrozenLabelWin; + m_frozenColGridWin = NULL; + m_colFrozenLabelWin = NULL; + } + + // frozen corner window + if ( m_numFrozenRows > 0 && m_numFrozenCols > 0 && !m_frozenCornerGridWin ) + { + m_frozenCornerGridWin = new wxGridWindow(this, wxGridWindow::wxGridWindowFrozenCorner); + + m_frozenCornerGridWin->SetOwnForegroundColour(m_gridWin->GetForegroundColour()); + m_frozenCornerGridWin->SetOwnBackgroundColour(m_gridWin->GetBackgroundColour()); + } + else if ((m_numFrozenRows == 0 || m_numFrozenCols == 0) && m_frozenCornerGridWin) + { + delete m_frozenCornerGridWin; + m_frozenCornerGridWin = NULL; + } +} + +bool wxGrid::FreezeTo(int row, int col) +{ + wxCHECK_MSG( row >= 0 && col >= 0, false, + "Number of rows or cols can't be negative!"); + + if ( row >= m_numRows || col >= m_numCols || + !m_colAt.empty() || m_canDragColMove || m_useNativeHeader ) + return false; + + // freeze + if ( row > m_numFrozenRows || col > m_numFrozenCols ) + { + // check that it fits in client size + int cw, ch; + GetClientSize( &cw, &ch ); + + cw -= m_rowLabelWidth; + ch -= m_colLabelHeight; + + if ((row > 0 && m_rowBottoms[row - 1] >= ch) || + (col > 0 && m_colRights[col - 1] >= cw)) + return false; + + // check all involved cells for merged ones + int cell_rows, cell_cols; + + for ( int i = m_numFrozenRows; i < row; i++ ) + { + for ( int j = 0; j < m_numCols; j++ ) + { + GetCellSize(i, GetColAt(j), &cell_rows, &cell_cols ); + + if (( cell_rows > 1 ) || ( cell_cols > 1 )) + return false; + } + } + + for ( int i = m_numFrozenCols; i < col; i++ ) + { + for ( int j = 0; j < m_numRows; j++ ) + { + GetCellSize(j, GetColAt(i), &cell_rows, &cell_cols ); + + if (( cell_rows > 1 ) || ( cell_cols > 1 )) + return false; + } + } + } + + m_numFrozenRows = row; + m_numFrozenCols = col; + + HideCellEditControl(); + + InitializeFrozenWindows(); + + // recompute dimensions + InvalidateBestSize(); + + if ( !GetBatchCount() ) + { + CalcDimensions(); + Refresh(); + } + + return true; +} + +bool wxGrid::IsFrozen() const +{ + return m_numFrozenRows || m_numFrozenCols; } void wxGrid::UpdateGridWindows() const { m_gridWin->Update(); + + if ( m_frozenCornerGridWin ) + m_frozenCornerGridWin->Update(); + + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->Update(); + + if ( m_frozenColGridWin ) + m_frozenColGridWin->Update(); } // @@ -4914,6 +5249,19 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) m_colLabelWin->Refresh(eraseb, NULL); m_rowLabelWin->Refresh(eraseb, NULL); m_gridWin->Refresh(eraseb, NULL); + + if ( m_frozenColGridWin ) + { + m_frozenColGridWin->Refresh(eraseb, NULL); + m_colFrozenLabelWin->Refresh(eraseb, NULL); + } + if ( m_frozenRowGridWin ) + { + m_frozenRowGridWin->Refresh(eraseb, NULL); + m_rowFrozenLabelWin->Refresh(eraseb, NULL); + } + if ( m_frozenCornerGridWin ) + m_frozenCornerGridWin->Refresh(eraseb, NULL); } } } @@ -4928,10 +5276,55 @@ void wxGrid::RefreshBlock(const wxGridCellCoords& topLeft, void wxGrid::RefreshBlock(int topRow, int leftCol, int bottomRow, int rightCol) { - const wxRect rect = BlockToDeviceRect(wxGridCellCoords(topRow, leftCol), - wxGridCellCoords(bottomRow, rightCol)); - if ( !rect.IsEmpty() ) - m_gridWin->Refresh(false, &rect); + int row = topRow; + int col = leftCol; + + // corner grid + if ( topRow < m_numFrozenRows && GetColPos(leftCol) < m_numFrozenCols && m_frozenCornerGridWin ) + { + row = wxMin(bottomRow, m_numFrozenRows - 1); + col = wxMin(rightCol, m_numFrozenCols - 1); + + wxRect rect = BlockToDeviceRect(wxGridCellCoords(topRow, leftCol), + wxGridCellCoords(row, col), + m_frozenCornerGridWin); + m_frozenCornerGridWin->Refresh(false, &rect); + row++; col++; + } + + // frozen cols grid + if ( GetColPos(leftCol) < m_numFrozenCols && bottomRow >= m_numFrozenRows && m_frozenColGridWin ) + { + col = wxMin(rightCol, m_numFrozenCols - 1); + + wxRect rect = BlockToDeviceRect(wxGridCellCoords(row, leftCol), + wxGridCellCoords(bottomRow, col), + m_frozenColGridWin); + m_frozenColGridWin->Refresh(false, &rect); + col++; + } + + // frozen rows grid + if ( topRow < m_numFrozenRows && GetColPos(rightCol) >= m_numFrozenCols && m_frozenRowGridWin ) + { + row = wxMin(bottomRow, m_numFrozenRows - 1); + + wxRect rect = BlockToDeviceRect(wxGridCellCoords(topRow, col), + wxGridCellCoords(row, rightCol), + m_frozenRowGridWin); + m_frozenRowGridWin->Refresh(false, &rect); + row++; + } + + // main grid + if ( bottomRow >= m_numFrozenRows && GetColPos(rightCol) >= m_numFrozenCols ) + { + const wxRect rect = BlockToDeviceRect(wxGridCellCoords(row, col), + wxGridCellCoords(bottomRow, rightCol), + m_gridWin); + if ( !rect.IsEmpty() ) + m_gridWin->Refresh(false, &rect); + } } void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event)) @@ -5239,19 +5632,18 @@ bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) return false; } -#if !defined(__WXMAC__) - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); -#endif + wxGridWindow *currentGridWindow = CellToGridWindow(coords); if ( m_currentCellCoords != wxGridNoCellCoords ) { + wxGridWindow *prevGridWindow = CellToGridWindow(m_currentCellCoords); + DisableCellEditControl(); if ( IsVisible( m_currentCellCoords, false ) ) { wxRect r; - r = BlockToDeviceRect( m_currentCellCoords, m_currentCellCoords ); + r = BlockToDeviceRect( m_currentCellCoords, m_currentCellCoords, prevGridWindow ); if ( !m_gridLinesEnabled ) { r.x--; @@ -5260,16 +5652,25 @@ bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) r.height++; } - wxGridCellCoordsArray cells = CalcCellsExposed( r ); + wxGridCellCoordsArray cells = CalcCellsExposed( r, prevGridWindow ); // Otherwise refresh redraws the highlight! m_currentCellCoords = coords; #if defined(__WXMAC__) - m_gridWin->Refresh(true /*, & r */); + currentGridWindow->Refresh(true /*, & r */); + + if ( prevGridWindow != currentGridWindow ) + prevGridWindow->Refresh(true); #else - DrawGridCellArea( dc, cells ); - DrawAllGridLines( dc, r ); + wxClientDC prevDc( prevGridWindow ); + PrepareDCFor(prevDc, prevGridWindow); + + DrawGridCellArea( prevDc, cells ); + DrawAllGridWindowLines( prevDc, r , prevGridWindow); + + if ( prevGridWindow->GetType() != wxGridWindow::wxGridWindowNormal ) + DrawFrozenBorder(prevDc, prevGridWindow); #endif } } @@ -5278,6 +5679,8 @@ bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) wxGridCellAttr *attr = GetCellAttr( coords ); #if !defined(__WXMAC__) + wxClientDC dc( currentGridWindow ); + PrepareDCFor(dc, currentGridWindow); DrawCellHighlight( dc, attr ); #endif attr->DecRef(); @@ -5519,13 +5922,14 @@ void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells ) } } -void wxGrid::DrawGridSpace( wxDC& dc ) +void wxGrid::DrawGridSpace( wxDC& dc, wxGridWindow *gridWindow ) { int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + gridWindow->GetClientSize( &cw, &ch ); int right, bottom; - CalcUnscrolledPosition( cw, ch, &right, &bottom ); + wxPoint offset = GetGridWindowOffset(gridWindow); + CalcGridWindowUnscrolledPosition(cw + offset.x, ch + offset.y, &right, &bottom, gridWindow); int rightCol = m_numCols > 0 ? GetColRight(GetColAt( m_numCols - 1 )) : 0; int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0; @@ -5533,7 +5937,7 @@ void wxGrid::DrawGridSpace( wxDC& dc ) if ( right > rightCol || bottom > bottomRow ) { int left, top; - CalcUnscrolledPosition( 0, 0, &left, &top ); + CalcGridWindowUnscrolledPosition(offset.x, offset.y, &left, &top, gridWindow); dc.SetBrush(GetDefaultCellBackgroundColour()); dc.SetPen( *wxTRANSPARENT_PEN ); @@ -5723,6 +6127,54 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells) } } +void wxGrid::DrawFrozenBorder(wxDC& dc, wxGridWindow *gridWindow) +{ + if ( gridWindow && m_numCols && m_numRows) + { + int top, bottom, left, right; + int cw, ch; + wxPoint gridOffset = GetGridWindowOffset(gridWindow); + gridWindow->GetClientSize(&cw, &ch); + CalcGridWindowUnscrolledPosition( gridOffset.x, gridOffset.y, &left, &top, gridWindow ); + CalcGridWindowUnscrolledPosition( cw + gridOffset.x, ch + gridOffset.y, &right, &bottom, gridWindow ); + + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow ) + { + right = wxMin(right, GetColRight(m_numCols - 1)); + + dc.SetPen(wxPen(m_gridFrozenBorderColour, + m_gridFrozenBorderPenWidth)); + dc.DrawLine(left, bottom, right, bottom); + } + + if ( gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol ) + { + bottom = wxMin(bottom, GetRowBottom(m_numRows - 1)); + + dc.SetPen(wxPen(m_gridFrozenBorderColour, + m_gridFrozenBorderPenWidth)); + dc.DrawLine(right, top, right, bottom); + } + } +} + +void wxGrid::DrawLabelFrozenBorder(wxDC& dc, wxWindow *window, bool isRow) +{ + if ( window ) + { + int cw, ch; + window->GetClientSize(&cw, &ch); + + dc.SetPen(wxPen(m_gridFrozenBorderColour, + m_gridFrozenBorderPenWidth)); + + if ( isRow ) + dc.DrawLine(0, ch, cw, ch); + else + dc.DrawLine(cw, 0, cw, ch); + } +} + // Used by wxGrid::Render() to draw the grid lines only for the cells in the // specified range. void @@ -5787,17 +6239,19 @@ wxGrid::DrawRangeGridLines(wxDC& dc, // This is used to redraw all grid lines e.g. when the grid line colour // has been changed // -void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) +void wxGrid::DrawAllGridWindowLines(wxDC& dc, const wxRegion & WXUNUSED(reg), wxGridWindow *gridWindow) { - if ( !m_gridLinesEnabled ) + if ( !m_gridLinesEnabled || !gridWindow ) return; int top, bottom, left, right; + wxPoint gridOffset = GetGridWindowOffset(gridWindow); + int cw, ch; - m_gridWin->GetClientSize(&cw, &ch); - CalcUnscrolledPosition( 0, 0, &left, &top ); - CalcUnscrolledPosition( cw, ch, &right, &bottom ); + gridWindow->GetClientSize(&cw, &ch); + CalcGridWindowUnscrolledPosition( gridOffset.x, gridOffset.y, &left, &top, gridWindow ); + CalcGridWindowUnscrolledPosition( cw + gridOffset.x, ch + gridOffset.y, &right, &bottom, gridWindow ); // avoid drawing grid lines past the last row and col if ( m_gridLinesClipHorz ) @@ -5821,39 +6275,44 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) } // no gridlines inside multicells, clip them out - int leftCol = GetColPos( internalXToCol(left) ); - int topRow = internalYToRow(top); - int rightCol = GetColPos( internalXToCol(right) ); - int bottomRow = internalYToRow(bottom); + int leftCol = GetColPos( internalXToCol(left, gridWindow) ); + int topRow = internalYToRow(top, gridWindow); + int rightCol = GetColPos( internalXToCol(right, gridWindow) ); + int bottomRow = internalYToRow(bottom, gridWindow); - wxRegion clippedcells(0, 0, cw, ch); - - int cell_rows, cell_cols; - wxRect rect; - - for ( int j = topRow; j <= bottomRow; j++ ) + if ( gridWindow == m_gridWin ) { - for ( int colPos = leftCol; colPos <= rightCol; colPos++ ) - { - int i = GetColAt( colPos ); + wxRegion clippedcells(0, 0, cw, ch); - GetCellSize( j, i, &cell_rows, &cell_cols ); - if ((cell_rows > 1) || (cell_cols > 1)) + int cell_rows, cell_cols; + wxRect rect; + + for ( int j = topRow; j <= bottomRow; j++ ) + { + for ( int colPos = leftCol; colPos <= rightCol; colPos++ ) { - rect = CellToRect(j,i); - CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); - clippedcells.Subtract(rect); - } - else if ((cell_rows < 0) || (cell_cols < 0)) - { - rect = CellToRect(j + cell_rows, i + cell_cols); - CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); - clippedcells.Subtract(rect); + int i = GetColAt( colPos ); + + GetCellSize( j, i, &cell_rows, &cell_cols ); + if ((cell_rows > 1) || (cell_cols > 1)) + { + rect = CellToRect(j,i); + rect.Offset(-gridOffset); + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + clippedcells.Subtract(rect); + } + else if ((cell_rows < 0) || (cell_cols < 0)) + { + rect = CellToRect(j + cell_rows, i + cell_cols); + rect.Offset(-gridOffset); + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + clippedcells.Subtract(rect); + } } } - } - dc.SetDeviceClippingRegion( clippedcells ); + dc.SetDeviceClippingRegion( clippedcells ); + } DoDrawGridLines(dc, top, left, bottom, right, @@ -5862,6 +6321,41 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) dc.DestroyClippingRegion(); } +void wxGrid::DrawAllGridLines() +{ + if ( m_gridWin ) + { + wxClientDC dc(m_gridWin); + PrepareDCFor(dc, m_gridWin); + + DrawAllGridWindowLines(dc, wxRegion(), m_gridWin); + } + + if ( m_frozenRowGridWin ) + { + wxClientDC dc(m_frozenRowGridWin); + PrepareDCFor(dc, m_frozenRowGridWin); + + DrawAllGridWindowLines(dc, wxRegion(), m_frozenRowGridWin); + } + + if ( m_frozenColGridWin ) + { + wxClientDC dc(m_frozenColGridWin); + PrepareDCFor(dc, m_frozenColGridWin); + + DrawAllGridWindowLines(dc, wxRegion(), m_frozenColGridWin); + } + + if ( m_frozenCornerGridWin ) + { + wxClientDC dc(m_frozenCornerGridWin); + PrepareDCFor(dc, m_frozenCornerGridWin); + + DrawAllGridWindowLines(dc, wxRegion(), m_frozenCornerGridWin); + } +} + void wxGrid::DoDrawGridLines(wxDC& dc, int top, int left, @@ -5944,10 +6438,14 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) rect, hAlign, vAlign, wxHORIZONTAL); } -void wxGrid::UseNativeColHeader(bool native) +bool wxGrid::UseNativeColHeader(bool native) { if ( native == m_useNativeHeader ) - return; + return true; + + // Using native control doesn't work if any columns are frozen currently. + if ( native && m_numFrozenCols ) + return false; delete m_colLabelWin; m_useNativeHeader = native; @@ -5958,6 +6456,8 @@ void wxGrid::UseNativeColHeader(bool native) SetNativeHeaderColCount(); CalcWindowSizes(); + + return true; } void wxGrid::SetUseNativeColLabels( bool native ) @@ -6265,10 +6765,7 @@ void wxGrid::EndBatch() if ( !m_batchCount ) { CalcDimensions(); - m_rowLabelWin->Refresh(); - m_colLabelWin->Refresh(); - m_cornerLabelWin->Refresh(); - m_gridWin->Refresh(); + Refresh(); } } } @@ -6405,6 +6902,8 @@ void wxGrid::ShowCellEditControl() int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); + wxGridWindow *gridWindow = CellToGridWindow(row, col); + // if this is part of a multicell, find owner (topleft) int cell_rows, cell_cols; GetCellSize( row, col, &cell_rows, &cell_cols ); @@ -6418,15 +6917,17 @@ void wxGrid::ShowCellEditControl() // erase the highlight and the cell contents because the editor // might not cover the entire cell - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); + wxClientDC dc( gridWindow ); + PrepareDCFor(dc, gridWindow); wxGridCellAttr* attr = GetCellAttr(row, col); dc.SetBrush(wxBrush(attr->GetBackgroundColour())); dc.SetPen(*wxTRANSPARENT_PEN); dc.DrawRectangle(rect); + rect.Offset(-GetGridWindowOffset(gridWindow)); + // convert to scrolled coords - CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + CalcGridWindowScrolledPosition( rect.x, rect.y, &rect.x, &rect.y, gridWindow ); int nXMove = 0; if (rect.x < 0) @@ -6450,7 +6951,7 @@ void wxGrid::ShowCellEditControl() wxGridCellEditor* editor = attr->GetEditor(this, row, col); if ( !editor->IsCreated() ) { - editor->Create(m_gridWin, wxID_ANY, + editor->Create(gridWindow, wxID_ANY, new wxGridCellEditorEvtHandler(this, editor)); wxGridEditorCreatedEvent evt(GetId(), @@ -6461,6 +6962,11 @@ void wxGrid::ShowCellEditControl() editor->GetControl()); GetEventHandler()->ProcessEvent(evt); } + else if ( editor->GetControl() && + editor->GetControl()->GetParent() != gridWindow ) + { + editor->GetControl()->Reparent(gridWindow); + } // resize editor to overflow into righthand cells if allowed int maxWidth = rect.width; @@ -6473,7 +6979,7 @@ void wxGrid::ShowCellEditControl() maxWidth = rect.width; } - int client_right = m_gridWin->GetClientSize().GetWidth(); + int client_right = gridWindow->GetClientSize().GetWidth(); if (rect.x + maxWidth > client_right) maxWidth = client_right - rect.x; @@ -6531,29 +7037,49 @@ void wxGrid::HideCellEditControl() wxGridCellAttr *attr = GetCellAttr(row, col); wxGridCellEditor *editor = attr->GetEditor(this, row, col); const bool editorHadFocus = editor->GetControl()->HasFocus(); + + if ( editor->GetControl()->GetParent() != m_gridWin ) + editor->GetControl()->Reparent(m_gridWin); + editor->Show( false ); editor->DecRef(); attr->DecRef(); + wxGridWindow *gridWindow = CellToGridWindow(row, col); // return the focus to the grid itself if the editor had it // // note that we must not do this unconditionally to avoid stealing // focus from the window which just received it if we are hiding the // editor precisely because we lost focus if ( editorHadFocus ) - m_gridWin->SetFocus(); + gridWindow->SetFocus(); // refresh whole row to the right wxRect rect( CellToRect(row, col) ); - CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y ); - rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x; + rect.Offset( -GetGridWindowOffset(gridWindow) ); + CalcGridWindowScrolledPosition(rect.x, rect.y, &rect.x, &rect.y, gridWindow); + rect.width = gridWindow->GetClientSize().GetWidth() - rect.x; #ifdef __WXMAC__ // ensure that the pixels under the focus ring get refreshed as well rect.Inflate(10, 10); #endif - m_gridWin->Refresh( false, &rect ); + gridWindow->Refresh( false, &rect ); + + // refresh also the grid to the right + wxGridWindow *rightGridWindow = NULL; + if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCorner ) + rightGridWindow = m_frozenRowGridWin; + else if ( gridWindow->GetType() == wxGridWindow::wxGridWindowFrozenCol ) + rightGridWindow = m_gridWin; + + if ( rightGridWindow ) + { + rect.x = 0; + rect.width = rightGridWindow->GetClientSize().GetWidth(); + rightGridWindow->Refresh( false, &rect ); + } } } @@ -6603,10 +7129,10 @@ void wxGrid::OnHideEditor(wxCommandEvent& WXUNUSED(event)) // coordinates for mouse events etc. // -wxGridCellCoords wxGrid::XYToCell(int x, int y) const +wxGridCellCoords wxGrid::XYToCell(int x, int y, wxGridWindow *gridWindow) const { - int row = YToRow(y); - int col = XToCol(x); + int row = YToRow(y, false, gridWindow); + int col = XToCol(x, false, gridWindow); return row == -1 || col == -1 ? wxGridNoCellCoords : wxGridCellCoords(row, col); @@ -6618,9 +7144,10 @@ wxGridCellCoords wxGrid::XYToCell(int x, int y) const // NOTE: This may not work correctly for reordered columns. int wxGrid::PosToLinePos(int coord, bool clipToMinMax, - const wxGridOperations& oper) const + const wxGridOperations& oper, + wxGridWindow *gridWindow) const { - const int numLines = oper.GetNumberOfLines(this); + const int numLines = oper.GetNumberOfLines(this, gridWindow); if ( coord < 0 ) return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND; @@ -6628,37 +7155,37 @@ int wxGrid::PosToLinePos(int coord, const int defaultLineSize = oper.GetDefaultLineSize(this); wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" ); - int maxPos = coord / defaultLineSize, - minPos = 0; + int maxPos = coord / defaultLineSize; + int minPos = oper.GetFirstLine(this, gridWindow); // 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 ( maxPos < numLines ) + if ( maxPos < (numLines + minPos) ) return maxPos; - return clipToMinMax ? numLines - 1 : -1; + return clipToMinMax ? numLines + minPos - 1 : -1; } - // binary search is quite efficient and we can't really make any assumptions // on where to start here since row and columns could be of size 0 if they are // hidden. While this could be made more efficient, some profiling will be // necessary to determine if it really is a performance bottleneck - maxPos = numLines - 1; + maxPos = numLines + minPos - 1; // check if the position is beyond the last column const int lineAtMaxPos = oper.GetLineAt(this, maxPos); if ( coord >= lineEnds[lineAtMaxPos] ) - return clipToMinMax ? maxPos : -1; + return clipToMinMax ? maxPos : wxNOT_FOUND; // or before the first one - const int lineAt0 = oper.GetLineAt(this, 0); - if ( coord < lineEnds[lineAt0] ) - return 0; - + const int lineAt0 = oper.GetLineAt(this, minPos); + if ( coord < oper.GetLineStartPos(this, lineAt0) ) + return clipToMinMax ? minPos : wxNOT_FOUND; + else if ( coord < lineEnds[lineAt0] ) + return minPos; // finally do perform the binary search while ( minPos < maxPos ) @@ -6686,26 +7213,27 @@ int wxGrid::PosToLinePos(int coord, int wxGrid::PosToLine(int coord, bool clipToMinMax, - const wxGridOperations& oper) const + const wxGridOperations& oper, + wxGridWindow *gridWindow) const { - int pos = PosToLinePos(coord, clipToMinMax, oper); + int pos = PosToLinePos(coord, clipToMinMax, oper, gridWindow); return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos); } -int wxGrid::YToRow(int y, bool clipToMinMax) const +int wxGrid::YToRow(int y, bool clipToMinMax, wxGridWindow *gridWindow) const { - return PosToLine(y, clipToMinMax, wxGridRowOperations()); + return PosToLine(y, clipToMinMax, wxGridRowOperations(), gridWindow); } -int wxGrid::XToCol(int x, bool clipToMinMax) const +int wxGrid::XToCol(int x, bool clipToMinMax, wxGridWindow *gridWindow) const { - return PosToLine(x, clipToMinMax, wxGridColumnOperations()); + return PosToLine(x, clipToMinMax, wxGridColumnOperations(), gridWindow); } -int wxGrid::XToPos(int x) const +int wxGrid::XToPos(int x, wxGridWindow *gridWindow) const { - return PosToLinePos(x, true /* clip */, wxGridColumnOperations()); + return PosToLinePos(x, true /* clip */, wxGridColumnOperations(), gridWindow); } // return the row/col number such that the pos is near the edge of, or -1 if @@ -6718,7 +7246,7 @@ int wxGrid::XToPos(int x) const int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const { // Get the bottom or rightmost line that could match. - int line = oper.PosToLine(this, pos, true); + int line = oper.PosToLine(this, pos, NULL, true); if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE ) { @@ -6793,21 +7321,129 @@ wxRect wxGrid::CellToRect( int row, int col ) const return rect; } +wxGridWindow* wxGrid::CellToGridWindow( int row, int col ) const +{ + if ( row < m_numFrozenRows && GetColPos(col) < m_numFrozenCols ) + return m_frozenCornerGridWin; + else if ( row < m_numFrozenRows ) + return m_frozenRowGridWin; + else if ( GetColPos(col) < m_numFrozenCols ) + return m_frozenColGridWin; + + return m_gridWin; +} + +void wxGrid::GetGridWindowOffset(const wxGridWindow *gridWindow, int &x, int &y) const +{ + wxPoint pt = GetGridWindowOffset(gridWindow); + + x = pt.x; + y = pt.y; +} + +wxPoint wxGrid::GetGridWindowOffset(const wxGridWindow *gridWindow) const +{ + wxPoint pt(0, 0); + + if ( gridWindow ) + { + if ( m_frozenRowGridWin && + (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow) == 0 ) + { + pt.y = m_frozenRowGridWin->GetClientSize().y; + } + + if ( m_frozenColGridWin && + (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol) == 0 ) + { + pt.x = m_frozenColGridWin->GetClientSize().x; + } + } + + return pt; +} + +wxGridWindow* wxGrid::DevicePosToGridWindow(wxPoint pos) const +{ + return DevicePosToGridWindow(pos.x, pos.y); +} + +wxGridWindow* wxGrid::DevicePosToGridWindow(int x, int y) const +{ + if ( m_gridWin->GetRect().Contains(x, y) ) + return m_gridWin; + else if ( m_frozenCornerGridWin && m_frozenCornerGridWin->GetRect().Contains(x, y) ) + return m_frozenCornerGridWin; + else if ( m_frozenRowGridWin && m_frozenRowGridWin->GetRect().Contains(x, y) ) + return m_frozenRowGridWin; + else if ( m_frozenColGridWin && m_frozenColGridWin->GetRect().Contains(x, y) ) + return m_frozenColGridWin; + + return NULL; +} + +void wxGrid::CalcGridWindowUnscrolledPosition(int x, int y, int *xx, int *yy, + const wxGridWindow *gridWindow) const +{ + CalcUnscrolledPosition(x, y, xx, yy); + + if ( gridWindow ) + { + if ( yy && (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow) ) + *yy = y; + if ( xx && (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol) ) + *xx = x; + } +} + +wxPoint wxGrid::CalcGridWindowUnscrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const +{ + wxPoint pt2; + CalcGridWindowUnscrolledPosition(pt.x, pt.y, &pt2.x, &pt2.y, gridWindow); + return pt2; +} + +void wxGrid::CalcGridWindowScrolledPosition(int x, int y, int *xx, int *yy, + const wxGridWindow *gridWindow) const +{ + CalcScrolledPosition(x, y, xx, yy); + + if ( gridWindow ) + { + if ( yy && (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenRow) ) + *yy = y; + if ( xx && (gridWindow->GetType() & wxGridWindow::wxGridWindowFrozenCol) ) + *xx = x; + } +} + +wxPoint wxGrid::CalcGridWindowScrolledPosition(const wxPoint& pt, + const wxGridWindow *gridWindow) const +{ + wxPoint pt2; + CalcGridWindowScrolledPosition(pt.x, pt.y, &pt2.x, &pt2.y, gridWindow); + return pt2; +} + bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const { // get the cell rectangle in logical coords // wxRect r( CellToRect( row, col ) ); + wxGridWindow *gridWindow = CellToGridWindow(row, col); + r.Offset(-GetGridWindowOffset(gridWindow)); + // convert to device coords // int left, top, right, bottom; - CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top ); - CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); + CalcGridWindowScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top, gridWindow ); + CalcGridWindowScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom, gridWindow ); // check against the client area of the grid window int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + gridWindow->GetClientSize( &cw, &ch ); if ( wholeCellVisible ) { @@ -6837,22 +7473,27 @@ void wxGrid::MakeCellVisible( int row, int col ) // get the cell rectangle in logical coords wxRect r( CellToRect( row, col ) ); + wxGridWindow *gridWindow = CellToGridWindow(row, col); + wxPoint gridOffset = GetGridWindowOffset(gridWindow); + // convert to device coords int left, top, right, bottom; - CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top ); - CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom ); + CalcGridWindowScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top, gridWindow ); + CalcGridWindowScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom, gridWindow ); int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + gridWindow->GetClientSize( &cw, &ch ); - if ( top < 0 ) + if ( top < gridOffset.y ) { - ypos = r.GetTop(); + ypos = r.GetTop() - gridOffset.y; } - else if ( bottom > ch ) + else if ( bottom > (ch + gridOffset.y) ) { int h = r.GetHeight(); - ypos = r.GetTop(); + + ypos = r.GetTop() - gridOffset.y; + int i; for ( i = row - 1; i >= 0; i-- ) { @@ -6877,15 +7518,15 @@ void wxGrid::MakeCellVisible( int row, int col ) // Otherwise, e.g. when stepping from row to row, it would jump between // left and right part of the cell on every step! // if ( left < 0 ) - if ( left < 0 || (right - left) >= cw ) + if ( left < gridOffset.x || (right - left) >= cw ) { - xpos = r.GetLeft(); + xpos = r.GetLeft() - gridOffset.x; } - else if ( right > cw ) + else if ( right > (cw + gridOffset.x) ) { // position the view so that the cell is on the right int x0, y0; - CalcUnscrolledPosition(0, 0, &x0, &y0); + CalcGridWindowUnscrolledPosition(0, 0, &x0, &y0, gridWindow); xpos = x0 + (right - cw); // see comment for ypos above @@ -7247,12 +7888,25 @@ void wxGrid::SetLabelBackgroundColour( const wxColour& colour ) m_rowLabelWin->SetBackgroundColour( colour ); m_colLabelWin->SetBackgroundColour( colour ); m_cornerLabelWin->SetBackgroundColour( colour ); + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->SetBackgroundColour( colour ); + if ( m_frozenColGridWin ) + m_frozenColGridWin->SetBackgroundColour( colour ); + if ( m_frozenCornerGridWin ) + m_frozenCornerGridWin->SetBackgroundColour( colour ); if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); m_colLabelWin->Refresh(); m_cornerLabelWin->Refresh(); + + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->Refresh(); + if ( m_frozenColGridWin ) + m_frozenColGridWin->Refresh(); + if ( m_frozenCornerGridWin ) + m_frozenCornerGridWin->Refresh(); } } } @@ -7479,8 +8133,11 @@ void wxGrid::SetCellHighlightColour( const wxColour& colour ) { m_cellHighlightColour = colour; - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); + wxGridWindow *gridWindow = CellToGridWindow(m_currentCellCoords); + + wxClientDC dc( gridWindow ); + PrepareDCFor( dc, gridWindow ); + wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords); DrawCellHighlight(dc, attr); attr->DecRef(); @@ -7501,7 +8158,8 @@ void wxGrid::SetCellHighlightPenWidth(int width) return; wxRect rect = CellToRect(row, col); - m_gridWin->Refresh(true, &rect); + wxGridWindow *gridWindow = CellToGridWindow(row, col); + gridWindow->Refresh(true, &rect); } } @@ -7520,7 +8178,40 @@ void wxGrid::SetCellHighlightROPenWidth(int width) return; wxRect rect = CellToRect(row, col); - m_gridWin->Refresh(true, &rect); + wxGridWindow *gridWindow = CellToGridWindow(row, col); + gridWindow->Refresh(true, &rect); + } +} + +void wxGrid::SetGridFrozenBorderColour(const wxColour &colour) +{ + if ( m_gridFrozenBorderColour != colour ) + { + m_gridFrozenBorderColour = colour; + + if ( !GetBatchCount() ) + { + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->Refresh(); + if ( m_frozenColGridWin ) + m_frozenColGridWin->Refresh(); + } + } +} + +void wxGrid::SetGridFrozenBorderPenWidth(int width) +{ + if ( m_gridFrozenBorderPenWidth != width ) + { + m_gridFrozenBorderPenWidth = width; + + if ( !GetBatchCount() ) + { + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->Refresh(); + if ( m_frozenColGridWin ) + m_frozenColGridWin->Refresh(); + } } } @@ -7532,13 +8223,18 @@ void wxGrid::RedrawGridLines() if ( GridLinesEnabled() ) { - wxClientDC dc( m_gridWin ); - PrepareDC( dc ); - DrawAllGridLines( dc, wxRegion() ); + DrawAllGridLines(); } else // remove the grid lines { m_gridWin->Refresh(); + + if ( m_frozenColGridWin ) + m_frozenColGridWin->Refresh(); + if ( m_frozenRowGridWin ) + m_frozenRowGridWin->Refresh(); + if ( m_frozenCornerGridWin ) + m_frozenCornerGridWin->Refresh(); } } @@ -8990,7 +9686,7 @@ void wxGrid::DeselectLine(int line, const wxGridOperations& oper) } else if ( mode != oper.Dual().GetSelectionMode() ) { - const int nOther = oper.Dual().GetNumberOfLines(this); + const int nOther = oper.Dual().GetNumberOfLines(this, NULL); for ( int i = 0; i < nOther; i++ ) { const wxGridCellCoords c(oper.MakeCoords(line, i)); @@ -9106,7 +9802,8 @@ void wxGrid::ClearSelection() // in device coords clipped to the client size of the grid window. // wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, - const wxGridCellCoords& bottomRight ) const + const wxGridCellCoords& bottomRight, + const wxGridWindow *gridWindow) const { wxRect resultRect; wxRect tempCellRect = CellToRect(topLeft); @@ -9163,61 +9860,68 @@ wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, bottomRow = tmp; } - // The following loop is ONLY necessary to detect and handle merged cells. + if ( !gridWindow ) + gridWindow = m_gridWin; + int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); + gridWindow->GetClientSize( &cw, &ch ); + wxPoint offset = GetGridWindowOffset(gridWindow); - // Get the origin coordinates: notice that they will be negative if the - // grid is scrolled downwards/to the right. - int gridOriginX = 0; - int gridOriginY = 0; - CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY); - - int onScreenLeftmostCol = internalXToCol(-gridOriginX); - int onScreenUppermostRow = internalYToRow(-gridOriginY); - - int onScreenRightmostCol = internalXToCol(-gridOriginX + cw); - int onScreenBottommostRow = internalYToRow(-gridOriginY + ch); - - // Bound our loop so that we only examine the portion of the selected block - // that is shown on screen. Therefore, we compare the Top-Left block values - // to the Top-Left screen values, and the Bottom-Right block values to the - // Bottom-Right screen values, choosing appropriately. - const int visibleTopRow = wxMax(topRow, onScreenUppermostRow); - const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow); - const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol); - const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol); - - for ( int j = visibleTopRow; j <= visibleBottomRow; j++ ) + // The following loop is ONLY necessary to detect and handle merged cells. + if ( gridWindow == m_gridWin ) { - for ( int i = visibleLeftCol; i <= visibleRightCol; i++ ) - { - if ( (j == visibleTopRow) || (j == visibleBottomRow) || - (i == visibleLeftCol) || (i == visibleRightCol) ) - { - tempCellRect = CellToRect( j, i ); + // Get the origin coordinates: notice that they will be negative if the + // grid is scrolled downwards/to the right. + int gridOriginX = offset.x; + int gridOriginY = offset.y; + CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY); - if (tempCellRect.x < left) - left = tempCellRect.x; - if (tempCellRect.y < top) - top = tempCellRect.y; - if (tempCellRect.x + tempCellRect.width > right) - right = tempCellRect.x + tempCellRect.width; - if (tempCellRect.y + tempCellRect.height > bottom) - bottom = tempCellRect.y + tempCellRect.height; - } - else + int onScreenLeftmostCol = internalXToCol(-gridOriginX, m_gridWin); + int onScreenUppermostRow = internalYToRow(-gridOriginY, m_gridWin); + + int onScreenRightmostCol = internalXToCol(-gridOriginX + cw, m_gridWin); + int onScreenBottommostRow = internalYToRow(-gridOriginY + ch, m_gridWin); + + // Bound our loop so that we only examine the portion of the selected block + // that is shown on screen. Therefore, we compare the Top-Left block values + // to the Top-Left screen values, and the Bottom-Right block values to the + // Bottom-Right screen values, choosing appropriately. + const int visibleTopRow = wxMax(topRow, onScreenUppermostRow); + const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow); + const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol); + const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol); + + for ( int j = visibleTopRow; j <= visibleBottomRow; j++ ) + { + for ( int i = visibleLeftCol; i <= visibleRightCol; i++ ) { - i = visibleRightCol; // jump over inner cells. + if ( (j == visibleTopRow) || (j == visibleBottomRow) || + (i == visibleLeftCol) || (i == visibleRightCol) ) + { + tempCellRect = CellToRect( j, i ); + + if (tempCellRect.x < left) + left = tempCellRect.x; + if (tempCellRect.y < top) + top = tempCellRect.y; + if (tempCellRect.x + tempCellRect.width > right) + right = tempCellRect.x + tempCellRect.width; + if (tempCellRect.y + tempCellRect.height > bottom) + bottom = tempCellRect.y + tempCellRect.height; + } + else + { + i = visibleRightCol; // jump over inner cells. + } } } } // Convert to scrolled coords - CalcScrolledPosition( left, top, &left, &top ); - CalcScrolledPosition( right, bottom, &right, &bottom ); + CalcGridWindowScrolledPosition( left - offset.x, top - offset.y, &left, &top, gridWindow ); + CalcGridWindowScrolledPosition( right - offset.x, bottom - offset.y, &right, &bottom, gridWindow ); - if (right < 0 || bottom < 0 || left > cw || top > ch) + if ( right < 0 || bottom < 0 || left > cw || top > ch ) return wxRect(0,0,0,0); resultRect.SetLeft( wxMax(0, left) ); @@ -9233,7 +9937,7 @@ void wxGrid::DoSetSizes(const wxGridSizesInfo& sizeInfo, { BeginBatch(); oper.SetDefaultLineSize(this, sizeInfo.m_sizeDefault, true); - const int numLines = oper.GetNumberOfLines(this); + const int numLines = oper.GetNumberOfLines(this, NULL); for ( int i = 0; i < numLines; i++ ) { int size = sizeInfo.GetSize(i);