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"
31 #include "wx/imaglist.h"
33 #include "wx/msw/wrapcctl.h"
34 #include "wx/msw/private.h"
36 // from src/msw/listctrl.cpp
37 extern int WXDLLIMPEXP_CORE
wxMSWGetColumnClicked(NMHDR
*nmhdr
, POINT
*ptClick
);
39 // ============================================================================
40 // wxHeaderCtrl implementation
41 // ============================================================================
43 // ----------------------------------------------------------------------------
44 // wxHeaderCtrl construction/destruction
45 // ----------------------------------------------------------------------------
47 void wxHeaderCtrl::Init()
52 bool wxHeaderCtrl::Create(wxWindow
*parent
,
59 // notice that we don't need InitCommonControlsEx(ICC_LISTVIEW_CLASSES)
60 // here as we already call InitCommonControls() in wxApp initialization
61 // code which covers this
63 if ( !CreateControl(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
66 if ( !MSWCreateControl(WC_HEADER
, _T(""), pos
, size
) )
72 WXDWORD
wxHeaderCtrl::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
74 WXDWORD msStyle
= wxControl::MSWGetStyle(style
, exstyle
);
76 if ( style
& wxHD_DRAGDROP
)
77 msStyle
|= HDS_DRAGDROP
;
79 // the control looks nicer with these styles and there doesn't seem to be
80 // any reason to not use them so we always do (as for HDS_HORZ it is 0
81 // anyhow but include it for clarity)
82 msStyle
|= HDS_HORZ
| HDS_BUTTONS
| HDS_FLAT
| HDS_FULLDRAG
| HDS_HOTTRACK
;
87 wxHeaderCtrl::~wxHeaderCtrl()
92 // ----------------------------------------------------------------------------
93 // wxHeaderCtrl scrolling
94 // ----------------------------------------------------------------------------
96 void wxHeaderCtrl::DoScrollHorz(int dx
)
98 // as the native control doesn't support offsetting its contents, we use a
99 // hack here to make it appear correctly when the parent is scrolled:
100 // instead of scrolling or repainting we simply move the control window
101 // itself: to be precise, offset it by the scroll increment to the left and
102 // increment its width to still extend to the right boundary to compensate
103 // for it (notice that dx is negative when scrolling to the right)
104 SetSize(GetPosition().x
+ dx
, -1, GetSize().x
- dx
, -1, wxSIZE_USE_EXISTING
);
107 // ----------------------------------------------------------------------------
108 // wxHeaderCtrl geometry calculation
109 // ----------------------------------------------------------------------------
111 wxSize
wxHeaderCtrl::DoGetBestSize() const
113 RECT rc
= wxGetClientRect(GetHwndOf(GetParent()));
115 HDLAYOUT layout
= { &rc
, &wpos
};
116 if ( !Header_Layout(GetHwnd(), &layout
) )
118 wxLogLastError(_T("Header_Layout"));
119 return wxControl::DoGetBestSize();
122 return wxSize(wpos
.cx
, wpos
.cy
);
125 // ----------------------------------------------------------------------------
126 // wxHeaderCtrl columns managements
127 // ----------------------------------------------------------------------------
129 unsigned int wxHeaderCtrl::DoGetCount() const
131 return Header_GetItemCount(GetHwnd());
134 void wxHeaderCtrl::DoSetCount(unsigned int count
)
138 // first delete all old columns
139 const unsigned countOld
= DoGetCount();
140 for ( n
= 0; n
< countOld
; n
++ )
142 if ( !Header_DeleteItem(GetHwnd(), 0) )
144 wxLogLastError(_T("Header_DeleteItem"));
148 // and add the new ones
149 for ( n
= 0; n
< count
; n
++ )
151 DoSetOrInsertItem(Insert
, n
);
155 void wxHeaderCtrl::DoUpdate(unsigned int idx
)
157 DoSetOrInsertItem(Set
, idx
);
160 void wxHeaderCtrl::DoSetOrInsertItem(Operation oper
, unsigned int idx
)
162 const wxHeaderColumnBase
& col
= GetColumn(idx
);
166 // notice that we need to store the string we use the pointer to until we
167 // pass it to the control
169 if ( !col
.GetTitle().empty() )
171 hdi
.mask
|= HDI_TEXT
;
173 buf
= col
.GetTitle().wx_str();
174 hdi
.pszText
= buf
.data();
175 hdi
.cchTextMax
= wxStrlen(buf
);
178 const wxBitmap bmp
= col
.GetBitmap();
181 const int bmpWidth
= bmp
.GetWidth(),
182 bmpHeight
= bmp
.GetHeight();
186 m_imageList
= new wxImageList(bmpWidth
, bmpHeight
);
187 Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList
));
189 else // already have an image list
191 // check that all bitmaps we use have the same size
194 m_imageList
->GetSize(0, imageWidth
, imageHeight
);
196 wxASSERT_MSG( imageWidth
== bmpWidth
&& imageHeight
== bmpHeight
,
197 "all column bitmaps must have the same size" );
200 m_imageList
->Add(bmp
);
201 hdi
.mask
|= HDI_IMAGE
;
202 hdi
.iImage
= m_imageList
->GetImageCount() - 1;
205 if ( col
.GetAlignment() != wxALIGN_NOT
)
207 hdi
.mask
|= HDI_FORMAT
;
208 switch ( col
.GetAlignment() )
215 case wxALIGN_CENTER_HORIZONTAL
:
216 hdi
.fmt
|= HDF_CENTER
;
220 hdi
.fmt
|= HDF_RIGHT
;
224 wxFAIL_MSG( "invalid column header alignment" );
228 if ( col
.IsSortKey() )
230 hdi
.mask
|= HDI_FORMAT
;
231 hdi
.fmt
|= col
.IsSortOrderAscending() ? HDF_SORTUP
: HDF_SORTDOWN
;
234 if ( col
.GetWidth() != wxCOL_WIDTH_DEFAULT
|| col
.IsHidden() )
236 hdi
.mask
|= HDI_WIDTH
;
237 hdi
.cxy
= col
.IsHidden() ? 0 : col
.GetWidth();
240 const LRESULT rc
= ::SendMessage(GetHwnd(),
241 oper
== Set
? HDM_SETITEM
: HDM_INSERTITEM
,
247 wxLogLastError(_T("Header_SetItem()"));
252 wxLogLastError(_T("Header_InsertItem()"));
256 // ----------------------------------------------------------------------------
257 // wxHeaderCtrl events
258 // ----------------------------------------------------------------------------
260 bool wxHeaderCtrl::SendClickEvent(bool dblclk
, unsigned int idx
, int button
)
266 evtType
= dblclk
? wxEVT_COMMAND_HEADER_DCLICK
267 : wxEVT_COMMAND_HEADER_CLICK
;
271 evtType
= dblclk
? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
272 : wxEVT_COMMAND_HEADER_RIGHT_CLICK
;
276 evtType
= dblclk
? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
277 : wxEVT_COMMAND_HEADER_MIDDLE_CLICK
;
281 wxFAIL_MSG( wxS("unexpected event type") );
285 wxHeaderCtrlEvent
event(evtType
, GetId());
286 event
.SetEventObject(this);
287 event
.SetColumn(idx
);
289 return GetEventHandler()->ProcessEvent(event
);
292 bool wxHeaderCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
294 NMHEADER
* const nmhdr
= (NMHEADER
*)lParam
;
296 switch ( nmhdr
->hdr
.code
)
299 case HDN_ITEMDBLCLICK
:
300 if ( SendClickEvent(nmhdr
->hdr
.code
== HDN_ITEMDBLCLICK
,
306 // although we should get the notifications about the right clicks
307 // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't
308 // happen in practice on any Windows system up to 2003
313 const int col
= wxMSWGetColumnClicked(&nmhdr
->hdr
, &pt
);
314 if ( col
!= wxNOT_FOUND
)
316 if ( SendClickEvent(nmhdr
->hdr
.code
== NM_RDBLCLK
, col
, 1) )
319 //else: ignore clicks outside any column
324 return wxHeaderCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
);