diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 825d69102e..9b821d2237 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -100,6 +100,7 @@ class WXDLLIMPEXP_FWD_CORE wxGridCornerLabelWindow; class WXDLLIMPEXP_FWD_CORE wxGridEvent; class WXDLLIMPEXP_FWD_CORE wxGridRowLabelWindow; class WXDLLIMPEXP_FWD_CORE wxGridWindow; +class WXDLLIMPEXP_FWD_CORE wxGridSubwindow; class WXDLLIMPEXP_FWD_CORE wxGridTypeRegistry; class WXDLLIMPEXP_FWD_CORE wxGridSelection; @@ -2804,10 +2805,19 @@ protected: // or -1 if there is no resize operation in progress. int m_dragRowOrCol; + // Original row or column size when resizing; used when the user cancels + int m_dragRowOrColOldSize; + // true if a drag operation is in progress; when this is true, // m_startDragPos is valid, i.e. not wxDefaultPosition bool m_isDragging; + // true if a drag operation was canceled + // (mouse event Dragging() might still be active until LeftUp) + // m_isDragging can only be set after m_cancelledDragging is cleared. + // This is done when a mouse event happens with left button up. + bool m_cancelledDragging; + // the position (in physical coordinates) where the user started dragging // the mouse or wxDefaultPosition if mouse isn't being dragged // @@ -2816,6 +2826,10 @@ protected: // setting m_isDragging to true wxPoint m_startDragPos; + // the position of the last mouse event + // used for detection of the movement direction + wxPoint m_lastMousePos; + bool m_waitForSlowClick; wxCursor m_rowResizeCursor; @@ -2960,6 +2974,13 @@ private: // release the mouse capture if it's currently captured void EndDraggingIfNecessary(); + // helper for Process...MouseEvent to block re-triggering m_isDragging + bool CheckIfDragCancelled(wxMouseEvent *event); + + // helper for Process...MouseEvent to scroll + void CheckDoDragScroll(wxGridSubwindow *eventGridWindow, wxGridSubwindow *gridWindow, + wxPoint posEvent, int direction); + // return true if the grid should be refreshed right now bool ShouldRefresh() const { @@ -3029,7 +3050,7 @@ private: void DoColHeaderClick(int col); - void DoStartResizeRowOrCol(int col); + void DoStartResizeRowOrCol(int col, int size); void DoStartMoveRow(int col); void DoStartMoveCol(int col); diff --git a/include/wx/generic/gridsel.h b/include/wx/generic/gridsel.h index 4a895e4166..e2dcaaf704 100644 --- a/include/wx/generic/gridsel.h +++ b/include/wx/generic/gridsel.h @@ -112,6 +112,7 @@ public: wxVectorGridBlockCoords& GetBlocks() { return m_selection; } void EndSelecting(); + void CancelSelecting(); private: void SelectBlockNoEvent(const wxGridBlockCoords& block) diff --git a/include/wx/generic/private/grid.h b/include/wx/generic/private/grid.h index 3d7ab59a71..8893c406ad 100644 --- a/include/wx/generic/private/grid.h +++ b/include/wx/generic/private/grid.h @@ -281,6 +281,8 @@ public: wxGrid *GetOwner() { return m_owner; } + virtual bool IsFrozen() const { return false; } + protected: void OnMouseCaptureLost(wxMouseCaptureLostEvent& event); @@ -298,8 +300,6 @@ public: { } - virtual bool IsFrozen() const { return false; } - private: void OnPaint( wxPaintEvent& event ); void OnMouseEvent( wxMouseEvent& event ); @@ -330,8 +330,6 @@ public: { } - virtual bool IsFrozen() const { return false; } - private: void OnPaint( wxPaintEvent& event ); void OnMouseEvent( wxMouseEvent& event ); diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 98e8e9fe20..cf548ab76e 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -13,7 +13,6 @@ - Make Begin/EndBatch() the same as the generic Freeze/Thaw() - Review the column reordering code, it's a mess. - - Implement row reordering after dealing with the columns. */ // For compilers that support precompilation, includes "wx/wx.h". @@ -3034,8 +3033,11 @@ void wxGrid::Init() m_dragMoveCol = -1; m_dragLastPos = -1; m_dragRowOrCol = -1; + m_dragRowOrColOldSize = -1; m_isDragging = false; + m_cancelledDragging = false; m_startDragPos = wxDefaultPosition; + m_lastMousePos = wxDefaultPosition; m_sortCol = wxNOT_FOUND; m_sortIsAscending = true; @@ -3864,12 +3866,99 @@ void wxGrid::PrepareDCFor(wxDC &dc, wxGridWindow *gridWindow) dc.SetDeviceOrigin(dcOrigin.x, dcOrigin.y); } +void wxGrid::CheckDoDragScroll(wxGridSubwindow *eventGridWindow, wxGridSubwindow *gridWindow, + wxPoint posEvent, int direction) +{ + // helper for Process{Row|Col}LabelMouseEvent, ProcessGridCellMouseEvent: + // scroll when at the edges or outside the window w. respect to direction + // eventGridWindow: the window that received the mouse event + // gridWindow: the same or the corresponding non-frozen window + + if ( !m_isDragging ) + { + // drag is just starting + m_lastMousePos = posEvent; + return; + } + + int w, h; + eventGridWindow->GetSize(&w, &h); + // ViewStart is scroll position in scroll units + wxPoint scrollPos = GetViewStart(); + wxPoint newScrollPos = wxPoint(wxDefaultCoord, wxDefaultCoord); + + if ( direction & wxHORIZONTAL ) + { + // check x direction + if ( eventGridWindow->IsFrozen() && posEvent.x < w ) + { + // in the frozen window, moving left? + if ( scrollPos.x > 0 && posEvent.x < m_lastMousePos.x ) + newScrollPos.x = scrollPos.x - 1; + } + else if ( eventGridWindow->IsFrozen() && posEvent.x >= w ) + { + // frozen window was left, add the width of the non-frozen window + w += gridWindow->GetSize().x; + } + + if ( posEvent.x < 0 && scrollPos.x > 0 ) + newScrollPos.x = scrollPos.x - 1; + else if ( posEvent.x >= w ) + newScrollPos.x = scrollPos.x + 1; + } + + if ( direction & wxVERTICAL ) + { + // check y direction + if ( eventGridWindow->IsFrozen() && posEvent.y < h ) + { + // in the frozen window, moving upward? + if ( scrollPos.y && posEvent.y < m_lastMousePos.y ) + newScrollPos.y = scrollPos.y - 1; + } + else if ( eventGridWindow->IsFrozen() && posEvent.y >= h ) + { + // frozen window was left, add the height of the non-frozen window + h += gridWindow->GetSize().y; + } + + if ( posEvent.y < 0 && scrollPos.y > 0 ) + newScrollPos.y = scrollPos.y - 1; + else if ( posEvent.y >= h ) + newScrollPos.y = scrollPos.y + 1; + } + + if ( newScrollPos.x != wxDefaultCoord || newScrollPos.y != wxDefaultCoord ) + Scroll(newScrollPos); + + m_lastMousePos = posEvent; +} + +bool wxGrid::CheckIfDragCancelled(wxMouseEvent *event) +{ + // helper for Process{Row|Col}LabelMouseEvent, ProcessGridCellMouseEvent: + // block re-triggering m_isDragging + if ( !m_cancelledDragging ) + return false; + + if ( event->LeftIsDown() ) + return true; + + m_cancelledDragging = false; + + return false; +} + void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindow* rowLabelWin ) { int y; wxGridWindow *gridWindow = rowLabelWin->IsFrozen() ? m_frozenRowGridWin : m_gridWin; - event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow)); + // store position, before it's modified in the next step + const wxPoint posEvent = event.GetPosition(); + + event.SetPosition(posEvent + 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 ) @@ -3878,6 +3967,15 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo CalcGridWindowUnscrolledPosition(0, event.GetPosition().y, NULL, &y, gridWindow); int row = YToRow( y ); + if ( CheckIfDragCancelled(&event) ) + return; + + if ( event.Dragging() && (m_winCapture == rowLabelWin) ) + { + // scroll when at the edges or outside the window + CheckDoDragScroll(rowLabelWin, m_rowLabelWin, posEvent, wxVERTICAL); + } + if ( event.Dragging() ) { if (!m_isDragging) @@ -3995,7 +4093,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo row = YToEdgeOfRow(y); if ( row != wxNOT_FOUND && CanDragRowSize(row) ) { - DoStartResizeRowOrCol(row); + DoStartResizeRowOrCol(row, GetRowSize(row)); ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin); } else // not a request to start resizing @@ -4157,6 +4255,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, rowLabelWin); m_dragLastPos = -1; + m_lastMousePos = wxDefaultPosition; m_isDragging = false; } @@ -4311,13 +4410,14 @@ void wxGrid::DoColHeaderClick(int col) } } -void wxGrid::DoStartResizeRowOrCol(int col) +void wxGrid::DoStartResizeRowOrCol(int col, int size) { // Hide the editor if it's currently shown to avoid any weird interactions // with it while dragging the row/column separator. AcceptCellEditControlIfShown(); m_dragRowOrCol = col; + m_dragRowOrColOldSize = size; } void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindow* colLabelWin ) @@ -4325,7 +4425,10 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo int x; wxGridWindow *gridWindow = colLabelWin->IsFrozen() ? m_frozenColGridWin : m_gridWin; - event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow)); + // store position, before it's modified in the next step + const wxPoint posEvent = event.GetPosition(); + + event.SetPosition(posEvent + 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) @@ -4334,6 +4437,16 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo CalcGridWindowUnscrolledPosition(event.GetPosition().x, 0, &x, NULL, gridWindow); int col = XToCol(x); + + if ( CheckIfDragCancelled(&event) ) + return; + + if ( event.Dragging() && (m_winCapture == colLabelWin) ) + { + // scroll when at the edges or outside the window + CheckDoDragScroll(colLabelWin, GetColLabelWindow(), posEvent, wxHORIZONTAL); + } + if ( event.Dragging() ) { if (!m_isDragging) @@ -4452,7 +4565,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo int colEdge = XToEdgeOfCol(x); if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) ) { - DoStartResizeRowOrCol(colEdge); + DoStartResizeRowOrCol(colEdge, GetColSize(colEdge)); ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin); } else // not a request to start resizing @@ -4611,6 +4724,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow()); m_dragLastPos = -1; + m_lastMousePos = wxDefaultPosition; m_isDragging = false; } @@ -4736,6 +4850,7 @@ void wxGrid::DoAfterDraggingEnd() m_isDragging = false; m_startDragPos = wxDefaultPosition; + m_lastMousePos = wxDefaultPosition; m_cursorMode = WXGRID_CURSOR_SELECT_CELL; m_winCapture->SetCursor( *wxSTANDARD_CURSOR ); @@ -4803,9 +4918,6 @@ void wxGrid::ChangeCursorMode(CursorMode mode, case WXGRID_CURSOR_MOVE_ROW: case WXGRID_CURSOR_MOVE_COL: - // Currently we don't capture mouse when moving columns, which is - // almost certainly wrong. - captureMouse = false; win->SetCursor( wxCursor(wxCURSOR_HAND) ); break; @@ -4931,12 +5043,16 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event, { int dragRowOrCol = wxNOT_FOUND; if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + { dragRowOrCol = XToEdgeOfCol(pos.x); + DoStartResizeRowOrCol(dragRowOrCol, GetColSize(dragRowOrCol)); + } else + { dragRowOrCol = YToEdgeOfRow(pos.y); + DoStartResizeRowOrCol(dragRowOrCol, GetRowSize(dragRowOrCol)); + } wxCHECK_RET( dragRowOrCol != -1, "Can't determine row or column in resizing mode" ); - - DoStartResizeRowOrCol(dragRowOrCol); } break; @@ -5115,13 +5231,20 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG // 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) + + if ( CheckIfDragCancelled(&event) ) + return; + wxGridWindow *gridWindow = DevicePosToGridWindow(event.GetPosition() + eventGridWindow->GetPosition()); if ( !gridWindow ) gridWindow = eventGridWindow; - event.SetPosition(event.GetPosition() + eventGridWindow->GetPosition() - + // store position, before it's modified in the next step + const wxPoint posEvent = event.GetPosition(); + + event.SetPosition(posEvent + eventGridWindow->GetPosition() - wxPoint(m_rowLabelWidth, m_colLabelHeight)); wxPoint pos = CalcGridWindowUnscrolledPosition(event.GetPosition(), gridWindow); @@ -5152,6 +5275,13 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG const bool isDraggingWithLeft = event.Dragging() && event.LeftIsDown(); + if ( isDraggingWithLeft && (m_winCapture == eventGridWindow) ) + { + // scroll when at the edges or outside the window + CheckDoDragScroll(eventGridWindow, m_gridWin, posEvent, + wxHORIZONTAL | wxVERTICAL); + } + // While dragging the mouse, only releasing the left mouse button, which // cancels the drag operation, is processed (above) and any other events // are just ignored while it's in progress. @@ -5295,7 +5425,7 @@ void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event, wxGridWindow* gridWin void wxGrid::DoHeaderStartDragResizeCol(int col) { - DoStartResizeRowOrCol(col); + DoStartResizeRowOrCol(col, GetColSize(col)); } void wxGrid::DoHeaderDragResizeCol(int width) @@ -6185,7 +6315,44 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) break; case WXK_ESCAPE: - ClearSelection(); + if ( m_isDragging && m_winCapture ) + { + switch ( m_cursorMode ) + { + case WXGRID_CURSOR_MOVE_COL: + case WXGRID_CURSOR_MOVE_ROW: + // end row/column moving + m_winCapture->Refresh(); + m_dragLastPos = -1; + break; + + case WXGRID_CURSOR_RESIZE_ROW: + case WXGRID_CURSOR_RESIZE_COL: + // reset to size from before dragging + (m_cursorMode == WXGRID_CURSOR_RESIZE_ROW + ? static_cast(wxGridRowOperations()) + : static_cast(wxGridColumnOperations()) + ).SetLineSize(this, m_dragRowOrCol, m_dragRowOrColOldSize); + + m_dragRowOrCol = -1; + break; + + case WXGRID_CURSOR_SELECT_CELL: + case WXGRID_CURSOR_SELECT_ROW: + case WXGRID_CURSOR_SELECT_COL: + if ( m_selection ) + m_selection->CancelSelecting(); + break; + } + EndDraggingIfNecessary(); + + // ensure that a new drag operation is only started after a LeftUp + m_cancelledDragging = true; + } + else + { + ClearSelection(); + } break; case WXK_TAB: diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp index 81ee2a4641..bf8f617480 100644 --- a/src/generic/gridsel.cpp +++ b/src/generic/gridsel.cpp @@ -72,6 +72,20 @@ void wxGridSelection::EndSelecting() m_grid->GetEventHandler()->ProcessEvent(gridEvt); } +void wxGridSelection::CancelSelecting() +{ + // It's possible that nothing was selected finally, e.g. the mouse could + // have been dragged around only to return to the starting cell, just don't + // do anything in this case. + if ( !IsSelection() ) + return; + + const wxGridBlockCoords& block = m_selection.back(); + m_grid->RefreshBlock(block.GetTopLeft(), block.GetBottomRight()); + m_selection.pop_back(); +} + + bool wxGridSelection::IsInSelection( int row, int col ) const { // Check whether the given cell is contained in one of the selected blocks.