]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/headerctrl.cpp
use a slightly less ugly way to conditionally suppress unused parameter warnings
[wxWidgets.git] / src / msw / headerctrl.cpp
index 6330b225e1e9f3e0efc8434df9ca9e74ce3aaa6e..d6ad5ccddaad6e63e8d7e36efe1fbf91b81c7be7 100644 (file)
 #endif // WX_PRECOMP
 
 #include "wx/headerctrl.h"
+
+#ifndef wxHAS_GENERIC_HEADERCTRL
+
 #include "wx/imaglist.h"
 
 #include "wx/msw/wrapcctl.h"
 #include "wx/msw/private.h"
 
+// from src/msw/listctrl.cpp
+extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
+
 // ============================================================================
 // wxHeaderCtrl implementation
 // ============================================================================
@@ -90,22 +96,14 @@ wxHeaderCtrl::~wxHeaderCtrl()
 // wxHeaderCtrl scrolling
 // ----------------------------------------------------------------------------
 
-// as the native control doesn't support offsetting its contents, we use a hack
-// here to make it appear correctly when the parent is scrolled: instead of
-// scrolling or repainting we simply move the control window itself
-void wxHeaderCtrl::ScrollWindow(int dx,
-                                int WXUNUSED_UNLESS_DEBUG(dy),
-                                const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
+void wxHeaderCtrl::DoScrollHorz(int dx)
 {
-    // this doesn't make sense at all
-    wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
-
-    // this would actually be nice to support for "frozen" headers
-    wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
-
-    // offset the window by the scroll increment to the left and increment its
-    // width to still extend to the right boundary to compensate for it (notice
-    // that dx is negative when scrolling to the right)
+    // as the native control doesn't support offsetting its contents, we use a
+    // hack here to make it appear correctly when the parent is scrolled:
+    // instead of scrolling or repainting we simply move the control window
+    // itself: to be precise, offset it by the scroll increment to the left and
+    // increment its width to still extend to the right boundary to compensate
+    // for it (notice that dx is negative when scrolling to the right)
     SetSize(GetPosition().x + dx, -1, GetSize().x - dx, -1, wxSIZE_USE_EXISTING);
 }
 
@@ -136,104 +134,281 @@ unsigned int wxHeaderCtrl::DoGetCount() const
     return Header_GetItemCount(GetHwnd());
 }
 
-void wxHeaderCtrl::DoInsert(const wxHeaderColumn& col, unsigned int idx)
+void wxHeaderCtrl::DoSetCount(unsigned int count)
 {
-    // copy the HDITEM because we may modify it below
-    HDITEM hdi = col.GetHDI();
+    unsigned n;
+
+    // first delete all old columns
+    const unsigned countOld = DoGetCount();
+    for ( n = 0; n < countOld; n++ )
+    {
+        if ( !Header_DeleteItem(GetHwnd(), 0) )
+        {
+            wxLogLastError(_T("Header_DeleteItem"));
+        }
+    }
+
+    // and add the new ones
+    for ( n = 0; n < count; n++ )
+    {
+        DoInsertItem(n);
+    }
+}
+
+void wxHeaderCtrl::DoUpdate(unsigned int idx)
+{
+    // the native control does provide Header_SetItem() but it's inconvenient
+    // to use it because it sends HDN_ITEMCHANGING messages and we'd have to
+    // arrange not to block setting the width from there and the logic would be
+    // more complicated as we'd have to reset the old values as well as setting
+    // the new ones -- so instead just recreate the column
+    Header_DeleteItem(GetHwnd(), idx);
+    DoInsertItem(idx);
+}
+
+void wxHeaderCtrl::DoInsertItem(unsigned int idx)
+{
+    const wxHeaderColumnBase& col = GetColumn(idx);
+
+    wxHDITEM hdi;
+
+    // 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();
+    hdi.pszText = buf.data();
+    hdi.cchTextMax = wxStrlen(buf);
 
     const wxBitmap bmp = col.GetBitmap();
     if ( bmp.IsOk() )
     {
-        const int bmpWidth = bmp.GetWidth(),
-                  bmpHeight = bmp.GetHeight();
+        hdi.mask |= HDI_IMAGE;
 
-        if ( !m_imageList )
+        if ( bmp.IsOk() )
         {
-            m_imageList = new wxImageList(bmpWidth, bmpHeight);
-            Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
+            const int bmpWidth = bmp.GetWidth(),
+                      bmpHeight = bmp.GetHeight();
+
+            if ( !m_imageList )
+            {
+                m_imageList = new wxImageList(bmpWidth, bmpHeight);
+                Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
+            }
+            else // already have an image list
+            {
+                // check that all bitmaps we use have the same size
+                int imageWidth,
+                    imageHeight;
+                m_imageList->GetSize(0, imageWidth, imageHeight);
+
+                wxASSERT_MSG( imageWidth == bmpWidth && imageHeight == bmpHeight,
+                              "all column bitmaps must have the same size" );
+            }
+
+            m_imageList->Add(bmp);
+            hdi.iImage = m_imageList->GetImageCount() - 1;
         }
-        else // already have an image list
+        else // no bitmap but we still need to update the item
         {
-            // check that all bitmaps we use have the same size
-            int imageWidth,
-                imageHeight;
-            m_imageList->GetSize(0, imageWidth, imageHeight);
-
-            wxASSERT_MSG( imageWidth == bmpWidth && imageHeight == bmpHeight,
-                          "all column bitmaps must have the same size" );
+            hdi.iImage = I_IMAGENONE;
         }
+    }
 
-        m_imageList->Add(bmp);
-        hdi.mask |= HDI_IMAGE;
-        hdi.iImage = m_imageList->GetImageCount() - 1;
+    if ( col.GetAlignment() != wxALIGN_NOT )
+    {
+        hdi.mask |= HDI_FORMAT;
+        switch ( col.GetAlignment() )
+        {
+            case wxALIGN_LEFT:
+                hdi.fmt |= HDF_LEFT;
+                break;
+
+            case wxALIGN_CENTER:
+            case wxALIGN_CENTER_HORIZONTAL:
+                hdi.fmt |= HDF_CENTER;
+                break;
+
+            case wxALIGN_RIGHT:
+                hdi.fmt |= HDF_RIGHT;
+                break;
+
+            default:
+                wxFAIL_MSG( "invalid column header alignment" );
+        }
     }
 
-    if ( col.IsHidden() )
+    if ( col.IsSortKey() )
     {
-        hdi.mask |= HDI_WIDTH;
-        hdi.cxy = 0;
+        hdi.mask |= HDI_FORMAT;
+        hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
     }
 
-    if ( Header_InsertItem(GetHwnd(), idx, &hdi) == -1 )
+    if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT || col.IsHidden() )
     {
-        wxLogLastError(_T("Header_InsertItem"));
+        hdi.mask |= HDI_WIDTH;
+        hdi.cxy = col.IsHidden() ? 0 : col.GetWidth();
     }
-}
 
-void wxHeaderCtrl::DoDelete(unsigned int idx)
-{
-    if ( !Header_DeleteItem(GetHwnd(), idx) )
+    if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM, idx, (LPARAM)&hdi) == -1 )
     {
-        wxLogLastError(_T("Header_DeleteItem"));
+        wxLogLastError(_T("Header_InsertItem()"));
     }
 }
 
 // ----------------------------------------------------------------------------
-// wxHeaderCtrl columns attributes
+// wxHeaderCtrl events
 // ----------------------------------------------------------------------------
 
-void wxHeaderCtrl::DoShowColumn(unsigned int idx, bool show)
+wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
 {
-    wxHDITEM hdi;
-    hdi.mask = HDI_WIDTH;
-
-    if ( !Header_GetItem(GetHwnd(), idx, &hdi) )
+    wxEventType evtType;
+    switch ( button )
     {
-        wxLogLastError(_T("Header_GetItem(HDI_WIDTH)"));
-        return;
+        case 0:
+            evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK
+                             : wxEVT_COMMAND_HEADER_CLICK;
+            break;
+
+        case 1:
+            evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
+                             : wxEVT_COMMAND_HEADER_RIGHT_CLICK;
+            break;
+
+        case 2:
+            evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
+                             : wxEVT_COMMAND_HEADER_MIDDLE_CLICK;
+            break;
+
+        default:
+            wxFAIL_MSG( wxS("unexpected event type") );
+            evtType = wxEVT_NULL;
     }
 
-    if ( show )
-        hdi.cxy = 80; // FIXME: we don't have the column width here any more
-    else
-        hdi.cxy = 0;
-
-    if ( !Header_SetItem(GetHwnd(), idx, &hdi) )
-    {
-        wxLogLastError(_T("Header_SetItem(HDI_WIDTH)"));
-        return;
-    }
+    return evtType;
 }
 
-void wxHeaderCtrl::DoShowSortIndicator(unsigned int idx, int sortOrder)
+bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 {
-    wxHDITEM hdi;
-    hdi.mask = HDI_FORMAT;
-
-    if ( !Header_GetItem(GetHwnd(), idx, &hdi) )
+    NMHEADER * const nmhdr = (NMHEADER *)lParam;
+
+    wxEventType evtType = wxEVT_NULL;
+    int idx = nmhdr->iItem;
+    int width = 0;
+    bool cancelled = false;
+    const UINT code = nmhdr->hdr.code;
+    switch ( code )
     {
-        wxLogLastError(_T("Header_GetItem(HDI_FORMAT)"));
-        return;
+        // click events
+        // ------------
+
+        case HDN_ITEMCLICK:
+        case HDN_ITEMDBLCLICK:
+            evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
+            break;
+
+            // although we should get the notifications about the right clicks
+            // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't
+            // happen in practice on any Windows system up to 2003
+        case NM_RCLICK:
+        case NM_RDBLCLK:
+            {
+                POINT pt;
+                idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
+                if ( idx != wxNOT_FOUND )
+                    evtType = GetClickEventType(code == NM_RDBLCLK, 1);
+                //else: ignore clicks outside any column
+            }
+            break;
+
+        case HDN_DIVIDERDBLCLICK:
+            evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
+            break;
+
+
+        // column resizing events
+        // ----------------------
+
+        // see comments in wxListCtrl::MSWOnNotify() for why we catch both
+        // 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
+            // even generate any events for them
+            if ( !GetColumn(idx).IsResizeable() )
+            {
+                *result = TRUE;
+
+                return true;
+            }
+
+            evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
+            // fall through
+
+        case HDN_TRACKA:
+        case HDN_TRACKW:
+            if ( evtType == wxEVT_NULL )
+                evtType = wxEVT_COMMAND_HEADER_RESIZING;
+            // fall through
+
+        case HDN_ENDTRACKA:
+        case HDN_ENDTRACKW:
+            width = nmhdr->pitem->cxy;
+
+            if ( evtType == wxEVT_NULL )
+            {
+                evtType = wxEVT_COMMAND_HEADER_END_RESIZE;
+
+                // don't generate events with invalid width
+                const int minWidth = GetColumn(idx).GetMinWidth();
+                if ( width < minWidth )
+                    width = minWidth;
+            }
+            break;
+
+        case HDN_ITEMCHANGING:
+            if ( nmhdr->pitem && (nmhdr->pitem->mask & HDI_WIDTH) )
+            {
+                // prevent the column from being shrunk beneath its min width
+                if ( nmhdr->pitem->cxy < GetColumn(idx).GetMinWidth() )
+                {
+                    *result = TRUE;
+
+                    return true;
+                }
+            }
+            break;
+
+        case NM_RELEASEDCAPTURE:
+            cancelled = true;
+            break;
     }
 
-    if ( sortOrder == -1 )
-        hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
-    else
-        hdi.fmt |= sortOrder ? HDF_SORTUP : HDF_SORTDOWN;
 
-    if ( !Header_SetItem(GetHwnd(), idx, &hdi) )
+    // do generate the corresponding wx event
+    if ( evtType != wxEVT_NULL )
     {
-        wxLogLastError(_T("Header_SetItem(HDI_FORMAT)"));
+        wxHeaderCtrlEvent event(evtType, GetId());
+        event.SetEventObject(this);
+        event.SetColumn(idx);
+        event.SetWidth(width);
+        if ( cancelled )
+            event.SetCancelled();
+
+        if ( GetEventHandler()->ProcessEvent(event) )
+        {
+            if ( !event.IsAllowed() )
+            {
+                // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
+                // interpret TRUE return value as meaning to stop the control
+                // default handling of the message
+                *result = TRUE;
+            }
+
+            return true;
+        }
     }
+
+    return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
 }
 
+#endif // wxHAS_GENERIC_HEADERCTRL