// Purpose: generic wxHeaderCtrl implementation
// Author: Vadim Zeitlin
// Created: 2008-12-03
-// RCS-ID: $Id$
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#pragma hdrstop
#endif
-#ifndef WX_PRECOMP
-#endif // WX_PRECOMP
+#if wxUSE_HEADERCTRL
#include "wx/headerctrl.h"
namespace
{
-const unsigned NO_SORT = (unsigned)-1;
-
const unsigned COL_NONE = (unsigned)-1;
} // anonymous namespace
void wxHeaderCtrl::DoSetCount(unsigned int count)
{
- // update the column indices array if necessary
- if ( count > m_numColumns )
- {
- // all new columns have default positions equal to their indices
- for ( unsigned n = m_numColumns; n < count; n++ )
- m_colIndices.push_back(n);
- }
- else if ( count < m_numColumns )
- {
- // filter out all the positions which are invalid now while keeping the
- // order of the remaining ones
- wxArrayInt colIndices;
- for ( unsigned n = 0; n < m_numColumns; n++ )
- {
- const unsigned idx = m_colIndices[n];
- if ( idx < count )
- colIndices.push_back(idx);
- }
-
- wxASSERT_MSG( colIndices.size() == count, "logic error" );
-
- m_colIndices = colIndices;
- }
+ // update the column indices order array before changing m_numColumns
+ DoResizeColumnIndices(m_colIndices, count);
m_numColumns = count;
+ InvalidateBestSize();
Refresh();
}
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)
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(IsEmpty() ? wxHeaderCtrlBase::DoGetBestSize().x
- : GetColEnd(GetColumnCount() - 1),
- (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<wxHeaderCtrl *>(this);
-
int pos = m_scrollOffset;
for ( unsigned n = 0; ; n++ )
{
if ( i == idx )
break;
- const wxHeaderColumn& col = self->GetColumn(i);
+ const wxHeaderColumn& col = GetColumn(i);
if ( col.IsShown() )
pos += col.GetWidth();
}
{
int x = GetColStart(idx);
- return x + const_cast<wxHeaderCtrl *>(this)->GetColumn(idx).GetWidth();
+ return x + GetColumn(idx).GetWidth();
}
unsigned int wxHeaderCtrl::FindColumnAtPoint(int x, bool *onSeparator) const
{
- wxHeaderCtrl * const self = const_cast<wxHeaderCtrl *>(this);
-
int pos = 0;
const unsigned count = GetColumnCount();
for ( unsigned n = 0; n < count; n++ )
{
const unsigned idx = m_colIndices[n];
- const wxHeaderColumn& col = self->GetColumn(idx);
+ 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 ( onSeparator )
+ *onSeparator = false;
return COL_NONE;
}
dcover.Clear();
}
-void wxHeaderCtrl::UpdateResizingMarker(int xPhysical)
-{
- wxClientDC dc(this);
-
- wxDCOverlay dcover(m_overlay, &dc);
- dcover.Clear();
-
- // 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
- dc.SetPen(*wxLIGHT_GREY_PEN);
- dc.DrawLine(xPhysical, 0, xPhysical, GetClientSize().y);
-}
-
void wxHeaderCtrl::EndDragging()
{
- ClearMarkers();
-
- m_overlay.Reset();
+ // We currently only use markers for reordering, not for resizing
+ if (IsReordering())
+ {
+ ClearMarkers();
+ m_overlay.Reset();
+ }
// 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());
+ wxHeaderCtrlEvent event(wxEVT_HEADER_DRAGGING_CANCELLED, GetId());
event.SetEventObject(this);
event.SetColumn(col);
void wxHeaderCtrl::StartOrContinueResizing(unsigned int col, int xPhysical)
{
- wxHeaderCtrlEvent event(IsResizing() ? wxEVT_COMMAND_HEADER_RESIZING
- : wxEVT_COMMAND_HEADER_BEGIN_RESIZE,
+ wxHeaderCtrlEvent event(IsResizing() ? wxEVT_HEADER_RESIZING
+ : wxEVT_HEADER_BEGIN_RESIZE,
GetId());
event.SetEventObject(this);
event.SetColumn(col);
}
//else: we had already done the above when we started
- UpdateResizingMarker(xPhysical);
}
}
ReleaseMouse();
- wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_RESIZE, GetId());
+ wxHeaderCtrlEvent event(wxEVT_HEADER_END_RESIZE, GetId());
event.SetEventObject(this);
event.SetColumn(m_colBeingResized);
event.SetWidth(ConstrainByMinWidth(m_colBeingResized, xPhysical));
void wxHeaderCtrl::StartReordering(unsigned int col, int xPhysical)
{
- wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_BEGIN_REORDER, GetId());
+ wxHeaderCtrlEvent event(wxEVT_HEADER_BEGIN_REORDER, GetId());
event.SetEventObject(this);
event.SetColumn(col);
SetCursor(wxCursor(wxCURSOR_HAND));
CaptureMouse();
- UpdateReorderingMarker(xPhysical);
+ // 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
}
-void wxHeaderCtrl::EndReordering(int xPhysical)
+bool wxHeaderCtrl::EndReordering(int xPhysical)
{
wxASSERT_MSG( IsReordering(), "shouldn't be called if we're not reordering" );
ReleaseMouse();
- wxHeaderCtrlEvent event(wxEVT_COMMAND_HEADER_END_REORDER, GetId());
- event.SetEventObject(this);
- event.SetColumn(m_colBeingReordered);
+ const int colOld = m_colBeingReordered,
+ colNew = FindColumnAtPoint(xPhysical);
- const unsigned pos = GetColumnPos(FindColumnAtPoint(xPhysical));
- event.SetNewOrder(pos);
+ m_colBeingReordered = COL_NONE;
- if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
+ if ( xPhysical - GetColStart(colOld) == m_dragOffset )
+ return false;
+
+ if ( colNew != colOld )
{
- // do reorder the columns
- DoMoveCol(m_colBeingReordered, pos);
+ wxHeaderCtrlEvent event(wxEVT_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);
+ }
}
- m_colBeingReordered = COL_NONE;
+ // 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;
}
// ----------------------------------------------------------------------------
void wxHeaderCtrl::DoMoveCol(unsigned int idx, unsigned int pos)
{
- const unsigned count = m_colIndices.size();
-
- wxArrayInt colIndices;
- colIndices.reserve(count);
- for ( unsigned n = 0; n < count; n++ )
- {
- // NB: order of checks is important for this to work when the new
- // column position is the same as the old one
-
- // insert the column at its new position
- if ( colIndices.size() == pos )
- colIndices.push_back(idx);
-
- // delete the column from its old position
- const unsigned idxOld = m_colIndices[n];
- if ( idxOld == idx )
- continue;
-
- colIndices.push_back(idxOld);
- }
-
- m_colIndices = colIndices;
+ MoveColumnInOrderArray(m_colIndices, idx, pos);
Refresh();
}
int w, h;
GetClientSize(&w, &h);
+#ifdef __WXGTK__
+// int vw;
+// GetVirtualSize(&vw, NULL);
+#endif
+
wxAutoBufferedPaintDC dc(this);
dc.SetBackground(GetBackgroundColour());
if ( col.IsHidden() )
continue;
- const int colWidth = col.GetWidth();
+ int colWidth = col.GetWidth();
wxHeaderSortIconType sortArrow;
if ( col.IsSortKey() )
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,
if ( IsReordering() )
{
- if ( mevent.LeftUp() )
- EndReordering(xPhysical);
- else // update the column position
+ if ( !mevent.LeftUp() )
+ {
+ // update the column position
UpdateReorderingMarker(xPhysical);
- return;
+ return;
+ }
+
+ // finish reordering and continue to generate a click event below if we
+ // didn't really reorder anything
+ if ( EndReordering(xPhysical) )
+ return;
}
// treat left double clicks on separator specially
if ( onSeparator && dblclk )
{
- evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
+ evtType = wxEVT_HEADER_SEPARATOR_DCLICK;
}
else // not double click on separator
{
- evtType = click ? wxEVT_COMMAND_HEADER_CLICK
- : wxEVT_COMMAND_HEADER_DCLICK;
+ evtType = click ? wxEVT_HEADER_CLICK
+ : wxEVT_HEADER_DCLICK;
}
break;
case wxMOUSE_BTN_RIGHT:
- evtType = click ? wxEVT_COMMAND_HEADER_RIGHT_CLICK
- : wxEVT_COMMAND_HEADER_RIGHT_DCLICK;
+ evtType = click ? wxEVT_HEADER_RIGHT_CLICK
+ : wxEVT_HEADER_RIGHT_DCLICK;
break;
case wxMOUSE_BTN_MIDDLE:
- evtType = click ? wxEVT_COMMAND_HEADER_MIDDLE_CLICK
- : wxEVT_COMMAND_HEADER_MIDDLE_DCLICK;
+ evtType = click ? wxEVT_HEADER_MIDDLE_CLICK
+ : wxEVT_HEADER_MIDDLE_DCLICK;
break;
default:
}
#endif // wxHAS_GENERIC_HEADERCTRL
+
+#endif // wxUSE_HEADERCTRL