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
171 hdi
.mask
|= HDI_TEXT
;
172 wxWxCharBuffer buf
= col
.GetTitle().wx_str();
173 hdi
.pszText
= buf
.data();
174 hdi
.cchTextMax
= wxStrlen(buf
);
176 const wxBitmap bmp
= col
.GetBitmap();
177 if ( bmp
.IsOk() || oper
== Set
)
179 hdi
.mask
|= HDI_IMAGE
;
183 const int bmpWidth
= bmp
.GetWidth(),
184 bmpHeight
= bmp
.GetHeight();
188 m_imageList
= new wxImageList(bmpWidth
, bmpHeight
);
189 Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList
));
191 else // already have an image list
193 // check that all bitmaps we use have the same size
196 m_imageList
->GetSize(0, imageWidth
, imageHeight
);
198 wxASSERT_MSG( imageWidth
== bmpWidth
&& imageHeight
== bmpHeight
,
199 "all column bitmaps must have the same size" );
202 m_imageList
->Add(bmp
);
203 hdi
.iImage
= m_imageList
->GetImageCount() - 1;
205 else // no bitmap but we still need to update the item
207 hdi
.iImage
= I_IMAGENONE
;
211 if ( col
.GetAlignment() != wxALIGN_NOT
|| oper
== Set
)
213 hdi
.mask
|= HDI_FORMAT
;
214 switch ( col
.GetAlignment() )
221 case wxALIGN_CENTER_HORIZONTAL
:
222 hdi
.fmt
|= HDF_CENTER
;
226 hdi
.fmt
|= HDF_RIGHT
;
230 wxFAIL_MSG( "invalid column header alignment" );
234 if ( col
.IsSortKey() )
236 hdi
.mask
|= HDI_FORMAT
;
237 hdi
.fmt
|= col
.IsSortOrderAscending() ? HDF_SORTUP
: HDF_SORTDOWN
;
240 if ( col
.GetWidth() != wxCOL_WIDTH_DEFAULT
|| col
.IsHidden() )
242 hdi
.mask
|= HDI_WIDTH
;
243 hdi
.cxy
= col
.IsHidden() ? 0 : col
.GetWidth();
246 const LRESULT rc
= ::SendMessage(GetHwnd(),
247 oper
== Set
? HDM_SETITEM
: HDM_INSERTITEM
,
253 wxLogLastError(_T("Header_SetItem()"));
258 wxLogLastError(_T("Header_InsertItem()"));
262 // ----------------------------------------------------------------------------
263 // wxHeaderCtrl events
264 // ----------------------------------------------------------------------------
266 wxEventType
wxHeaderCtrl::GetClickEventType(bool dblclk
, int button
)
272 evtType
= dblclk
? wxEVT_COMMAND_HEADER_DCLICK
273 : wxEVT_COMMAND_HEADER_CLICK
;
277 evtType
= dblclk
? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
278 : wxEVT_COMMAND_HEADER_RIGHT_CLICK
;
282 evtType
= dblclk
? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
283 : wxEVT_COMMAND_HEADER_MIDDLE_CLICK
;
287 wxFAIL_MSG( wxS("unexpected event type") );
288 evtType
= wxEVT_NULL
;
294 bool wxHeaderCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
296 NMHEADER
* const nmhdr
= (NMHEADER
*)lParam
;
298 wxEventType evtType
= wxEVT_NULL
;
299 int idx
= nmhdr
->iItem
;
301 bool cancelled
= false;
302 const UINT code
= nmhdr
->hdr
.code
;
309 case HDN_ITEMDBLCLICK
:
310 evtType
= GetClickEventType(code
== HDN_ITEMDBLCLICK
, nmhdr
->iButton
);
313 // although we should get the notifications about the right clicks
314 // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't
315 // happen in practice on any Windows system up to 2003
320 idx
= wxMSWGetColumnClicked(&nmhdr
->hdr
, &pt
);
321 if ( idx
!= wxNOT_FOUND
)
322 evtType
= GetClickEventType(code
== NM_RDBLCLK
, 1);
323 //else: ignore clicks outside any column
327 case HDN_DIVIDERDBLCLICK
:
328 evtType
= wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
;
332 // column resizing events
333 // ----------------------
335 // see comments in wxListCtrl::MSWOnNotify() for why we catch both
336 // ASCII and Unicode versions of this message
337 case HDN_BEGINTRACKA
:
338 case HDN_BEGINTRACKW
:
339 // non-resizeable columns can't be resized no matter what, don't
340 // even generate any events for them
341 if ( !GetColumn(idx
).IsResizeable() )
348 evtType
= wxEVT_COMMAND_HEADER_BEGIN_RESIZE
;
353 if ( evtType
== wxEVT_NULL
)
354 evtType
= wxEVT_COMMAND_HEADER_RESIZING
;
359 width
= nmhdr
->pitem
->cxy
;
361 if ( evtType
== wxEVT_NULL
)
363 evtType
= wxEVT_COMMAND_HEADER_END_RESIZE
;
365 // don't generate events with invalid width
366 const int minWidth
= GetColumn(idx
).GetMinWidth();
367 if ( width
< minWidth
)
372 case HDN_ITEMCHANGING
:
373 if ( nmhdr
->pitem
&& (nmhdr
->pitem
->mask
& HDI_WIDTH
) )
375 // prevent the column from being shrunk beneath its min width
376 if ( nmhdr
->pitem
->cxy
< GetColumn(idx
).GetMinWidth() )
385 case NM_RELEASEDCAPTURE
:
391 // do generate the corresponding wx event
392 if ( evtType
!= wxEVT_NULL
)
394 wxHeaderCtrlEvent
event(evtType
, GetId());
395 event
.SetEventObject(this);
396 event
.SetColumn(idx
);
397 event
.SetWidth(width
);
399 event
.SetCancelled();
401 if ( GetEventHandler()->ProcessEvent(event
) )
403 if ( !event
.IsAllowed() )
405 // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
406 // interpret TRUE return value as meaning to stop the control
407 // default handling of the message
415 return wxHeaderCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
);
418 #endif // wxHAS_GENERIC_HEADERCTRL