From b69b4206c6a9bcae5837662a872872a2d9bddb61 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 10 Jul 2019 11:54:17 +0200 Subject: [PATCH 1/8] Add wxScrollWindow::ShouldScrollToChildOnFocus() virtual hook This method can be overridden to indicate that the scrolled window doesn't want its children to be scrolled into view when they're focused, which is the default behaviour. Also reuse this method for Mac-specific scrollbar workaround. --- include/wx/scrolwin.h | 19 +++++++++++++++++++ interface/wx/scrolwin.h | 13 +++++++++++++ src/generic/scrlwing.cpp | 7 ++++--- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/include/wx/scrolwin.h b/include/wx/scrolwin.h index c7a858feea..bc3c90723c 100644 --- a/include/wx/scrolwin.h +++ b/include/wx/scrolwin.h @@ -14,6 +14,10 @@ #include "wx/control.h" #include "wx/panel.h" +#ifdef __WXOSX__ + #include "wx/scrolbar.h" +#endif + class WXDLLIMPEXP_FWD_CORE wxScrollHelperEvtHandler; class WXDLLIMPEXP_FWD_BASE wxTimer; @@ -305,6 +309,21 @@ protected: return size; } + // Can be overridden to return false if the child window shouldn't be + // scrolled into view automatically when it gets focus, which is the + // default behaviour. + virtual bool ShouldScrollToChildOnFocus(wxWindow* child) + { +#if defined(__WXOSX__) && wxUSE_SCROLLBAR + if ( wxDynamicCast(child, wxScrollBar) ) + return false; +#else + wxUnusedVar(child); +#endif + + return true; + } + double m_scaleX; double m_scaleY; diff --git a/interface/wx/scrolwin.h b/interface/wx/scrolwin.h index fc62e085b6..6e5ca6b7a4 100644 --- a/interface/wx/scrolwin.h +++ b/interface/wx/scrolwin.h @@ -581,6 +581,19 @@ public: */ virtual bool SendAutoScrollEvents(wxScrollWinEvent& event) const; + /** + This method can be overridden in a derived class to prevent scrolling + the child window into view automatically when it gets focus. + + The default behaviour is to scroll this window to show its currently + focused child automatically, to ensure that the user can interact with + it. This is usually helpful, but can be undesirable for some windows, + in which case this method can be overridden to return @false for them + to prevent any scrolling from taking place when such windows get focus. + + @since 3.1.3 + */ + virtual bool ShouldScrollToChildOnFocus(wxWindow* child) protected: /** diff --git a/src/generic/scrlwing.cpp b/src/generic/scrlwing.cpp index a55f832d94..906680689b 100644 --- a/src/generic/scrlwing.cpp +++ b/src/generic/scrlwing.cpp @@ -1070,10 +1070,11 @@ void wxScrollHelperBase::HandleOnChildFocus(wxChildFocusEvent& event) if ( win == m_targetWindow ) return; // nothing to do -#if defined( __WXOSX__ ) && wxUSE_SCROLLBAR - if (wxDynamicCast(win, wxScrollBar)) + if ( !ShouldScrollToChildOnFocus(win) ) + { + // the window does not require to be scrolled into view return; -#endif + } // Fixing ticket: https://trac.wxwidgets.org/ticket/9563 // When a child inside a wxControlContainer receives a focus, the From d37a15444f8d5f64d04b8933d20f69ccfff005fd Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 10 Jul 2019 12:23:10 +0200 Subject: [PATCH 2/8] Add simple wxGrid::GetGridCursorCoords() method This is sometimes more convenient than using GetGridCursor{Row,Col}() separately. --- include/wx/generic/grid.h | 3 +++ interface/wx/grid.h | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 4c28bc355a..8e689dca67 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1179,6 +1179,9 @@ public: wxRect CellToRect( const wxGridCellCoords& coords ) const { return CellToRect( 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(); } diff --git a/interface/wx/grid.h b/interface/wx/grid.h index 093022f57b..2cd8ec92b9 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -3827,13 +3827,28 @@ public: */ //@{ + /** + Returns the current grid cursor position. + + If grid cursor doesn't have any valid position (e.g. if the grid is + completely empty and doesn't have any rows or columns), returns + @c wxGridNoCellCoords which has both row and columns set to @c -1. + + @since 3.1.3 + */ + const wxGridCellCoords& GetGridCursorCoords() const; + /** Returns the current grid cell column position. + + @see GetGridCursorCoords() */ int GetGridCursorCol() const; /** Returns the current grid cell row position. + + @see GetGridCursorCoords() */ int GetGridCursorRow() const; From cf3709147aff2d9e68e455da42903c5a69478eaf Mon Sep 17 00:00:00 2001 From: lucian-rotariu Date: Thu, 11 Jul 2019 01:02:14 +0200 Subject: [PATCH 3/8] Use wxGrid::UpdateGridWindows() helper in wxGridSelection code This function advantageously replaces the ugly casts that were used in wxGridSelection code before. --- include/wx/generic/grid.h | 1 + src/generic/grid.cpp | 4 ++++ src/generic/gridsel.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 8e689dca67..237759c9ce 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1068,6 +1068,7 @@ public: void DrawAllGridLines( wxDC& dc, const wxRegion & reg ); void DrawCell( wxDC& dc, const wxGridCellCoords& ); void DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells); + void UpdateGridWindows() const; // this function is called when the current cell highlight must be redrawn // and may be overridden by the user diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index f39242a39d..c641b0879f 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -4629,6 +4629,10 @@ void wxGrid::EnableDragColMove( bool enable ) // order, whereas now you can always call ResetColPos() manually if needed } +void wxGrid::UpdateGridWindows() const +{ + m_gridWin->Update(); +} // // ------ interaction with data model diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp index 3f0f3f596b..d657772494 100644 --- a/src/generic/gridsel.cpp +++ b/src/generic/gridsel.cpp @@ -915,7 +915,7 @@ void wxGridSelection::ClearSelection() ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); #ifdef __WXMAC__ - ((wxWindow *)m_grid->m_gridWin)->Update(); + m_grid->UpdateGridWindows(); #endif } } @@ -935,7 +935,7 @@ void wxGridSelection::ClearSelection() ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); #ifdef __WXMAC__ - ((wxWindow *)m_grid->m_gridWin)->Update(); + m_grid->UpdateGridWindows(); #endif } } @@ -955,7 +955,7 @@ void wxGridSelection::ClearSelection() ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); #ifdef __WXMAC__ - ((wxWindow *)m_grid->m_gridWin)->Update(); + m_grid->UpdateGridWindows(); #endif } } @@ -976,7 +976,7 @@ void wxGridSelection::ClearSelection() ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); #ifdef __WXMAC__ - ((wxWindow *)m_grid->m_gridWin)->Update(); + m_grid->UpdateGridWindows(); #endif } } From 00224e3f30226c4080c3d1c0f9a14ab53a93a7c4 Mon Sep 17 00:00:00 2001 From: lucian-rotariu Date: Thu, 11 Jul 2019 01:13:54 +0200 Subject: [PATCH 4/8] Add wxGrid::RefreshBlock() helper This simple function combines BlockToDeviceRect() and wxWindow::Refresh() calls and allows to avoid the ugly casts in wxGridSelection code as well as making the code slightly shorter and more clear. No real changes. --- include/wx/generic/grid.h | 5 +++ src/generic/grid.cpp | 64 ++++++++++++++------------------------- src/generic/gridsel.cpp | 49 ++++++++---------------------- 3 files changed, 40 insertions(+), 78 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 237759c9ce..8f23ee19d0 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1112,6 +1112,11 @@ public: const wxArrayString& lines, long *width, long *height ) const; + void RefreshBlock(const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight); + + void RefreshBlock(int topRow, int leftCol, + int bottomRow, int rightCol); // ------ // Code that does a lot of grid modification can be enclosed diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index c641b0879f..bc0c8f6f3d 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -4913,6 +4913,22 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) } } +void wxGrid::RefreshBlock(const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight) +{ + RefreshBlock(topLeft.GetRow(), topLeft.GetCol(), + bottomRight.GetRow(), bottomRight.GetCol()); +} + +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); +} + void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event)) { if (m_targetWindow != this) // check whether initialisation has been done @@ -5317,10 +5333,7 @@ wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, if ( m_selectedBlockTopLeft == wxGridNoCellCoords || m_selectedBlockBottomRight == wxGridNoCellCoords ) { - wxRect rect; - rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ), - wxGridCellCoords ( bottomRow, rightCol ) ); - m_gridWin->Refresh( false, &rect ); + RefreshBlock(topRow, leftCol, bottomRow, rightCol); } // Now handle changing an existing selection area. @@ -5330,13 +5343,6 @@ wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, // Compute two optimal update rectangles: // Either one rectangle is a real subset of the // other, or they are (almost) disjoint! - wxRect rect[4]; - bool need_refresh[4]; - need_refresh[0] = - need_refresh[1] = - need_refresh[2] = - need_refresh[3] = false; - int i; // Store intermediate values wxCoord oldLeft = m_selectedBlockTopLeft.GetCol(); @@ -5358,46 +5364,29 @@ wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol, { // Refresh the newly selected or deselected // area to the left of the old or new selection. - need_refresh[0] = true; - rect[0] = BlockToDeviceRect( - wxGridCellCoords( oldTop, oldLeft ), - wxGridCellCoords( oldBottom, leftCol - 1 ) ); + RefreshBlock(oldTop, oldLeft, oldBottom, leftCol - 1); } if ( oldTop < topRow ) { // Refresh the newly selected or deselected // area above the old or new selection. - need_refresh[1] = true; - rect[1] = BlockToDeviceRect( - wxGridCellCoords( oldTop, leftCol ), - wxGridCellCoords( topRow - 1, rightCol ) ); + RefreshBlock(oldTop, leftCol, topRow - 1, rightCol); } if ( oldRight > rightCol ) { // Refresh the newly selected or deselected // area to the right of the old or new selection. - need_refresh[2] = true; - rect[2] = BlockToDeviceRect( - wxGridCellCoords( oldTop, rightCol + 1 ), - wxGridCellCoords( oldBottom, oldRight ) ); + RefreshBlock(oldTop, rightCol + 1, oldBottom, oldRight); } if ( oldBottom > bottomRow ) { // Refresh the newly selected or deselected // area below the old or new selection. - need_refresh[3] = true; - rect[3] = BlockToDeviceRect( - wxGridCellCoords( bottomRow + 1, leftCol ), - wxGridCellCoords( oldBottom, rightCol ) ); + RefreshBlock(bottomRow + 1, leftCol, oldBottom, rightCol); } - - // various Refresh() calls - for (i = 0; i < 4; i++ ) - if ( need_refresh[i] && rect[i] != wxGridNoCellRect ) - m_gridWin->Refresh( false, &(rect[i]) ); } // change selection @@ -9077,20 +9066,13 @@ wxArrayInt wxGrid::GetSelectedCols() const void wxGrid::ClearSelection() { - wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft, - m_selectedBlockBottomRight); - wxRect r2 = BlockToDeviceRect(m_currentCellCoords, - m_selectedBlockCorner); + RefreshBlock(m_selectedBlockTopLeft, m_selectedBlockBottomRight); + RefreshBlock(m_currentCellCoords, m_selectedBlockCorner); m_selectedBlockTopLeft = m_selectedBlockBottomRight = m_selectedBlockCorner = wxGridNoCellCoords; - if ( !r1.IsEmpty() ) - RefreshRect(r1, false); - if ( !r2.IsEmpty() ) - RefreshRect(r2, false); - if ( m_selection ) m_selection->ClearSelection(); } diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp index d657772494..8497a17af6 100644 --- a/src/generic/gridsel.cpp +++ b/src/generic/gridsel.cpp @@ -259,9 +259,7 @@ void wxGridSelection::SelectRow(int row, const wxKeyboardState& kbd) // Update View: if ( !m_grid->GetBatchCount() ) { - wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( row, 0 ), - wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(row, 0, row, m_grid->GetNumberCols() - 1); } // Send Event @@ -353,9 +351,7 @@ void wxGridSelection::SelectCol(int col, const wxKeyboardState& kbd) // Update View: if ( !m_grid->GetBatchCount() ) { - wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( 0, col ), - wxGridCellCoords( m_grid->GetNumberRows() - 1, col ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(0, col, m_grid->GetNumberRows() - 1, col); } // Send Event @@ -572,9 +568,7 @@ void wxGridSelection::SelectBlock( int topRow, int leftCol, // Update View: if ( !m_grid->GetBatchCount() ) { - wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( topRow, leftCol ), - wxGridCellCoords( bottomRow, rightCol ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(topRow, leftCol, bottomRow, rightCol); } // Send Event, if not disabled. @@ -621,10 +615,7 @@ void wxGridSelection::SelectCell( int row, int col, // Update View: if ( !m_grid->GetBatchCount() ) { - wxRect r = m_grid->BlockToDeviceRect( - selectedTopLeft, - selectedBottomRight ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(selectedTopLeft, selectedBottomRight); } // Send event @@ -674,8 +665,7 @@ wxGridSelection::ToggleCellSelection(int row, int col, m_cellSelection.RemoveAt(n); if ( !m_grid->GetBatchCount() ) { - wxRect r = m_grid->BlockToDeviceRect( coords, coords ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(coords, coords); } // Send event @@ -810,10 +800,7 @@ wxGridSelection::ToggleCellSelection(int row, int col, { if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( - wxGridCellCoords( row, col ), - wxGridCellCoords( row, col ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(row, col, row, col); } wxGridRangeSelectEvent gridEvt( m_grid->GetId(), @@ -839,10 +826,7 @@ wxGridSelection::ToggleCellSelection(int row, int col, { if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( - wxGridCellCoords( row, colFrom ), - wxGridCellCoords( row, colTo-1 ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(row, colFrom, row, colTo - 1); } wxGridRangeSelectEvent gridEvt( m_grid->GetId(), @@ -872,10 +856,7 @@ wxGridSelection::ToggleCellSelection(int row, int col, { if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( - wxGridCellCoords( rowFrom, col ), - wxGridCellCoords( rowTo - 1, col ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(rowFrom, col, rowTo - 1, col); } wxGridRangeSelectEvent gridEvt( m_grid->GetId(), @@ -911,8 +892,7 @@ void wxGridSelection::ClearSelection() m_cellSelection.RemoveAt(n); if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( coords1, coords1 ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(coords1, coords1); #ifdef __WXMAC__ m_grid->UpdateGridWindows(); @@ -931,8 +911,7 @@ void wxGridSelection::ClearSelection() m_blockSelectionBottomRight.RemoveAt(n); if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( coords1, coords2 ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(coords1, coords2); #ifdef __WXMAC__ m_grid->UpdateGridWindows(); @@ -950,9 +929,7 @@ void wxGridSelection::ClearSelection() m_rowSelection.RemoveAt(n); if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( wxGridCellCoords( row, 0 ), - wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(row, 0, row, m_grid->GetNumberCols() - 1); #ifdef __WXMAC__ m_grid->UpdateGridWindows(); @@ -971,9 +948,7 @@ void wxGridSelection::ClearSelection() m_colSelection.RemoveAt(n); if ( !m_grid->GetBatchCount() ) { - r = m_grid->BlockToDeviceRect( wxGridCellCoords( 0, col ), - wxGridCellCoords( m_grid->GetNumberRows() - 1, col ) ); - ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r ); + m_grid->RefreshBlock(0, col, m_grid->GetNumberRows() - 1, col); #ifdef __WXMAC__ m_grid->UpdateGridWindows(); From 3baeb6e83470916ac7414e1d26f8716e65c6de45 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 15 Jul 2019 15:04:50 +0200 Subject: [PATCH 5/8] Rename wxGrid::m_colWindow to m_colLabelWin No real changes, just rename it for consistency with m_rowLabelWin. --- include/wx/generic/grid.h | 8 ++--- src/generic/grid.cpp | 62 +++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 8f23ee19d0..ca5db2a18c 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1670,7 +1670,7 @@ public: // Accessors for component windows wxWindow* GetGridWindow() const { return (wxWindow*)m_gridWin; } wxWindow* GetGridRowLabelWindow() const { return (wxWindow*)m_rowLabelWin; } - wxWindow* GetGridColLabelWindow() const { return m_colWindow; } + wxWindow* GetGridColLabelWindow() const { return m_colLabelWin; } wxWindow* GetGridCornerLabelWindow() const { return (wxWindow*)m_cornerLabelWin; } // This one can only be called if we are using the native header window @@ -1681,7 +1681,7 @@ public: // static_cast<> doesn't work without the full class declaration in // view and we prefer to avoid adding more compile-time dependencies // even at the cost of using reinterpret_cast<> - return reinterpret_cast(m_colWindow); + return reinterpret_cast(m_colLabelWin); } // Allow adjustment of scroll increment. The default is (15, 15). @@ -1908,13 +1908,13 @@ protected: // 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_colWindow; + wxWindow *m_colLabelWin; wxGridColLabelWindow *GetColLabelWindow() const { wxASSERT_MSG( !m_useNativeHeader, "no column label window" ); - return reinterpret_cast(m_colWindow); + return reinterpret_cast(m_colLabelWin); } wxGridTableBase *m_table; diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index bc0c8f6f3d..fcce59670d 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -2322,8 +2322,8 @@ void wxGrid::Create() m_cornerLabelWin->SetOwnBackgroundColour(lbg); m_rowLabelWin->SetOwnForegroundColour(lfg); m_rowLabelWin->SetOwnBackgroundColour(lbg); - m_colWindow->SetOwnForegroundColour(lfg); - m_colWindow->SetOwnBackgroundColour(lbg); + m_colLabelWin->SetOwnForegroundColour(lfg); + m_colLabelWin->SetOwnBackgroundColour(lbg); m_gridWin->SetOwnForegroundColour(gfg); m_gridWin->SetOwnBackgroundColour(gbg); @@ -2346,12 +2346,12 @@ void wxGrid::CreateColumnWindow() { if ( m_useNativeHeader ) { - m_colWindow = new wxGridHeaderCtrl(this); - m_colLabelHeight = m_colWindow->GetBestSize().y; + m_colLabelWin = new wxGridHeaderCtrl(this); + m_colLabelHeight = m_colLabelWin->GetBestSize().y; } else // draw labels ourselves { - m_colWindow = new wxGridColLabelWindow(this); + m_colLabelWin = new wxGridColLabelWindow(this); m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; } } @@ -2479,7 +2479,7 @@ void wxGrid::Init() m_cornerLabelWin = NULL; m_rowLabelWin = NULL; - m_colWindow = NULL; + m_colLabelWin = NULL; m_gridWin = NULL; m_table = NULL; @@ -2754,8 +2754,8 @@ void wxGrid::CalcWindowSizes() if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() ) m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight ); - if ( m_colWindow && m_colWindow->IsShown() ) - m_colWindow->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); + if ( m_colLabelWin && m_colLabelWin->IsShown() ) + m_colLabelWin->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); if ( m_rowLabelWin && m_rowLabelWin->IsShown() ) m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh ); @@ -2989,7 +2989,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } result = true; @@ -3047,7 +3047,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } result = true; @@ -3124,7 +3124,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) if ( !GetBatchCount() ) { CalcDimensions(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } result = true; @@ -3510,7 +3510,7 @@ void wxGrid::UpdateColumnSortingIndicator(int col) if ( m_useNativeHeader ) GetGridColHeader()->UpdateColumn(col); else if ( m_nativeColumnLabels ) - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); //else: sorting indicator display not yet implemented in grid version } @@ -3795,7 +3795,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) // the column didn't actually move anywhere if ( col != -1 ) DoColHeaderClick(col); - m_colWindow->Refresh(); // "unpress" the column + m_colLabelWin->Refresh(); // "unpress" the column } else { @@ -3964,9 +3964,9 @@ void wxGrid::ChangeCursorMode(CursorMode mode, wxLogTrace(wxT("grid"), wxT("wxGrid cursor mode (mouse capture for %s): %s -> %s"), - win == m_colWindow ? wxT("colLabelWin") - : win ? wxT("rowLabelWin") - : wxT("gridWin"), + win == m_colLabelWin ? wxT("colLabelWin") + : win ? wxT("rowLabelWin") + : wxT("gridWin"), cursorModes[m_cursorMode], cursorModes[mode]); #endif // wxUSE_LOG_TRACE @@ -4576,7 +4576,7 @@ void wxGrid::RefreshAfterColPosChange() } else { - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } m_gridWin->Refresh(); } @@ -4886,7 +4886,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) if ( width_cell > 0 && height_label > 0 ) { wxRect anotherrect(x, rect_y, width_cell, height_label); - m_colWindow->Refresh(eraseb, &anotherrect); + m_colLabelWin->Refresh(eraseb, &anotherrect); } // Paint row labels part intersecting rect. @@ -4906,7 +4906,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) else { m_cornerLabelWin->Refresh(eraseb, NULL); - m_colWindow->Refresh(eraseb, NULL); + m_colLabelWin->Refresh(eraseb, NULL); m_rowLabelWin->Refresh(eraseb, NULL); m_gridWin->Refresh(eraseb, NULL); } @@ -5944,7 +5944,7 @@ void wxGrid::UseNativeColHeader(bool native) if ( native == m_useNativeHeader ) return; - delete m_colWindow; + delete m_colLabelWin; m_useNativeHeader = native; CreateColumnWindow(); @@ -6053,7 +6053,7 @@ void wxGrid::DrawColLabel(wxDC& dc, int col) { // It is reported that we need to erase the background to avoid display // artefacts, see #12055. - wxDCBrushChanger setBrush(dc, m_colWindow->GetBackgroundColour()); + wxDCBrushChanger setBrush(dc, m_colLabelWin->GetBackgroundColour()); dc.DrawRectangle(rect); rend.DrawBorder(*this, dc, rect); @@ -6260,7 +6260,7 @@ void wxGrid::EndBatch() { CalcDimensions(); m_rowLabelWin->Refresh(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); m_cornerLabelWin->Refresh(); m_gridWin->Refresh(); } @@ -7216,12 +7216,12 @@ void wxGrid::SetColLabelSize( int height ) { if ( height == 0 ) { - m_colWindow->Show( false ); + m_colLabelWin->Show( false ); m_cornerLabelWin->Show( false ); } else if ( m_colLabelHeight == 0 ) { - m_colWindow->Show( true ); + m_colLabelWin->Show( true ); if ( m_rowLabelWidth > 0 ) m_cornerLabelWin->Show( true ); } @@ -7239,13 +7239,13 @@ void wxGrid::SetLabelBackgroundColour( const wxColour& colour ) { m_labelBackgroundColour = colour; m_rowLabelWin->SetBackgroundColour( colour ); - m_colWindow->SetBackgroundColour( colour ); + m_colLabelWin->SetBackgroundColour( colour ); m_cornerLabelWin->SetBackgroundColour( colour ); if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); m_cornerLabelWin->Refresh(); } } @@ -7259,7 +7259,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } } @@ -7270,7 +7270,7 @@ void wxGrid::SetLabelFont( const wxFont& font ) if ( !GetBatchCount() ) { m_rowLabelWin->Refresh(); - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } @@ -7336,7 +7336,7 @@ void wxGrid::SetColLabelAlignment( int horiz, int vert ) if ( !GetBatchCount() ) { - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } } @@ -7386,7 +7386,7 @@ void wxGrid::SetColLabelTextOrientation( int textOrientation ) m_colLabelTextOrientation = textOrientation; if ( !GetBatchCount() ) - m_colWindow->Refresh(); + m_colLabelWin->Refresh(); } void wxGrid::SetCornerLabelTextOrientation( int textOrientation ) @@ -8381,7 +8381,7 @@ void wxGrid::SetColSize( int col, int width ) { long w, h; wxArrayString lines; - wxClientDC dc(m_colWindow); + wxClientDC dc(m_colLabelWin); dc.SetFont(GetLabelFont()); StringToLines(GetColLabelValue(col), lines); if ( GetColLabelTextOrientation() == wxHORIZONTAL ) From a871229f8be965ed2c66cfc9002e327b932dea45 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 15 Jul 2019 15:13:42 +0200 Subject: [PATCH 6/8] Refactor wxGrid code to use SetNativeHeaderColXXX() functions Add two simple helpers: SetNativeHeaderColCount() and SetNativeHeaderColOrder() and call the latter from the former to ensure that the columns order is always correct when switching to the native control. --- include/wx/generic/grid.h | 6 ++++++ src/generic/grid.cpp | 29 +++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index ca5db2a18c..94f8c09679 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -2382,6 +2382,12 @@ private: void DoSetRowSize( int row, int height ); void DoSetColSize( int col, int width ); + // These methods can only be called when m_useNativeHeader is true and call + // SetColumnCount() and Set- or ResetColumnsOrder() as necessary on the + // native wxHeaderCtrl being used. Note that the first one already calls + // the second one, so it's never necessary to call both of them. + void SetNativeHeaderColCount(); + void SetNativeHeaderColOrder(); // these sets contain the indices of fixed, i.e. non-resizable // interactively, grid rows or columns and are NULL if there are no fixed diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index fcce59670d..a5ef612367 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -2438,7 +2438,7 @@ wxGrid::SetTable(wxGridTableBase *table, // Notice that this must be called after setting m_table as it uses it // indirectly, via wxGrid::GetColLabelValue(). if ( m_useNativeHeader ) - GetGridColHeader()->SetColumnCount(m_numCols); + SetNativeHeaderColCount(); m_selection = new wxGridSelection( this, selmode ); if (checkSelection) @@ -4569,10 +4569,7 @@ void wxGrid::RefreshAfterColPosChange() // and make the changes visible if ( m_useNativeHeader ) { - if ( m_colAt.empty() ) - GetGridColHeader()->ResetColumnsOrder(); - else - GetGridColHeader()->SetColumnsOrder(m_colAt); + SetNativeHeaderColOrder(); } else { @@ -5950,7 +5947,8 @@ void wxGrid::UseNativeColHeader(bool native) CreateColumnWindow(); if ( m_useNativeHeader ) - GetGridColHeader()->SetColumnCount(m_numCols); + SetNativeHeaderColCount(); + CalcWindowSizes(); } @@ -8494,6 +8492,25 @@ int wxGrid::GetRowMinimalAcceptableHeight() const return m_minAcceptableRowHeight; } +void wxGrid::SetNativeHeaderColCount() +{ + wxASSERT_MSG( m_useNativeHeader, "no column header window" ); + + GetGridColHeader()->SetColumnCount(m_numCols); + + SetNativeHeaderColOrder(); +} + +void wxGrid::SetNativeHeaderColOrder() +{ + wxASSERT_MSG( m_useNativeHeader, "no column header window" ); + + if ( !m_colAt.empty() ) + GetGridColHeader()->SetColumnsOrder(m_colAt); + else + GetGridColHeader()->ResetColumnsOrder(); +} + // ---------------------------------------------------------------------------- // auto sizing // ---------------------------------------------------------------------------- From f61b58bba3aceae07afbf95d03ca7c56d23871c7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 15 Jul 2019 15:51:00 +0200 Subject: [PATCH 7/8] Override wxGrid::ScrollWindow() to scroll row/column labels too Instead of doing it in overridden wxGridWindow::ScrollWindow(), do it from wxGrid::ScrollWindow() itself, this makes more sense and will make it easier to generalize it to scroll more windows. No real changes yet. --- include/wx/generic/grid.h | 2 ++ src/generic/grid.cpp | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 94f8c09679..38905b2623 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -1068,6 +1068,8 @@ public: void DrawAllGridLines( wxDC& dc, const wxRegion & reg ); void DrawCell( wxDC& dc, const wxGridCellCoords& ); void DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells); + void ScrollWindow( int dx, int dy, const wxRect *rect ) wxOVERRIDE; + void UpdateGridWindows() const; // this function is called when the current cell highlight must be redrawn diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index a5ef612367..844dc150bb 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -2111,9 +2111,17 @@ void wxGrid::DoRenderBox( wxDC& dc, const int& style, void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect ) { - wxWindow::ScrollWindow( dx, dy, rect ); - m_owner->GetGridRowLabelWindow()->ScrollWindow( 0, dy, rect ); - m_owner->GetGridColLabelWindow()->ScrollWindow( dx, 0, rect ); + m_owner->ScrollWindow(dx, dy, rect); +} + +void wxGrid::ScrollWindow( int dx, int dy, const wxRect *rect ) +{ + // We must explicitly call wxWindow version to avoid infinite recursion as + // wxGridWindow::ScrollWindow() calls this method back. + m_gridWin->wxWindow::ScrollWindow( dx, dy, rect ); + + m_rowLabelWin->ScrollWindow( 0, dy, rect ); + m_colLabelWin->ScrollWindow( dx, 0, rect ); } void wxGridWindow::OnMouseEvent( wxMouseEvent& event ) From 04f7f1fd32a9f061bb5ad41d709a9e9ea7fe76c8 Mon Sep 17 00:00:00 2001 From: lucian-rotariu Date: Wed, 3 Jul 2019 23:07:30 +0300 Subject: [PATCH 8/8] 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);