]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/headerctrl.cpp
missing commit
[wxWidgets.git] / src / msw / headerctrl.cpp
index ef512e0e748808f694037d45b0c792460eb007e9..4845715d03e77bb14133d197a22f009e1f4b8d3e 100644 (file)
     #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);
 
@@ -52,6 +64,7 @@ void wxHeaderCtrl::Init()
     m_numColumns = 0;
     m_imageList = NULL;
     m_scrollOffset = 0;
+    m_colBeingDragged = -1;
 }
 
 bool wxHeaderCtrl::Create(wxWindow *parent,
@@ -68,9 +81,19 @@ 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;
 }
 
@@ -78,13 +101,15 @@ WXDWORD wxHeaderCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
 {
     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;
 }
@@ -132,7 +157,7 @@ wxSize wxHeaderCtrl::DoGetBestSize() const
     HDLAYOUT layout = { &rc, &wpos };
     if ( !Header_Layout(GetHwnd(), &layout) )
     {
-        wxLogLastError(_T("Header_Layout"));
+        wxLogLastError(wxT("Header_Layout"));
         return wxControl::DoGetBestSize();
     }
 
@@ -172,7 +197,7 @@ void wxHeaderCtrl::DoSetCount(unsigned int count)
     {
         if ( !Header_DeleteItem(GetHwnd(), 0) )
         {
-            wxLogLastError(_T("Header_DeleteItem"));
+            wxLogLastError(wxT("Header_DeleteItem"));
         }
     }
 
@@ -244,7 +269,7 @@ void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx)
     // 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);
 
@@ -261,6 +286,7 @@ void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx)
             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
@@ -285,7 +311,7 @@ void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx)
 
     if ( col.GetAlignment() != wxALIGN_NOT )
     {
-        hdi.mask |= HDI_FORMAT;
+        hdi.mask |= HDI_FORMAT | HDF_LEFT;
         switch ( col.GetAlignment() )
         {
             case wxALIGN_LEFT:
@@ -319,12 +345,12 @@ void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx)
     }
 
     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()"));
     }
 }
 
@@ -342,7 +368,7 @@ void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order)
 
     if ( !Header_SetOrderArray(GetHwnd(), orderShown.size(), &orderShown[0]) )
     {
-        wxLogLastError(_T("Header_GetOrderArray"));
+        wxLogLastError(wxT("Header_GetOrderArray"));
     }
 
     m_colIndices = order;
@@ -356,6 +382,10 @@ wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const
     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
@@ -364,13 +394,16 @@ int wxHeaderCtrl::MSWToNativeIdx(int idx)
                   "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)
@@ -379,37 +412,59 @@ 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
 // ----------------------------------------------------------------------------
@@ -420,18 +475,18 @@ wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
     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:
@@ -471,6 +526,9 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         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
@@ -484,14 +542,19 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 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;
 
 
@@ -502,7 +565,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         // 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() )
             {
@@ -510,7 +573,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 break;
             }
 
-            evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
+            evtType = wxEVT_HEADER_BEGIN_RESIZE;
             // fall through
 
         case HDN_ENDTRACKA:
@@ -519,7 +582,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
             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();
@@ -528,6 +591,11 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             }
             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) )
             {
@@ -544,7 +612,7 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     // 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;
@@ -558,14 +626,24 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             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:
@@ -579,11 +657,17 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
             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;
     }
 
@@ -598,22 +682,24 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         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;
+            }
         }
     }
 
@@ -631,3 +717,5 @@ bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 }
 
 #endif // wxHAS_GENERIC_HEADERCTRL
+
+#endif // wxUSE_HEADERCTRL