]> git.saurik.com Git - wxWidgets.git/blame - src/msw/headerctrl.cpp
skip the header click event to allow sorting (done in the default handler) to take...
[wxWidgets.git] / src / msw / headerctrl.cpp
CommitLineData
56873923
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/headerctrl.cpp
3// Purpose: implementation of wxHeaderCtrl for wxMSW
4// Author: Vadim Zeitlin
5// Created: 2008-12-01
6// RCS-ID: $Id$
7// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// for compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#ifndef WX_PRECOMP
9ef3e400 27 #include "wx/log.h"
56873923
VZ
28#endif // WX_PRECOMP
29
30#include "wx/headerctrl.h"
3bfaa5a7
VZ
31
32#ifndef wxHAS_GENERIC_HEADERCTRL
33
56873923
VZ
34#include "wx/imaglist.h"
35
36#include "wx/msw/wrapcctl.h"
9ef3e400 37#include "wx/msw/private.h"
56873923 38
fa3d4aaf
VZ
39// from src/msw/listctrl.cpp
40extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
41
56873923
VZ
42// ============================================================================
43// wxHeaderCtrl implementation
44// ============================================================================
45
46// ----------------------------------------------------------------------------
47// wxHeaderCtrl construction/destruction
48// ----------------------------------------------------------------------------
49
50void wxHeaderCtrl::Init()
51{
52 m_imageList = NULL;
53}
54
55bool wxHeaderCtrl::Create(wxWindow *parent,
56 wxWindowID id,
57 const wxPoint& pos,
58 const wxSize& size,
59 long style,
60 const wxString& name)
61{
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
65
66 if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
67 return false;
68
69 if ( !MSWCreateControl(WC_HEADER, _T(""), pos, size) )
70 return false;
71
72 return true;
73}
74
75WXDWORD wxHeaderCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
76{
77 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
78
79 if ( style & wxHD_DRAGDROP )
80 msStyle |= HDS_DRAGDROP;
81
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;
86
87 return msStyle;
88}
89
90wxHeaderCtrl::~wxHeaderCtrl()
91{
92 delete m_imageList;
93}
94
f24f6579
VZ
95// ----------------------------------------------------------------------------
96// wxHeaderCtrl scrolling
97// ----------------------------------------------------------------------------
98
d8fc3398 99void wxHeaderCtrl::DoScrollHorz(int dx)
f24f6579 100{
d8fc3398
VZ
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)
f24f6579
VZ
107 SetSize(GetPosition().x + dx, -1, GetSize().x - dx, -1, wxSIZE_USE_EXISTING);
108}
109
56873923
VZ
110// ----------------------------------------------------------------------------
111// wxHeaderCtrl geometry calculation
112// ----------------------------------------------------------------------------
113
114wxSize wxHeaderCtrl::DoGetBestSize() const
115{
116 RECT rc = wxGetClientRect(GetHwndOf(GetParent()));
117 WINDOWPOS wpos;
118 HDLAYOUT layout = { &rc, &wpos };
119 if ( !Header_Layout(GetHwnd(), &layout) )
120 {
121 wxLogLastError(_T("Header_Layout"));
122 return wxControl::DoGetBestSize();
123 }
124
125 return wxSize(wpos.cx, wpos.cy);
126}
127
128// ----------------------------------------------------------------------------
129// wxHeaderCtrl columns managements
130// ----------------------------------------------------------------------------
131
132unsigned int wxHeaderCtrl::DoGetCount() const
133{
134 return Header_GetItemCount(GetHwnd());
135}
136
e2bfe673 137void wxHeaderCtrl::DoSetCount(unsigned int count)
56873923 138{
e2bfe673
VZ
139 unsigned n;
140
141 // first delete all old columns
142 const unsigned countOld = DoGetCount();
143 for ( n = 0; n < countOld; n++ )
144 {
145 if ( !Header_DeleteItem(GetHwnd(), 0) )
146 {
147 wxLogLastError(_T("Header_DeleteItem"));
148 }
149 }
150
151 // and add the new ones
152 for ( n = 0; n < count; n++ )
153 {
154 DoSetOrInsertItem(Insert, n);
155 }
156}
157
158void wxHeaderCtrl::DoUpdate(unsigned int idx)
159{
160 DoSetOrInsertItem(Set, idx);
161}
162
163void wxHeaderCtrl::DoSetOrInsertItem(Operation oper, unsigned int idx)
164{
165 const wxHeaderColumnBase& col = GetColumn(idx);
166
167 wxHDITEM hdi;
168
169 // notice that we need to store the string we use the pointer to until we
170 // pass it to the control
171 wxWxCharBuffer buf;
172 if ( !col.GetTitle().empty() )
173 {
174 hdi.mask |= HDI_TEXT;
175
176 buf = col.GetTitle().wx_str();
177 hdi.pszText = buf.data();
178 hdi.cchTextMax = wxStrlen(buf);
179 }
56873923
VZ
180
181 const wxBitmap bmp = col.GetBitmap();
182 if ( bmp.IsOk() )
183 {
184 const int bmpWidth = bmp.GetWidth(),
185 bmpHeight = bmp.GetHeight();
186
187 if ( !m_imageList )
188 {
189 m_imageList = new wxImageList(bmpWidth, bmpHeight);
190 Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
191 }
192 else // already have an image list
193 {
194 // check that all bitmaps we use have the same size
195 int imageWidth,
196 imageHeight;
197 m_imageList->GetSize(0, imageWidth, imageHeight);
198
199 wxASSERT_MSG( imageWidth == bmpWidth && imageHeight == bmpHeight,
200 "all column bitmaps must have the same size" );
201 }
202
203 m_imageList->Add(bmp);
204 hdi.mask |= HDI_IMAGE;
205 hdi.iImage = m_imageList->GetImageCount() - 1;
206 }
207
e2bfe673 208 if ( col.GetAlignment() != wxALIGN_NOT )
a0009205 209 {
e2bfe673
VZ
210 hdi.mask |= HDI_FORMAT;
211 switch ( col.GetAlignment() )
212 {
213 case wxALIGN_LEFT:
214 hdi.fmt |= HDF_LEFT;
215 break;
56873923 216
e2bfe673
VZ
217 case wxALIGN_CENTER:
218 case wxALIGN_CENTER_HORIZONTAL:
219 hdi.fmt |= HDF_CENTER;
220 break;
56873923 221
e2bfe673
VZ
222 case wxALIGN_RIGHT:
223 hdi.fmt |= HDF_RIGHT;
224 break;
56873923 225
e2bfe673
VZ
226 default:
227 wxFAIL_MSG( "invalid column header alignment" );
228 }
229 }
a0009205 230
e2bfe673 231 if ( col.IsSortKey() )
a0009205 232 {
e2bfe673
VZ
233 hdi.mask |= HDI_FORMAT;
234 hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
a0009205
VZ
235 }
236
e2bfe673 237 if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT || col.IsHidden() )
a0009205 238 {
e2bfe673
VZ
239 hdi.mask |= HDI_WIDTH;
240 hdi.cxy = col.IsHidden() ? 0 : col.GetWidth();
a0009205 241 }
a0009205 242
e2bfe673
VZ
243 const LRESULT rc = ::SendMessage(GetHwnd(),
244 oper == Set ? HDM_SETITEM : HDM_INSERTITEM,
245 idx,
246 (LPARAM)&hdi);
247 if ( oper == Set )
56873923 248 {
e2bfe673
VZ
249 if ( !rc )
250 wxLogLastError(_T("Header_SetItem()"));
56873923 251 }
e2bfe673 252 else // Insert
56873923 253 {
e2bfe673
VZ
254 if ( rc == -1 )
255 wxLogLastError(_T("Header_InsertItem()"));
56873923
VZ
256 }
257}
258
fa3d4aaf
VZ
259// ----------------------------------------------------------------------------
260// wxHeaderCtrl events
261// ----------------------------------------------------------------------------
262
aef252d9 263wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
fa3d4aaf
VZ
264{
265 wxEventType evtType;
266 switch ( button )
267 {
268 case 0:
269 evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK
270 : wxEVT_COMMAND_HEADER_CLICK;
271 break;
272
273 case 1:
274 evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
275 : wxEVT_COMMAND_HEADER_RIGHT_CLICK;
276 break;
277
278 case 2:
279 evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
280 : wxEVT_COMMAND_HEADER_MIDDLE_CLICK;
281 break;
282
283 default:
284 wxFAIL_MSG( wxS("unexpected event type") );
aef252d9 285 evtType = wxEVT_NULL;
fa3d4aaf
VZ
286 }
287
aef252d9 288 return evtType;
fa3d4aaf
VZ
289}
290
291bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
292{
293 NMHEADER * const nmhdr = (NMHEADER *)lParam;
294
aef252d9
VZ
295 wxEventType evtType = wxEVT_NULL;
296 int idx = nmhdr->iItem;
297 int width = 0;
a45caa71 298 bool cancelled = false;
0c9c5b43
VZ
299 const UINT code = nmhdr->hdr.code;
300 switch ( code )
fa3d4aaf 301 {
aef252d9
VZ
302 // click events
303 // ------------
304
fa3d4aaf
VZ
305 case HDN_ITEMCLICK:
306 case HDN_ITEMDBLCLICK:
aef252d9 307 evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
fa3d4aaf
VZ
308 break;
309
310 // although we should get the notifications about the right clicks
311 // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't
312 // happen in practice on any Windows system up to 2003
313 case NM_RCLICK:
314 case NM_RDBLCLK:
315 {
316 POINT pt;
aef252d9
VZ
317 idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
318 if ( idx != wxNOT_FOUND )
319 evtType = GetClickEventType(code == NM_RDBLCLK, 1);
fa3d4aaf
VZ
320 //else: ignore clicks outside any column
321 }
322 break;
3bfaa5a7
VZ
323
324 case HDN_DIVIDERDBLCLICK:
aef252d9
VZ
325 evtType = wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK;
326 break;
327
328
329 // column resizing events
330 // ----------------------
331
332 // see comments in wxListCtrl::MSWOnNotify() for why we catch both
333 // ASCII and Unicode versions of this message
334 case HDN_BEGINTRACKA:
335 case HDN_BEGINTRACKW:
0b2e1483
VZ
336 // non-resizeable columns can't be resized no matter what, don't
337 // even generate any events for them
338 if ( !GetColumn(idx).IsResizeable() )
339 {
340 *result = TRUE;
341
342 return true;
343 }
344
396825dc 345 evtType = wxEVT_COMMAND_HEADER_BEGIN_RESIZE;
aef252d9
VZ
346 // fall through
347
348 case HDN_TRACKA:
349 case HDN_TRACKW:
350 if ( evtType == wxEVT_NULL )
396825dc 351 evtType = wxEVT_COMMAND_HEADER_RESIZING;
aef252d9
VZ
352 // fall through
353
354 case HDN_ENDTRACKA:
355 case HDN_ENDTRACKW:
a45caa71
VZ
356 width = nmhdr->pitem->cxy;
357
aef252d9 358 if ( evtType == wxEVT_NULL )
a45caa71 359 {
396825dc 360 evtType = wxEVT_COMMAND_HEADER_END_RESIZE;
aef252d9 361
a45caa71
VZ
362 // don't generate events with invalid width
363 const int minWidth = GetColumn(idx).GetMinWidth();
364 if ( width < minWidth )
365 width = minWidth;
366 }
367 break;
368
369 case HDN_ITEMCHANGING:
370 if ( nmhdr->pitem && (nmhdr->pitem->mask & HDI_WIDTH) )
371 {
372 // prevent the column from being shrunk beneath its min width
373 if ( nmhdr->pitem->cxy < GetColumn(idx).GetMinWidth() )
374 {
375 *result = TRUE;
376
377 return true;
378 }
379 }
380 break;
381
382 case NM_RELEASEDCAPTURE:
383 cancelled = true;
3bfaa5a7 384 break;
fa3d4aaf
VZ
385 }
386
aef252d9
VZ
387
388 // do generate the corresponding wx event
389 if ( evtType != wxEVT_NULL )
390 {
391 wxHeaderCtrlEvent event(evtType, GetId());
392 event.SetEventObject(this);
393 event.SetColumn(idx);
394 event.SetWidth(width);
a45caa71
VZ
395 if ( cancelled )
396 event.SetCancelled();
aef252d9
VZ
397
398 if ( GetEventHandler()->ProcessEvent(event) )
399 {
400 if ( !event.IsAllowed() )
401 {
402 // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
403 // interpret TRUE return value as meaning to stop the control
404 // default handling of the message
405 *result = TRUE;
406 }
407
408 return true;
409 }
410 }
411
fa3d4aaf
VZ
412 return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
413}
3bfaa5a7
VZ
414
415#endif // wxHAS_GENERIC_HEADERCTRL