X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/396825dced4558d8d724d0e38f7e1017f76d0a96..f239a20092359e3c914adb79bd39f3f5d2b2e06f:/src/generic/headerctrlg.cpp?ds=inline diff --git a/src/generic/headerctrlg.cpp b/src/generic/headerctrlg.cpp index 37cc6039a9..279c1d031b 100644 --- a/src/generic/headerctrlg.cpp +++ b/src/generic/headerctrlg.cpp @@ -23,8 +23,7 @@ #pragma hdrstop #endif -#ifndef WX_PRECOMP -#endif // WX_PRECOMP +#if wxUSE_HEADERCTRL #include "wx/headerctrl.h" @@ -40,8 +39,6 @@ namespace { -const unsigned NO_SORT = (unsigned)-1; - const unsigned COL_NONE = (unsigned)-1; } // anonymous namespace @@ -58,7 +55,9 @@ void wxHeaderCtrl::Init() { m_numColumns = 0; m_hover = - m_colBeingResized = COL_NONE; + m_colBeingResized = + m_colBeingReordered = COL_NONE; + m_dragOffset = 0; m_scrollOffset = 0; } @@ -90,8 +89,12 @@ wxHeaderCtrl::~wxHeaderCtrl() void wxHeaderCtrl::DoSetCount(unsigned int count) { + // update the column indices order array before changing m_numColumns + DoResizeColumnIndices(m_colIndices, count); + m_numColumns = count; + InvalidateBestSize(); Refresh(); } @@ -102,6 +105,8 @@ unsigned int wxHeaderCtrl::DoGetCount() const void wxHeaderCtrl::DoUpdate(unsigned int idx) { + InvalidateBestSize(); + // we need to refresh not only this column but also the ones after it in // case it was shown or hidden or its width changed -- it would be nice to // avoid doing this unnecessary by storing the old column width (TODO) @@ -126,19 +131,28 @@ void wxHeaderCtrl::DoScrollHorz(int dx) wxSize wxHeaderCtrl::DoGetBestSize() const { + wxWindow *win = GetParent(); + int height = wxRendererNative::Get().GetHeaderButtonHeight( win ); + // the vertical size is rather arbitrary but it looks better if we leave // some space around the text - return wxSize(GetColStart(GetColumnCount()), (7*GetCharHeight())/4); + const wxSize size(IsEmpty() ? wxHeaderCtrlBase::DoGetBestSize().x + : GetColEnd(GetColumnCount() - 1), + height ); // (7*GetCharHeight())/4); + CacheBestSize(size); + return size; } int wxHeaderCtrl::GetColStart(unsigned int idx) const { - wxHeaderCtrl * const self = const_cast(this); - int pos = m_scrollOffset; - for ( unsigned n = 0; n < idx; n++ ) + for ( unsigned n = 0; ; n++ ) { - const wxHeaderColumnBase& col = self->GetColumn(n); + const unsigned i = m_colIndices[n]; + if ( i == idx ) + break; + + const wxHeaderColumn& col = GetColumn(i); if ( col.IsShown() ) pos += col.GetWidth(); } @@ -146,38 +160,48 @@ int wxHeaderCtrl::GetColStart(unsigned int idx) const return pos; } -int wxHeaderCtrl::FindColumnAtPos(int x, bool& onSeparator) const +int wxHeaderCtrl::GetColEnd(unsigned int idx) const { - wxHeaderCtrl * const self = const_cast(this); + int x = GetColStart(idx); + + return x + GetColumn(idx).GetWidth(); +} +unsigned int wxHeaderCtrl::FindColumnAtPoint(int x, bool *onSeparator) const +{ int pos = 0; const unsigned count = GetColumnCount(); for ( unsigned n = 0; n < count; n++ ) { - const wxHeaderColumnBase& col = self->GetColumn(n); + const unsigned idx = m_colIndices[n]; + const wxHeaderColumn& col = GetColumn(idx); if ( col.IsHidden() ) continue; pos += col.GetWidth(); - // if the column is resizeable, check if we're approximatively over the + // if the column is resizable, check if we're approximatively over the // line separating it from the next column // // TODO: don't hardcode sensitivity if ( col.IsResizeable() && abs(x - pos) < 8 ) { - onSeparator = true; - return n; + if ( onSeparator ) + *onSeparator = true; + return idx; } // inside this column? if ( x < pos ) { - onSeparator = false; - return n; + if ( onSeparator ) + *onSeparator = false; + return idx; } } + if ( onSeparator ) + *onSeparator = false; return COL_NONE; } @@ -211,7 +235,7 @@ void wxHeaderCtrl::RefreshColsAfter(unsigned int idx) } // ---------------------------------------------------------------------------- -// wxHeaderCtrl dragging +// wxHeaderCtrl dragging/resizing/reordering // ---------------------------------------------------------------------------- bool wxHeaderCtrl::IsResizing() const @@ -219,58 +243,225 @@ bool wxHeaderCtrl::IsResizing() const return m_colBeingResized != COL_NONE; } -void wxHeaderCtrl::UpdateResizingMarker(int xPhysical) +bool wxHeaderCtrl::IsReordering() const +{ + return m_colBeingReordered != COL_NONE; +} + +void wxHeaderCtrl::ClearMarkers() { - // unfortunately drawing the marker over the parent window doesn't work as - // it's usually covered by another window (the main control view) so just - // draw the marker over the header itself, even if it makes it not very - // useful wxClientDC dc(this); wxDCOverlay dcover(m_overlay, &dc); dcover.Clear(); +} - if ( xPhysical != -1 ) +void wxHeaderCtrl::EndDragging() +{ + // We currently only use markers for reordering, not for resizing + if (IsReordering()) { - dc.SetPen(*wxLIGHT_GREY_PEN); - dc.DrawLine(xPhysical, 0, xPhysical, GetClientSize().y); + ClearMarkers(); + m_overlay.Reset(); } + + // don't use the special dragging cursor any more + SetCursor(wxNullCursor); } -void wxHeaderCtrl::EndDragging() +void wxHeaderCtrl::CancelDragging() { - UpdateResizingMarker(-1); + wxASSERT_MSG( IsDragging(), + "shouldn't be called if we're not dragging anything" ); - m_overlay.Reset(); + EndDragging(); - // don't use the special dragging cursor any more - SetCursor(wxNullCursor); + unsigned int& col = IsResizing() ? m_colBeingResized : m_colBeingReordered; + + wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED, GetId()); + event.SetEventObject(this); + event.SetColumn(col); + + GetEventHandler()->ProcessEvent(event); + + col = COL_NONE; +} + +int wxHeaderCtrl::ConstrainByMinWidth(unsigned int col, int& xPhysical) +{ + const int xStart = GetColStart(col); + + // notice that GetMinWidth() returns 0 if there is no minimal width so it + // still makes sense to use it even in this case + const int xMinEnd = xStart + GetColumn(col).GetMinWidth(); + + if ( xPhysical < xMinEnd ) + xPhysical = xMinEnd; + + return xPhysical - xStart; } -void wxHeaderCtrl::EndResizing(int width) +void wxHeaderCtrl::StartOrContinueResizing(unsigned int col, int xPhysical) +{ + wxHeaderCtrlEvent event(IsResizing() ? wxEVT_COMMAND_HEADER_RESIZING + : wxEVT_COMMAND_HEADER_BEGIN_RESIZE, + GetId()); + event.SetEventObject(this); + event.SetColumn(col); + + event.SetWidth(ConstrainByMinWidth(col, xPhysical)); + + if ( GetEventHandler()->ProcessEvent(event) && !event.IsAllowed() ) + { + if ( IsResizing() ) + { + ReleaseMouse(); + CancelDragging(); + } + //else: nothing to do -- we just don't start to resize + } + else // go ahead with resizing + { + if ( !IsResizing() ) + { + m_colBeingResized = col; + SetCursor(wxCursor(wxCURSOR_SIZEWE)); + CaptureMouse(); + } + //else: we had already done the above when we started + + } +} + +void wxHeaderCtrl::EndResizing(int xPhysical) { wxASSERT_MSG( IsResizing(), "shouldn't be called if we're not resizing" ); EndDragging(); - // if dragging was cancelled we must have already lost the mouse capture so - // don't try to release it - if ( width != -1 ) - ReleaseMouse(); + ReleaseMouse(); wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_RESIZE, GetId()); event.SetEventObject(this); event.SetColumn(m_colBeingResized); - if ( width == -1 ) - event.SetCancelled(); - else - event.SetWidth(width); + event.SetWidth(ConstrainByMinWidth(m_colBeingResized, xPhysical)); GetEventHandler()->ProcessEvent(event); m_colBeingResized = COL_NONE; } +void wxHeaderCtrl::UpdateReorderingMarker(int xPhysical) +{ + wxClientDC dc(this); + + wxDCOverlay dcover(m_overlay, &dc); + dcover.Clear(); + + dc.SetPen(*wxBLUE); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + + // draw the phantom position of the column being dragged + int x = xPhysical - m_dragOffset; + int y = GetClientSize().y; + dc.DrawRectangle(x, 0, + GetColumn(m_colBeingReordered).GetWidth(), y); + + // and also a hint indicating where it is going to be inserted if it's + // dropped now + unsigned int col = FindColumnAtPoint(xPhysical); + if ( col != COL_NONE ) + { + static const int DROP_MARKER_WIDTH = 4; + + dc.SetBrush(*wxBLUE); + dc.DrawRectangle(GetColEnd(col) - DROP_MARKER_WIDTH/2, 0, + DROP_MARKER_WIDTH, y); + } +} + +void wxHeaderCtrl::StartReordering(unsigned int col, int xPhysical) +{ + wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_BEGIN_REORDER, GetId()); + event.SetEventObject(this); + event.SetColumn(col); + + if ( GetEventHandler()->ProcessEvent(event) && !event.IsAllowed() ) + { + // don't start dragging it, nothing to do otherwise + return; + } + + m_dragOffset = xPhysical - GetColStart(col); + + m_colBeingReordered = col; + SetCursor(wxCursor(wxCURSOR_HAND)); + CaptureMouse(); + + // do not call UpdateReorderingMarker() here: we don't want to give + // feedback for reordering until the user starts to really move the mouse + // as he might want to just click on the column and not move it at all +} + +bool wxHeaderCtrl::EndReordering(int xPhysical) +{ + wxASSERT_MSG( IsReordering(), "shouldn't be called if we're not reordering" ); + + EndDragging(); + + ReleaseMouse(); + + const int colOld = m_colBeingReordered, + colNew = FindColumnAtPoint(xPhysical); + + m_colBeingReordered = COL_NONE; + + if ( xPhysical - GetColStart(colOld) == m_dragOffset ) + return false; + + if ( colNew != colOld ) + { + wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_REORDER, GetId()); + event.SetEventObject(this); + event.SetColumn(colOld); + + const unsigned pos = GetColumnPos(FindColumnAtPoint(xPhysical)); + event.SetNewOrder(pos); + + if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() ) + { + // do reorder the columns + DoMoveCol(colOld, pos); + } + } + + // whether we moved the column or not, the user did move the mouse and so + // did try to do it so return true + return true; +} + +// ---------------------------------------------------------------------------- +// wxHeaderCtrl column reordering +// ---------------------------------------------------------------------------- + +void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order) +{ + m_colIndices = order; + Refresh(); +} + +wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const +{ + return m_colIndices; +} + +void wxHeaderCtrl::DoMoveCol(unsigned int idx, unsigned int pos) +{ + MoveColumnInOrderArray(m_colIndices, idx, pos); + + Refresh(); +} + // ---------------------------------------------------------------------------- // wxHeaderCtrl event handlers // ---------------------------------------------------------------------------- @@ -290,6 +481,11 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) int w, h; GetClientSize(&w, &h); +#ifdef __WXGTK__ +// int vw; +// GetVirtualSize(&vw, NULL); +#endif + wxAutoBufferedPaintDC dc(this); dc.SetBackground(GetBackgroundColour()); @@ -302,11 +498,12 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) int xpos = 0; for ( unsigned int i = 0; i < count; i++ ) { - const wxHeaderColumnBase& col = GetColumn(i); + const unsigned idx = m_colIndices[i]; + const wxHeaderColumn& col = GetColumn(idx); if ( col.IsHidden() ) continue; - const int colWidth = col.GetWidth(); + int colWidth = col.GetWidth(); wxHeaderSortIconType sortArrow; if ( col.IsSortKey() ) @@ -322,7 +519,7 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) int state = 0; if ( IsEnabled() ) { - if ( i == m_hover ) + if ( idx == m_hover ) state = wxCONTROL_CURRENT; } else // disabled @@ -330,11 +527,22 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) state = wxCONTROL_DISABLED; } + if (i == 0) + state |= wxCONTROL_SPECIAL; + wxHeaderButtonParams params; params.m_labelText = col.GetTitle(); params.m_labelBitmap = col.GetBitmap(); params.m_labelAlignment = col.GetAlignment(); +#ifdef __WXGTK__ + if (i == count-1) + { +// colWidth = wxMax( colWidth, vw - xpos ); + state |= wxCONTROL_DIRTY; + } +#endif + wxRendererNative::Get().DrawHeaderButton ( this, @@ -351,21 +559,24 @@ void wxHeaderCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) void wxHeaderCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event)) { - if ( IsResizing() ) - EndResizing(-1); + if ( IsDragging() ) + CancelDragging(); } void wxHeaderCtrl::OnKeyDown(wxKeyEvent& event) { - if ( IsResizing() && event.GetKeyCode() == WXK_ESCAPE ) - { - ReleaseMouse(); - EndResizing(-1); - } - else + if ( event.GetKeyCode() == WXK_ESCAPE ) { - event.Skip(); + if ( IsDragging() ) + { + ReleaseMouse(); + CancelDragging(); + + return; + } } + + event.Skip(); } void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) @@ -384,19 +595,35 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) if ( IsResizing() ) { if ( mevent.LeftUp() ) - EndResizing(xPhysical - GetColStart(m_colBeingResized)); + EndResizing(xPhysical); else // update the live separator position - UpdateResizingMarker(xPhysical); + StartOrContinueResizing(m_colBeingResized, xPhysical); return; } + if ( IsReordering() ) + { + if ( !mevent.LeftUp() ) + { + // update the column position + UpdateReorderingMarker(xPhysical); + + return; + } + + // finish reordering and continue to generate a click event below if we + // didn't really reorder anything + if ( EndReordering(xPhysical) ) + return; + } + // find if the event is over a column at all bool onSeparator; const unsigned col = mevent.Leaving() ? (onSeparator = false, COL_NONE) - : FindColumnAtPos(xLogical, onSeparator); + : FindColumnAtPoint(xLogical, &onSeparator); // update the highlighted column if it changed @@ -427,15 +654,15 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) if ( onSeparator ) { // start resizing the column - m_colBeingResized = col; - SetCursor(wxCursor(wxCURSOR_SIZEWE)); - CaptureMouse(); - UpdateResizingMarker(xPhysical); + wxASSERT_MSG( !IsResizing(), "reentering column resize mode?" ); + StartOrContinueResizing(col, xPhysical); } else // on column itself { - // TODO: drag column - ; + // start dragging the column + wxASSERT_MSG( !IsReordering(), "reentering column move mode?" ); + + StartReordering(col, xPhysical); } return; @@ -490,3 +717,5 @@ void wxHeaderCtrl::OnMouse(wxMouseEvent& mevent) } #endif // wxHAS_GENERIC_HEADERCTRL + +#endif // wxUSE_HEADERCTRL