]> git.saurik.com Git - wxWidgets.git/blob - src/msw/headerctrl.cpp
add wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK and semi-automatic header resizing support
[wxWidgets.git] / src / msw / headerctrl.cpp
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
27 #include "wx/log.h"
28 #endif // WX_PRECOMP
29
30 #include "wx/headerctrl.h"
31
32 #ifndef wxHAS_GENERIC_HEADERCTRL
33
34 #include "wx/imaglist.h"
35
36 #include "wx/msw/wrapcctl.h"
37 #include "wx/msw/private.h"
38
39 // from src/msw/listctrl.cpp
40 extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
41
42 // ============================================================================
43 // wxHeaderCtrl implementation
44 // ============================================================================
45
46 // ----------------------------------------------------------------------------
47 // wxHeaderCtrl construction/destruction
48 // ----------------------------------------------------------------------------
49
50 void wxHeaderCtrl::Init()
51 {
52 m_imageList = NULL;
53 }
54
55 bool 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
75 WXDWORD 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
90 wxHeaderCtrl::~wxHeaderCtrl()
91 {
92 delete m_imageList;
93 }
94
95 // ----------------------------------------------------------------------------
96 // wxHeaderCtrl scrolling
97 // ----------------------------------------------------------------------------
98
99 void wxHeaderCtrl::DoScrollHorz(int dx)
100 {
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);
108 }
109
110 // ----------------------------------------------------------------------------
111 // wxHeaderCtrl geometry calculation
112 // ----------------------------------------------------------------------------
113
114 wxSize 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
132 unsigned int wxHeaderCtrl::DoGetCount() const
133 {
134 return Header_GetItemCount(GetHwnd());
135 }
136
137 void wxHeaderCtrl::DoSetCount(unsigned int count)
138 {
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
158 void wxHeaderCtrl::DoUpdate(unsigned int idx)
159 {
160 DoSetOrInsertItem(Set, idx);
161 }
162
163 void 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 }
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
208 if ( col.GetAlignment() != wxALIGN_NOT )
209 {
210 hdi.mask |= HDI_FORMAT;
211 switch ( col.GetAlignment() )
212 {
213 case wxALIGN_LEFT:
214 hdi.fmt |= HDF_LEFT;
215 break;
216
217 case wxALIGN_CENTER:
218 case wxALIGN_CENTER_HORIZONTAL:
219 hdi.fmt |= HDF_CENTER;
220 break;
221
222 case wxALIGN_RIGHT:
223 hdi.fmt |= HDF_RIGHT;
224 break;
225
226 default:
227 wxFAIL_MSG( "invalid column header alignment" );
228 }
229 }
230
231 if ( col.IsSortKey() )
232 {
233 hdi.mask |= HDI_FORMAT;
234 hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
235 }
236
237 if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT || col.IsHidden() )
238 {
239 hdi.mask |= HDI_WIDTH;
240 hdi.cxy = col.IsHidden() ? 0 : col.GetWidth();
241 }
242
243 const LRESULT rc = ::SendMessage(GetHwnd(),
244 oper == Set ? HDM_SETITEM : HDM_INSERTITEM,
245 idx,
246 (LPARAM)&hdi);
247 if ( oper == Set )
248 {
249 if ( !rc )
250 wxLogLastError(_T("Header_SetItem()"));
251 }
252 else // Insert
253 {
254 if ( rc == -1 )
255 wxLogLastError(_T("Header_InsertItem()"));
256 }
257 }
258
259 // ----------------------------------------------------------------------------
260 // wxHeaderCtrl events
261 // ----------------------------------------------------------------------------
262
263 bool wxHeaderCtrl::SendEvent(wxEventType evtType, unsigned int idx)
264 {
265 wxHeaderCtrlEvent event(evtType, GetId());
266 event.SetEventObject(this);
267 event.SetColumn(idx);
268
269 return GetEventHandler()->ProcessEvent(event);
270 }
271
272 bool wxHeaderCtrl::SendClickEvent(bool dblclk, int button, unsigned int idx)
273 {
274 wxEventType evtType;
275 switch ( button )
276 {
277 case 0:
278 evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK
279 : wxEVT_COMMAND_HEADER_CLICK;
280 break;
281
282 case 1:
283 evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
284 : wxEVT_COMMAND_HEADER_RIGHT_CLICK;
285 break;
286
287 case 2:
288 evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
289 : wxEVT_COMMAND_HEADER_MIDDLE_CLICK;
290 break;
291
292 default:
293 wxFAIL_MSG( wxS("unexpected event type") );
294 return false;
295 }
296
297 return SendEvent(evtType, idx);
298 }
299
300 bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
301 {
302 NMHEADER * const nmhdr = (NMHEADER *)lParam;
303
304 const int idx = nmhdr->iItem;
305 switch ( const UINT code = nmhdr->hdr.code )
306 {
307 case HDN_ITEMCLICK:
308 case HDN_ITEMDBLCLICK:
309 if ( SendClickEvent(code == HDN_ITEMDBLCLICK, nmhdr->iButton, idx) )
310 return true;
311 break;
312
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
316 case NM_RCLICK:
317 case NM_RDBLCLK:
318 {
319 POINT pt;
320 const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
321 if ( col != wxNOT_FOUND )
322 {
323 if ( SendClickEvent(code == NM_RDBLCLK, 1, col) )
324 return true;
325 }
326 //else: ignore clicks outside any column
327 }
328 break;
329
330 case HDN_DIVIDERDBLCLICK:
331 if ( SendEvent(wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, idx) )
332 return true;
333 break;
334 }
335
336 return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
337 }
338
339 #endif // wxHAS_GENERIC_HEADERCTRL