1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/headerctrl.cpp 
   3 // Purpose:     implementation of wxHeaderCtrl for wxMSW 
   4 // Author:      Vadim Zeitlin 
   7 // Copyright:   (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org> 
   8 // Licence:     wxWindows licence 
   9 /////////////////////////////////////////////////////////////////////////////// 
  11 // ============================================================================ 
  13 // ============================================================================ 
  15 // ---------------------------------------------------------------------------- 
  17 // ---------------------------------------------------------------------------- 
  19 // for compilers that support precompilation, includes "wx.h". 
  20 #include "wx/wxprec.h" 
  30 #include "wx/headerctrl.h" 
  32 #ifndef wxHAS_GENERIC_HEADERCTRL 
  34 #include "wx/imaglist.h" 
  36 #include "wx/msw/wrapcctl.h" 
  37 #include "wx/msw/private.h" 
  39 // from src/msw/listctrl.cpp 
  40 extern int WXDLLIMPEXP_CORE 
wxMSWGetColumnClicked(NMHDR 
*nmhdr
, POINT 
*ptClick
); 
  42 // ============================================================================ 
  43 // wxHeaderCtrl implementation 
  44 // ============================================================================ 
  46 // ---------------------------------------------------------------------------- 
  47 // wxHeaderCtrl construction/destruction 
  48 // ---------------------------------------------------------------------------- 
  50 void wxHeaderCtrl::Init() 
  55 bool wxHeaderCtrl::Create(wxWindow 
*parent
, 
  62     // notice that we don't need InitCommonControlsEx(ICC_LISTVIEW_CLASSES) 
  63     // here as we already call InitCommonControls() in wxApp initialization 
  64     // code which covers this 
  66     if ( !CreateControl(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) ) 
  69     if ( !MSWCreateControl(WC_HEADER
, _T(""), pos
, size
) ) 
  75 WXDWORD 
wxHeaderCtrl::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
  77     WXDWORD msStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
  79     if ( style 
& wxHD_DRAGDROP 
) 
  80         msStyle 
|= HDS_DRAGDROP
; 
  82     // the control looks nicer with these styles and there doesn't seem to be 
  83     // any reason to not use them so we always do (as for HDS_HORZ it is 0 
  84     // anyhow but include it for clarity) 
  85     msStyle 
|= HDS_HORZ 
| HDS_BUTTONS 
| HDS_FLAT 
| HDS_FULLDRAG 
| HDS_HOTTRACK
; 
  90 wxHeaderCtrl::~wxHeaderCtrl() 
  95 // ---------------------------------------------------------------------------- 
  96 // wxHeaderCtrl scrolling 
  97 // ---------------------------------------------------------------------------- 
  99 void wxHeaderCtrl::DoScrollHorz(int dx
) 
 101     // as the native control doesn't support offsetting its contents, we use a 
 102     // hack here to make it appear correctly when the parent is scrolled: 
 103     // instead of scrolling or repainting we simply move the control window 
 104     // itself: to be precise, offset it by the scroll increment to the left and 
 105     // increment its width to still extend to the right boundary to compensate 
 106     // for it (notice that dx is negative when scrolling to the right) 
 107     SetSize(GetPosition().x 
+ dx
, -1, GetSize().x 
- dx
, -1, wxSIZE_USE_EXISTING
); 
 110 // ---------------------------------------------------------------------------- 
 111 // wxHeaderCtrl geometry calculation 
 112 // ---------------------------------------------------------------------------- 
 114 wxSize 
wxHeaderCtrl::DoGetBestSize() const 
 116     RECT rc 
= wxGetClientRect(GetHwndOf(GetParent())); 
 118     HDLAYOUT layout 
= { &rc
, &wpos 
}; 
 119     if ( !Header_Layout(GetHwnd(), &layout
) ) 
 121         wxLogLastError(_T("Header_Layout")); 
 122         return wxControl::DoGetBestSize(); 
 125     return wxSize(wpos
.cx
, wpos
.cy
); 
 128 // ---------------------------------------------------------------------------- 
 129 // wxHeaderCtrl columns managements 
 130 // ---------------------------------------------------------------------------- 
 132 unsigned int wxHeaderCtrl::DoGetCount() const 
 134     return Header_GetItemCount(GetHwnd()); 
 137 void wxHeaderCtrl::DoSetCount(unsigned int count
) 
 141     // first delete all old columns 
 142     const unsigned countOld 
= DoGetCount(); 
 143     for ( n 
= 0; n 
< countOld
; n
++ ) 
 145         if ( !Header_DeleteItem(GetHwnd(), 0) ) 
 147             wxLogLastError(_T("Header_DeleteItem")); 
 151     // and add the new ones 
 152     for ( n 
= 0; n 
< count
; n
++ ) 
 154         DoSetOrInsertItem(Insert
, n
); 
 158 void wxHeaderCtrl::DoUpdate(unsigned int idx
) 
 160     DoSetOrInsertItem(Set
, idx
); 
 163 void wxHeaderCtrl::DoSetOrInsertItem(Operation oper
, unsigned int idx
) 
 165     const wxHeaderColumnBase
& col 
= GetColumn(idx
); 
 169     // notice that we need to store the string we use the pointer to until we 
 170     // pass it to the control 
 172     if ( !col
.GetTitle().empty() ) 
 174         hdi
.mask 
|= HDI_TEXT
; 
 176         buf 
= col
.GetTitle().wx_str(); 
 177         hdi
.pszText 
= buf
.data(); 
 178         hdi
.cchTextMax 
= wxStrlen(buf
); 
 181     const wxBitmap bmp 
= col
.GetBitmap(); 
 184         const int bmpWidth 
= bmp
.GetWidth(), 
 185                   bmpHeight 
= bmp
.GetHeight(); 
 189             m_imageList 
= new wxImageList(bmpWidth
, bmpHeight
); 
 190             Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList
)); 
 192         else // already have an image list 
 194             // check that all bitmaps we use have the same size 
 197             m_imageList
->GetSize(0, imageWidth
, imageHeight
); 
 199             wxASSERT_MSG( imageWidth 
== bmpWidth 
&& imageHeight 
== bmpHeight
, 
 200                           "all column bitmaps must have the same size" ); 
 203         m_imageList
->Add(bmp
); 
 204         hdi
.mask 
|= HDI_IMAGE
; 
 205         hdi
.iImage 
= m_imageList
->GetImageCount() - 1; 
 208     if ( col
.GetAlignment() != wxALIGN_NOT 
) 
 210         hdi
.mask 
|= HDI_FORMAT
; 
 211         switch ( col
.GetAlignment() ) 
 218             case wxALIGN_CENTER_HORIZONTAL
: 
 219                 hdi
.fmt 
|= HDF_CENTER
; 
 223                 hdi
.fmt 
|= HDF_RIGHT
; 
 227                 wxFAIL_MSG( "invalid column header alignment" ); 
 231     if ( col
.IsSortKey() ) 
 233         hdi
.mask 
|= HDI_FORMAT
; 
 234         hdi
.fmt 
|= col
.IsSortOrderAscending() ? HDF_SORTUP 
: HDF_SORTDOWN
; 
 237     if ( col
.GetWidth() != wxCOL_WIDTH_DEFAULT 
|| col
.IsHidden() ) 
 239         hdi
.mask 
|= HDI_WIDTH
; 
 240         hdi
.cxy 
= col
.IsHidden() ? 0 : col
.GetWidth(); 
 243     const LRESULT rc 
= ::SendMessage(GetHwnd(), 
 244                                      oper 
== Set 
? HDM_SETITEM 
: HDM_INSERTITEM
, 
 250             wxLogLastError(_T("Header_SetItem()")); 
 255             wxLogLastError(_T("Header_InsertItem()")); 
 259 // ---------------------------------------------------------------------------- 
 260 // wxHeaderCtrl events 
 261 // ---------------------------------------------------------------------------- 
 263 wxEventType 
wxHeaderCtrl::GetClickEventType(bool dblclk
, int button
) 
 269             evtType 
= dblclk 
? wxEVT_COMMAND_HEADER_DCLICK
 
 270                              : wxEVT_COMMAND_HEADER_CLICK
; 
 274             evtType 
= dblclk 
? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
 
 275                              : wxEVT_COMMAND_HEADER_RIGHT_CLICK
; 
 279             evtType 
= dblclk 
? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
 
 280                              : wxEVT_COMMAND_HEADER_MIDDLE_CLICK
; 
 284             wxFAIL_MSG( wxS("unexpected event type") ); 
 285             evtType 
= wxEVT_NULL
; 
 291 bool wxHeaderCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
 293     NMHEADER 
* const nmhdr 
= (NMHEADER 
*)lParam
; 
 295     wxEventType evtType 
= wxEVT_NULL
; 
 296     int idx 
= nmhdr
->iItem
; 
 298     switch ( const UINT code 
= nmhdr
->hdr
.code 
) 
 304         case HDN_ITEMDBLCLICK
: 
 305             evtType 
= GetClickEventType(code 
== HDN_ITEMDBLCLICK
, nmhdr
->iButton
); 
 308             // although we should get the notifications about the right clicks 
 309             // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't 
 310             // happen in practice on any Windows system up to 2003 
 315                 idx 
= wxMSWGetColumnClicked(&nmhdr
->hdr
, &pt
); 
 316                 if ( idx 
!= wxNOT_FOUND 
) 
 317                     evtType 
= GetClickEventType(code 
== NM_RDBLCLK
, 1); 
 318                 //else: ignore clicks outside any column 
 322         case HDN_DIVIDERDBLCLICK
: 
 323             evtType 
= wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
; 
 327         // column resizing events 
 328         // ---------------------- 
 330         // see comments in wxListCtrl::MSWOnNotify() for why we catch both 
 331         // ASCII and Unicode versions of this message 
 332         case HDN_BEGINTRACKA
: 
 333         case HDN_BEGINTRACKW
: 
 334             evtType 
= wxEVT_COMMAND_HEADER_BEGIN_DRAG
; 
 339             if ( evtType 
== wxEVT_NULL 
) 
 340                 evtType 
= wxEVT_COMMAND_HEADER_DRAGGING
; 
 345             if ( evtType 
== wxEVT_NULL 
) 
 346                 evtType 
= wxEVT_COMMAND_HEADER_END_DRAG
; 
 348             width 
= nmhdr
->pitem
->cxy
; 
 353     // do generate the corresponding wx event 
 354     if ( evtType 
!= wxEVT_NULL 
) 
 356         wxHeaderCtrlEvent 
event(evtType
, GetId()); 
 357         event
.SetEventObject(this); 
 358         event
.SetColumn(idx
); 
 359         event
.SetWidth(width
); 
 361         if ( GetEventHandler()->ProcessEvent(event
) ) 
 363             if ( !event
.IsAllowed() ) 
 365                 // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING 
 366                 // interpret TRUE return value as meaning to stop the control 
 367                 // default handling of the message 
 375     return wxHeaderCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
); 
 378 #endif // wxHAS_GENERIC_HEADERCTRL