#pragma hdrstop
#endif
+#if wxUSE_HEADERCTRL
+
#ifndef WX_PRECOMP
+ #include "wx/app.h"
#include "wx/log.h"
#endif // WX_PRECOMP
#include "wx/msw/wrapcctl.h"
#include "wx/msw/private.h"
+#ifndef HDM_SETBITMAPMARGIN
+ #define HDM_SETBITMAPMARGIN 0x1234
+#endif
+
+#ifndef Header_SetBitmapMargin
+ #define Header_SetBitmapMargin(hwnd, margin) \
+ ::SendMessage((hwnd), HDM_SETBITMAPMARGIN, (WPARAM)(margin), 0)
+#endif
+
// from src/msw/listctrl.cpp
extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
m_numColumns = 0;
m_imageList = NULL;
m_scrollOffset = 0;
+ m_colBeingDragged = -1;
}
bool wxHeaderCtrl::Create(wxWindow *parent,
if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
return false;
- if ( !MSWCreateControl(WC_HEADER, _T(""), pos, size) )
+ if ( !MSWCreateControl(WC_HEADER, wxT(""), pos, size) )
return false;
+ // special hack for margins when using comctl32.dll v6 or later: the
+ // default margin is too big and results in label truncation when the
+ // column width is just about right to show it together with the sort
+ // indicator, so reduce it to a smaller value (in principle we could even
+ // use 0 here but this starts to look ugly)
+ if ( wxApp::GetComCtl32Version() >= 600 )
+ {
+ Header_SetBitmapMargin(GetHwnd(), ::GetSystemMetrics(SM_CXEDGE));
+ }
+
return true;
}
{
WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
- if ( style & wxHD_DRAGDROP )
+ if ( style & wxHD_ALLOW_REORDER )
msStyle |= HDS_DRAGDROP;
// the control looks nicer with these styles and there doesn't seem to be
// any reason to not use them so we always do (as for HDS_HORZ it is 0
// anyhow but include it for clarity)
- msStyle |= HDS_HORZ | HDS_BUTTONS | HDS_FLAT | HDS_FULLDRAG | HDS_HOTTRACK;
+ // NOTE: don't use however HDS_FLAT because it makes the control look
+ // non-native when running WinXP in classic mode
+ msStyle |= HDS_HORZ | HDS_BUTTONS | HDS_FULLDRAG | HDS_HOTTRACK;
return msStyle;
}
HDLAYOUT layout = { &rc, &wpos };
if ( !Header_Layout(GetHwnd(), &layout) )
{
- wxLogLastError(_T("Header_Layout"));
+ wxLogLastError(wxT("Header_Layout"));
return wxControl::DoGetBestSize();
}
{
if ( !Header_DeleteItem(GetHwnd(), 0) )
{
- wxLogLastError(_T("Header_DeleteItem"));
+ wxLogLastError(wxT("Header_DeleteItem"));
}
}
// notice that we need to store the string we use the pointer to until we
// pass it to the control
hdi.mask |= HDI_TEXT;
- wxWxCharBuffer buf = col.GetTitle().wx_str();
+ wxWxCharBuffer buf = col.GetTitle().t_str();
hdi.pszText = buf.data();
hdi.cchTextMax = wxStrlen(buf);
if ( !m_imageList )
{
m_imageList = new wxImageList(bmpWidth, bmpHeight);
+ (void) // suppress mingw32 warning about unused computed value
Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
}
else // already have an image list
if ( col.GetAlignment() != wxALIGN_NOT )
{
- hdi.mask |= HDI_FORMAT;
+ hdi.mask |= HDI_FORMAT | HDF_LEFT;
switch ( col.GetAlignment() )
{
case wxALIGN_LEFT:
}
hdi.mask |= HDI_ORDER;
- hdi.iOrder = m_colIndices.Index(idx);
+ hdi.iOrder = MSWToNativeOrder(m_colIndices.Index(idx));
if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM,
MSWToNativeIdx(idx), (LPARAM)&hdi) == -1 )
{
- wxLogLastError(_T("Header_InsertItem()"));
+ wxLogLastError(wxT("Header_InsertItem()"));
}
}
if ( !Header_SetOrderArray(GetHwnd(), orderShown.size(), &orderShown[0]) )
{
- wxLogLastError(_T("Header_GetOrderArray"));
+ wxLogLastError(wxT("Header_GetOrderArray"));
}
m_colIndices = order;
return m_colIndices;
}
+// ----------------------------------------------------------------------------
+// wxHeaderCtrl indexes and positions translation
+// ----------------------------------------------------------------------------
+
int wxHeaderCtrl::MSWToNativeIdx(int idx)
{
// don't check for GetColumn(idx).IsShown() as it could have just became
"column must be visible to have an "
"index in the native control" );
+ int item = idx;
for ( int i = 0; i < idx; i++ )
{
if ( GetColumn(i).IsHidden() )
- idx--; // one less column the native control knows about
+ item--; // one less column the native control knows about
}
- return idx;
+ wxASSERT_MSG( item >= 0 && item <= GetShownColumnsCount(), "logic error" );
+
+ return item;
}
int wxHeaderCtrl::MSWFromNativeIdx(int item)
"column index out of range" );
// reverse the above function
- for ( int i = 0; i <= item; i++ )
+
+ unsigned idx = item;
+ for ( unsigned n = 0; n < m_numColumns; n++ )
{
- if ( GetColumn(i).IsHidden() )
- item++;
+ if ( n > idx )
+ break;
+
+ if ( GetColumn(n).IsHidden() )
+ idx++;
}
- return item;
+ wxASSERT_MSG( MSWToNativeIdx(idx) == item, "logic error" );
+
+ return idx;
}
-int wxHeaderCtrl::MSWFromNativeOrder(int order)
+int wxHeaderCtrl::MSWToNativeOrder(int pos)
{
- wxASSERT_MSG( order >= 0 && order < GetShownColumnsCount(),
+ wxASSERT_MSG( pos >= 0 && static_cast<unsigned>(pos) < m_numColumns,
"column position out of range" );
- // notice that the loop condition is inclusive because if the column
- // exactly at position order is hidden we should go to the next one
- for ( int pos = 0; pos <= order; pos++ )
+ int order = pos;
+ for ( int n = 0; n < pos; n++ )
{
- if ( GetColumn(m_colIndices[pos]).IsHidden() )
- {
- // order can't become greater than m_numColumns here because it is
- // less than the number of shown columns initially and it is going
- // to be incremented at most once for every hidden column and
- // m_numColumns is the total columns number
- order++;
- }
+ if ( GetColumn(m_colIndices[n]).IsHidden() )
+ order--;
}
+ wxASSERT_MSG( order >= 0 && order <= GetShownColumnsCount(), "logic error" );
+
return order;
}
+int wxHeaderCtrl::MSWFromNativeOrder(int order)
+{
+ wxASSERT_MSG( order >= 0 && order < GetShownColumnsCount(),
+ "native column position out of range" );
+
+ unsigned pos = order;
+ for ( unsigned n = 0; n < m_numColumns; n++ )
+ {
+ if ( n > pos )
+ break;
+
+ if ( GetColumn(m_colIndices[n]).IsHidden() )
+ pos++;
+ }
+
+ wxASSERT_MSG( MSWToNativeOrder(pos) == order, "logic error" );
+
+ return pos;
+}
+
// ----------------------------------------------------------------------------
// wxHeaderCtrl events
// ----------------------------------------------------------------------------
switch ( button )
{
case 0:
- evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK
- : wxEVT_COMMAND_HEADER_CLICK;
+ evtType = dblclk ? wxEVT_HEADER_DCLICK
+ : wxEVT_HEADER_CLICK;
break;
case 1:
- evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
- : wxEVT_COMMAND_HEADER_RIGHT_CLICK;
+ evtType = dblclk ? wxEVT_HEADER_RIGHT_DCLICK
+ : wxEVT_HEADER_RIGHT_CLICK;
break;
case 2:
- evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
- : wxEVT_COMMAND_HEADER_MIDDLE_CLICK;
+ evtType = dblclk ? wxEVT_HEADER_MIDDLE_DCLICK
+ : wxEVT_HEADER_MIDDLE_CLICK;
break;
default:
case HDN_ITEMCLICK:
case HDN_ITEMDBLCLICK:
evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
+
+ // We're not dragging any more.
+ m_colBeingDragged = -1;
break;
// although we should get the notifications about the right clicks
if ( idx != wxNOT_FOUND )
{
idx = MSWFromNativeIdx(idx);
- evtType = GetClickEventType(code == NM_RDBLCLK, 1);
+
+ // due to a bug in mingw32 headers NM_RDBLCLK is signed
+ // there so we need a cast to avoid warnings about signed/
+ // unsigned comparison
+ evtType = GetClickEventType(
+ code == static_cast<UINT>(NM_RDBLCLK), 1);
}
//else: ignore clicks outside any column
}
break;
case HDN_DIVIDERDBLCLICK:
- evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
+ evtType = wxEVT_HEADER_SEPARATOR_DCLICK;
break;
// ASCII and Unicode versions of this message
case HDN_BEGINTRACKA:
case HDN_BEGINTRACKW:
- // non-resizeable columns can't be resized no matter what, don't
+ // non-resizable columns can't be resized no matter what, don't
// even generate any events for them
if ( !GetColumn(idx).IsResizeable() )
{
break;
}
- evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
+ evtType = wxEVT_HEADER_BEGIN_RESIZE;
// fall through
case HDN_ENDTRACKA:
if ( evtType == wxEVT_NULL )
{
- evtType = wxEVT_COMMAND_HEADER_END_RESIZE;
+ evtType = wxEVT_HEADER_END_RESIZE;
// don't generate events with invalid width
const int minWidth = GetColumn(idx).GetMinWidth();
}
break;
+ // The control is not supposed to send HDN_TRACK when using
+ // HDS_FULLDRAG (which we do use) but apparently some versions of
+ // comctl32.dll still do it, see #13506, so catch both messages
+ // just in case we are dealing with one of these buggy versions.
+ case HDN_TRACK:
case HDN_ITEMCHANGING:
if ( nmhdr->pitem && (nmhdr->pitem->mask & HDI_WIDTH) )
{
// generate the resizing event from here as we don't seem
// to be getting HDN_TRACK events at all, at least with
// comctl32.dll v6
- evtType = wxEVT_COMMAND_HEADER_RESIZING;
+ evtType = wxEVT_HEADER_RESIZING;
}
}
break;
if ( nmhdr->iItem == -1 )
break;
+ // If we are dragging a column that is not draggable and the mouse
+ // is moved over a different column then we get the column number from
+ // the column under the mouse. This results in an unexpected behaviour
+ // if this column is draggable. To prevent this remember the column we
+ // are dragging for the complete drag and drop cycle.
+ if ( m_colBeingDragged == -1 )
+ {
+ m_colBeingDragged = idx;
+ }
+
// column must have the appropriate flag to be draggable
- if ( !GetColumn(idx).IsReorderable() )
+ if ( !GetColumn(m_colBeingDragged).IsReorderable() )
{
veto = true;
break;
}
- evtType = wxEVT_COMMAND_HEADER_BEGIN_REORDER;
+ evtType = wxEVT_HEADER_BEGIN_REORDER;
break;
case HDN_ENDDRAG:
order = MSWFromNativeOrder(order);
- evtType = wxEVT_COMMAND_HEADER_END_REORDER;
+ evtType = wxEVT_HEADER_END_REORDER;
+
+ // We (successfully) ended dragging the column.
+ m_colBeingDragged = -1;
break;
case NM_RELEASEDCAPTURE:
- evtType = wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED;
+ evtType = wxEVT_HEADER_DRAGGING_CANCELLED;
+
+ // Dragging the column was cancelled.
+ m_colBeingDragged = -1;
break;
}
if ( order != -1 )
event.SetNewOrder(order);
- if ( GetEventHandler()->ProcessEvent(event) )
- {
- if ( event.IsAllowed() )
- return true; // skip default message handling below
+ const bool processed = GetEventHandler()->ProcessEvent(event);
- // we need to veto the default handling of this message, don't
- // return to execute the code in the "if veto" branch below
+ if ( processed && !event.IsAllowed() )
veto = true;
- }
- else // not processed
+
+ if ( !veto )
{
// special post-processing for HDN_ENDDRAG: we need to update the
// internal column indices array if this is allowed to go ahead as
// the native control is going to reorder its columns now
- if ( evtType == wxEVT_COMMAND_HEADER_END_REORDER )
+ if ( evtType == wxEVT_HEADER_END_REORDER )
MoveColumnInOrderArray(m_colIndices, idx, order);
+
+ if ( processed )
+ {
+ // skip default processing below
+ return true;
+ }
}
}
}
#endif // wxHAS_GENERIC_HEADERCTRL
+
+#endif // wxUSE_HEADERCTRL