]> git.saurik.com Git - wxWidgets.git/blob - src/msw/headerctrl.cpp
601cb236f85997f0542f41b4555d1ce0c174d8c0
[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 #include "wx/imaglist.h"
32
33 #include "wx/msw/wrapcctl.h"
34 #include "wx/msw/private.h"
35
36 // from src/msw/listctrl.cpp
37 extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
38
39 // ============================================================================
40 // wxHeaderCtrl implementation
41 // ============================================================================
42
43 // ----------------------------------------------------------------------------
44 // wxHeaderCtrl construction/destruction
45 // ----------------------------------------------------------------------------
46
47 void wxHeaderCtrl::Init()
48 {
49 m_imageList = NULL;
50 }
51
52 bool wxHeaderCtrl::Create(wxWindow *parent,
53 wxWindowID id,
54 const wxPoint& pos,
55 const wxSize& size,
56 long style,
57 const wxString& name)
58 {
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
62
63 if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
64 return false;
65
66 if ( !MSWCreateControl(WC_HEADER, _T(""), pos, size) )
67 return false;
68
69 return true;
70 }
71
72 WXDWORD wxHeaderCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
73 {
74 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
75
76 if ( style & wxHD_DRAGDROP )
77 msStyle |= HDS_DRAGDROP;
78
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;
83
84 return msStyle;
85 }
86
87 wxHeaderCtrl::~wxHeaderCtrl()
88 {
89 delete m_imageList;
90 }
91
92 // ----------------------------------------------------------------------------
93 // wxHeaderCtrl scrolling
94 // ----------------------------------------------------------------------------
95
96 void wxHeaderCtrl::DoScrollHorz(int dx)
97 {
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);
105 }
106
107 // ----------------------------------------------------------------------------
108 // wxHeaderCtrl geometry calculation
109 // ----------------------------------------------------------------------------
110
111 wxSize wxHeaderCtrl::DoGetBestSize() const
112 {
113 RECT rc = wxGetClientRect(GetHwndOf(GetParent()));
114 WINDOWPOS wpos;
115 HDLAYOUT layout = { &rc, &wpos };
116 if ( !Header_Layout(GetHwnd(), &layout) )
117 {
118 wxLogLastError(_T("Header_Layout"));
119 return wxControl::DoGetBestSize();
120 }
121
122 return wxSize(wpos.cx, wpos.cy);
123 }
124
125 // ----------------------------------------------------------------------------
126 // wxHeaderCtrl columns managements
127 // ----------------------------------------------------------------------------
128
129 unsigned int wxHeaderCtrl::DoGetCount() const
130 {
131 return Header_GetItemCount(GetHwnd());
132 }
133
134 void wxHeaderCtrl::DoSetCount(unsigned int count)
135 {
136 unsigned n;
137
138 // first delete all old columns
139 const unsigned countOld = DoGetCount();
140 for ( n = 0; n < countOld; n++ )
141 {
142 if ( !Header_DeleteItem(GetHwnd(), 0) )
143 {
144 wxLogLastError(_T("Header_DeleteItem"));
145 }
146 }
147
148 // and add the new ones
149 for ( n = 0; n < count; n++ )
150 {
151 DoSetOrInsertItem(Insert, n);
152 }
153 }
154
155 void wxHeaderCtrl::DoUpdate(unsigned int idx)
156 {
157 DoSetOrInsertItem(Set, idx);
158 }
159
160 void wxHeaderCtrl::DoSetOrInsertItem(Operation oper, unsigned int idx)
161 {
162 const wxHeaderColumnBase& col = GetColumn(idx);
163
164 wxHDITEM hdi;
165
166 // notice that we need to store the string we use the pointer to until we
167 // pass it to the control
168 wxWxCharBuffer buf;
169 if ( !col.GetTitle().empty() )
170 {
171 hdi.mask |= HDI_TEXT;
172
173 buf = col.GetTitle().wx_str();
174 hdi.pszText = buf.data();
175 hdi.cchTextMax = wxStrlen(buf);
176 }
177
178 const wxBitmap bmp = col.GetBitmap();
179 if ( bmp.IsOk() )
180 {
181 const int bmpWidth = bmp.GetWidth(),
182 bmpHeight = bmp.GetHeight();
183
184 if ( !m_imageList )
185 {
186 m_imageList = new wxImageList(bmpWidth, bmpHeight);
187 Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
188 }
189 else // already have an image list
190 {
191 // check that all bitmaps we use have the same size
192 int imageWidth,
193 imageHeight;
194 m_imageList->GetSize(0, imageWidth, imageHeight);
195
196 wxASSERT_MSG( imageWidth == bmpWidth && imageHeight == bmpHeight,
197 "all column bitmaps must have the same size" );
198 }
199
200 m_imageList->Add(bmp);
201 hdi.mask |= HDI_IMAGE;
202 hdi.iImage = m_imageList->GetImageCount() - 1;
203 }
204
205 if ( col.GetAlignment() != wxALIGN_NOT )
206 {
207 hdi.mask |= HDI_FORMAT;
208 switch ( col.GetAlignment() )
209 {
210 case wxALIGN_LEFT:
211 hdi.fmt |= HDF_LEFT;
212 break;
213
214 case wxALIGN_CENTER:
215 case wxALIGN_CENTER_HORIZONTAL:
216 hdi.fmt |= HDF_CENTER;
217 break;
218
219 case wxALIGN_RIGHT:
220 hdi.fmt |= HDF_RIGHT;
221 break;
222
223 default:
224 wxFAIL_MSG( "invalid column header alignment" );
225 }
226 }
227
228 if ( col.IsSortKey() )
229 {
230 hdi.mask |= HDI_FORMAT;
231 hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
232 }
233
234 if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT || col.IsHidden() )
235 {
236 hdi.mask |= HDI_WIDTH;
237 hdi.cxy = col.IsHidden() ? 0 : col.GetWidth();
238 }
239
240 const LRESULT rc = ::SendMessage(GetHwnd(),
241 oper == Set ? HDM_SETITEM : HDM_INSERTITEM,
242 idx,
243 (LPARAM)&hdi);
244 if ( oper == Set )
245 {
246 if ( !rc )
247 wxLogLastError(_T("Header_SetItem()"));
248 }
249 else // Insert
250 {
251 if ( rc == -1 )
252 wxLogLastError(_T("Header_InsertItem()"));
253 }
254 }
255
256 // ----------------------------------------------------------------------------
257 // wxHeaderCtrl events
258 // ----------------------------------------------------------------------------
259
260 bool wxHeaderCtrl::SendClickEvent(bool dblclk, unsigned int idx, int button)
261 {
262 wxEventType evtType;
263 switch ( button )
264 {
265 case 0:
266 evtType = dblclk ? wxEVT_COMMAND_HEADER_DCLICK
267 : wxEVT_COMMAND_HEADER_CLICK;
268 break;
269
270 case 1:
271 evtType = dblclk ? wxEVT_COMMAND_HEADER_RIGHT_DCLICK
272 : wxEVT_COMMAND_HEADER_RIGHT_CLICK;
273 break;
274
275 case 2:
276 evtType = dblclk ? wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
277 : wxEVT_COMMAND_HEADER_MIDDLE_CLICK;
278 break;
279
280 default:
281 wxFAIL_MSG( wxS("unexpected event type") );
282 return false;
283 }
284
285 wxHeaderCtrlEvent event(evtType, GetId());
286 event.SetEventObject(this);
287 event.SetColumn(idx);
288
289 return GetEventHandler()->ProcessEvent(event);
290 }
291
292 bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
293 {
294 NMHEADER * const nmhdr = (NMHEADER *)lParam;
295
296 switch ( nmhdr->hdr.code )
297 {
298 case HDN_ITEMCLICK:
299 case HDN_ITEMDBLCLICK:
300 if ( SendClickEvent(nmhdr->hdr.code == HDN_ITEMDBLCLICK,
301 nmhdr->iItem,
302 nmhdr->iButton) )
303 return true;
304 break;
305
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
309 case NM_RCLICK:
310 case NM_RDBLCLK:
311 {
312 POINT pt;
313 const int col = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
314 if ( col != wxNOT_FOUND )
315 {
316 if ( SendClickEvent(nmhdr->hdr.code == NM_RDBLCLK, col, 1) )
317 return true;
318 }
319 //else: ignore clicks outside any column
320 }
321 break;
322 }
323
324 return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
325 }