1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/headerctrl.cpp
3 // Purpose:     implementation of wxHeaderCtrl for wxMSW
4 // Author:      Vadim Zeitlin
5 // Created:     2008-12-01
6 // Copyright:   (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #if wxUSE_HEADERCTRL
26 
27 #ifndef WX_PRECOMP
28     #include "wx/app.h"
29     #include "wx/log.h"
30 #endif // WX_PRECOMP
31 
32 #include "wx/headerctrl.h"
33 
34 #ifndef wxHAS_GENERIC_HEADERCTRL
35 
36 #include "wx/imaglist.h"
37 
38 #include "wx/msw/wrapcctl.h"
39 #include "wx/msw/private.h"
40 
41 #ifndef HDM_SETBITMAPMARGIN
42     #define HDM_SETBITMAPMARGIN 0x1234
43 #endif
44 
45 #ifndef Header_SetBitmapMargin
46     #define Header_SetBitmapMargin(hwnd, margin) \
47             ::SendMessage((hwnd), HDM_SETBITMAPMARGIN, (WPARAM)(margin), 0)
48 #endif
49 
50 // from src/msw/listctrl.cpp
51 extern int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick);
52 
53 // ============================================================================
54 // wxHeaderCtrl implementation
55 // ============================================================================
56 
57 // ----------------------------------------------------------------------------
58 // wxHeaderCtrl construction/destruction
59 // ----------------------------------------------------------------------------
60 
Init()61 void wxHeaderCtrl::Init()
62 {
63     m_numColumns = 0;
64     m_imageList = NULL;
65     m_scrollOffset = 0;
66     m_colBeingDragged = -1;
67 }
68 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)69 bool wxHeaderCtrl::Create(wxWindow *parent,
70                           wxWindowID id,
71                           const wxPoint& pos,
72                           const wxSize& size,
73                           long style,
74                           const wxString& name)
75 {
76     // notice that we don't need InitCommonControlsEx(ICC_LISTVIEW_CLASSES)
77     // here as we already call InitCommonControls() in wxApp initialization
78     // code which covers this
79 
80     if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
81         return false;
82 
83     if ( !MSWCreateControl(WC_HEADER, wxT(""), pos, size) )
84         return false;
85 
86     // special hack for margins when using comctl32.dll v6 or later: the
87     // default margin is too big and results in label truncation when the
88     // column width is just about right to show it together with the sort
89     // indicator, so reduce it to a smaller value (in principle we could even
90     // use 0 here but this starts to look ugly)
91     if ( wxApp::GetComCtl32Version() >= 600 )
92     {
93         Header_SetBitmapMargin(GetHwnd(), ::GetSystemMetrics(SM_CXEDGE));
94     }
95 
96     return true;
97 }
98 
MSWGetStyle(long style,WXDWORD * exstyle) const99 WXDWORD wxHeaderCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
100 {
101     WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
102 
103     if ( style & wxHD_ALLOW_REORDER )
104         msStyle |= HDS_DRAGDROP;
105 
106     // the control looks nicer with these styles and there doesn't seem to be
107     // any reason to not use them so we always do (as for HDS_HORZ it is 0
108     // anyhow but include it for clarity)
109     // NOTE: don't use however HDS_FLAT because it makes the control look
110     //       non-native when running WinXP in classic mode
111     msStyle |= HDS_HORZ | HDS_BUTTONS | HDS_FULLDRAG | HDS_HOTTRACK;
112 
113     return msStyle;
114 }
115 
~wxHeaderCtrl()116 wxHeaderCtrl::~wxHeaderCtrl()
117 {
118     delete m_imageList;
119 }
120 
121 // ----------------------------------------------------------------------------
122 // wxHeaderCtrl scrolling
123 // ----------------------------------------------------------------------------
124 
DoSetSize(int x,int y,int w,int h,int sizeFlags)125 void wxHeaderCtrl::DoSetSize(int x, int y,
126                              int w, int h,
127                              int sizeFlags)
128 {
129     wxHeaderCtrlBase::DoSetSize(x + m_scrollOffset, y, w - m_scrollOffset, h,
130                                 sizeFlags);
131 }
132 
DoScrollHorz(int dx)133 void wxHeaderCtrl::DoScrollHorz(int dx)
134 {
135     // as the native control doesn't support offsetting its contents, we use a
136     // hack here to make it appear correctly when the parent is scrolled:
137     // instead of scrolling or repainting we simply move the control window
138     // itself: to be precise, offset it by the scroll increment to the left and
139     // increment its width to still extend to the right boundary to compensate
140     // for it (notice that dx is negative when scrolling to the right)
141     m_scrollOffset += dx;
142 
143     wxHeaderCtrlBase::DoSetSize(GetPosition().x + dx, -1,
144                                 GetSize().x - dx, -1,
145                                 wxSIZE_USE_EXISTING);
146 }
147 
148 // ----------------------------------------------------------------------------
149 // wxHeaderCtrl geometry calculation
150 // ----------------------------------------------------------------------------
151 
DoGetBestSize() const152 wxSize wxHeaderCtrl::DoGetBestSize() const
153 {
154     RECT rc = wxGetClientRect(GetHwndOf(GetParent()));
155     WINDOWPOS wpos;
156     HDLAYOUT layout = { &rc, &wpos };
157     if ( !Header_Layout(GetHwnd(), &layout) )
158     {
159         wxLogLastError(wxT("Header_Layout"));
160         return wxControl::DoGetBestSize();
161     }
162 
163     return wxSize(wpos.cx, wpos.cy);
164 }
165 
166 // ----------------------------------------------------------------------------
167 // wxHeaderCtrl columns managements
168 // ----------------------------------------------------------------------------
169 
DoGetCount() const170 unsigned int wxHeaderCtrl::DoGetCount() const
171 {
172     // we can't use Header_GetItemCount() here because it doesn't take the
173     // hidden columns into account and we can't find the hidden columns after
174     // the last shown one in MSWFromNativeIdx() without knowing where to stop
175     // so we have to store the columns count internally
176     return m_numColumns;
177 }
178 
GetShownColumnsCount() const179 int wxHeaderCtrl::GetShownColumnsCount() const
180 {
181     const int numItems = Header_GetItemCount(GetHwnd());
182 
183     wxASSERT_MSG( numItems >= 0 && (unsigned)numItems <= m_numColumns,
184                   "unexpected number of items in the native control" );
185 
186     return numItems;
187 }
188 
DoSetCount(unsigned int count)189 void wxHeaderCtrl::DoSetCount(unsigned int count)
190 {
191     unsigned n;
192 
193     // first delete all old columns
194     const unsigned countOld = GetShownColumnsCount();
195     for ( n = 0; n < countOld; n++ )
196     {
197         if ( !Header_DeleteItem(GetHwnd(), 0) )
198         {
199             wxLogLastError(wxT("Header_DeleteItem"));
200         }
201     }
202 
203     // update the column indices order array before changing m_numColumns
204     DoResizeColumnIndices(m_colIndices, count);
205 
206     // and add the new ones
207     m_numColumns = count;
208     m_isHidden.resize(m_numColumns);
209     for ( n = 0; n < count; n++ )
210     {
211         const wxHeaderColumn& col = GetColumn(n);
212         if ( col.IsShown() )
213         {
214             m_isHidden[n] = false;
215 
216             DoInsertItem(col, n);
217         }
218         else // hidden initially
219         {
220             m_isHidden[n] = true;
221         }
222     }
223 }
224 
DoUpdate(unsigned int idx)225 void wxHeaderCtrl::DoUpdate(unsigned int idx)
226 {
227     // the native control does provide Header_SetItem() but it's inconvenient
228     // to use it because it sends HDN_ITEMCHANGING messages and we'd have to
229     // arrange not to block setting the width from there and the logic would be
230     // more complicated as we'd have to reset the old values as well as setting
231     // the new ones -- so instead just recreate the column
232 
233     const wxHeaderColumn& col = GetColumn(idx);
234     if ( col.IsHidden() )
235     {
236         // column is hidden now
237         if ( !m_isHidden[idx] )
238         {
239             // but it wasn't hidden before, so remove it
240             Header_DeleteItem(GetHwnd(), MSWToNativeIdx(idx));
241 
242             m_isHidden[idx] = true;
243         }
244         //else: nothing to do, updating hidden column doesn't have any effect
245     }
246     else // column is shown now
247     {
248         if ( m_isHidden[idx] )
249         {
250             m_isHidden[idx] = false;
251         }
252         else // and it was shown before as well
253         {
254             // we need to remove the old column
255             Header_DeleteItem(GetHwnd(), MSWToNativeIdx(idx));
256         }
257 
258         DoInsertItem(col, idx);
259     }
260 }
261 
DoInsertItem(const wxHeaderColumn & col,unsigned int idx)262 void wxHeaderCtrl::DoInsertItem(const wxHeaderColumn& col, unsigned int idx)
263 {
264     wxASSERT_MSG( !col.IsHidden(), "should only be called for shown columns" );
265 
266     wxHDITEM hdi;
267 
268     // notice that we need to store the string we use the pointer to until we
269     // pass it to the control
270     hdi.mask |= HDI_TEXT;
271     wxWxCharBuffer buf = col.GetTitle().t_str();
272     hdi.pszText = buf.data();
273     hdi.cchTextMax = wxStrlen(buf);
274 
275     const wxBitmap bmp = col.GetBitmap();
276     if ( bmp.IsOk() )
277     {
278         hdi.mask |= HDI_IMAGE;
279 
280         if ( bmp.IsOk() )
281         {
282             const int bmpWidth = bmp.GetWidth(),
283                       bmpHeight = bmp.GetHeight();
284 
285             if ( !m_imageList )
286             {
287                 m_imageList = new wxImageList(bmpWidth, bmpHeight);
288                 (void) // suppress mingw32 warning about unused computed value
289                 Header_SetImageList(GetHwnd(), GetHimagelistOf(m_imageList));
290             }
291             else // already have an image list
292             {
293                 // check that all bitmaps we use have the same size
294                 int imageWidth,
295                     imageHeight;
296                 m_imageList->GetSize(0, imageWidth, imageHeight);
297 
298                 wxASSERT_MSG( imageWidth == bmpWidth && imageHeight == bmpHeight,
299                               "all column bitmaps must have the same size" );
300             }
301 
302             m_imageList->Add(bmp);
303             hdi.iImage = m_imageList->GetImageCount() - 1;
304         }
305         else // no bitmap but we still need to update the item
306         {
307             hdi.iImage = I_IMAGENONE;
308         }
309     }
310 
311     if ( col.GetAlignment() != wxALIGN_NOT )
312     {
313         hdi.mask |= HDI_FORMAT | HDF_LEFT;
314         switch ( col.GetAlignment() )
315         {
316             case wxALIGN_LEFT:
317                 hdi.fmt |= HDF_LEFT;
318                 break;
319 
320             case wxALIGN_CENTER:
321             case wxALIGN_CENTER_HORIZONTAL:
322                 hdi.fmt |= HDF_CENTER;
323                 break;
324 
325             case wxALIGN_RIGHT:
326                 hdi.fmt |= HDF_RIGHT;
327                 break;
328 
329             default:
330                 wxFAIL_MSG( "invalid column header alignment" );
331         }
332     }
333 
334     if ( col.IsSortKey() )
335     {
336         hdi.mask |= HDI_FORMAT;
337         hdi.fmt |= col.IsSortOrderAscending() ? HDF_SORTUP : HDF_SORTDOWN;
338     }
339 
340     if ( col.GetWidth() != wxCOL_WIDTH_DEFAULT )
341     {
342         hdi.mask |= HDI_WIDTH;
343         hdi.cxy = col.GetWidth();
344     }
345 
346     hdi.mask |= HDI_ORDER;
347     hdi.iOrder = MSWToNativeOrder(m_colIndices.Index(idx));
348 
349     if ( ::SendMessage(GetHwnd(), HDM_INSERTITEM,
350                        MSWToNativeIdx(idx), (LPARAM)&hdi) == -1 )
351     {
352         wxLogLastError(wxT("Header_InsertItem()"));
353     }
354 }
355 
DoSetColumnsOrder(const wxArrayInt & order)356 void wxHeaderCtrl::DoSetColumnsOrder(const wxArrayInt& order)
357 {
358     wxArrayInt orderShown;
359     orderShown.reserve(m_numColumns);
360 
361     for ( unsigned n = 0; n < m_numColumns; n++ )
362     {
363         const int idx = order[n];
364         if ( GetColumn(idx).IsShown() )
365             orderShown.push_back(MSWToNativeIdx(idx));
366     }
367 
368     if ( !Header_SetOrderArray(GetHwnd(), orderShown.size(), &orderShown[0]) )
369     {
370         wxLogLastError(wxT("Header_GetOrderArray"));
371     }
372 
373     m_colIndices = order;
374 }
375 
DoGetColumnsOrder() const376 wxArrayInt wxHeaderCtrl::DoGetColumnsOrder() const
377 {
378     // we don't use Header_GetOrderArray() here because it doesn't return
379     // information about the hidden columns, instead we just save the columns
380     // order array in DoSetColumnsOrder() and update it when they're reordered
381     return m_colIndices;
382 }
383 
384 // ----------------------------------------------------------------------------
385 // wxHeaderCtrl indexes and positions translation
386 // ----------------------------------------------------------------------------
387 
MSWToNativeIdx(int idx)388 int wxHeaderCtrl::MSWToNativeIdx(int idx)
389 {
390     // don't check for GetColumn(idx).IsShown() as it could have just became
391     // false and we may be called from DoUpdate() to delete the old column
392     wxASSERT_MSG( !m_isHidden[idx],
393                   "column must be visible to have an "
394                   "index in the native control" );
395 
396     int item = idx;
397     for ( int i = 0; i < idx; i++ )
398     {
399         if ( GetColumn(i).IsHidden() )
400             item--; // one less column the native control knows about
401     }
402 
403     wxASSERT_MSG( item >= 0 && item <= GetShownColumnsCount(), "logic error" );
404 
405     return item;
406 }
407 
MSWFromNativeIdx(int item)408 int wxHeaderCtrl::MSWFromNativeIdx(int item)
409 {
410     wxASSERT_MSG( item >= 0 && item < GetShownColumnsCount(),
411                   "column index out of range" );
412 
413     // reverse the above function
414 
415     unsigned idx = item;
416     for ( unsigned n = 0; n < m_numColumns; n++ )
417     {
418         if ( n > idx )
419             break;
420 
421         if ( GetColumn(n).IsHidden() )
422             idx++;
423     }
424 
425     wxASSERT_MSG( MSWToNativeIdx(idx) == item, "logic error" );
426 
427     return idx;
428 }
429 
MSWToNativeOrder(int pos)430 int wxHeaderCtrl::MSWToNativeOrder(int pos)
431 {
432     wxASSERT_MSG( pos >= 0 && static_cast<unsigned>(pos) < m_numColumns,
433                   "column position out of range" );
434 
435     int order = pos;
436     for ( int n = 0; n < pos; n++ )
437     {
438         if ( GetColumn(m_colIndices[n]).IsHidden() )
439             order--;
440     }
441 
442     wxASSERT_MSG( order >= 0 && order <= GetShownColumnsCount(), "logic error" );
443 
444     return order;
445 }
446 
MSWFromNativeOrder(int order)447 int wxHeaderCtrl::MSWFromNativeOrder(int order)
448 {
449     wxASSERT_MSG( order >= 0 && order < GetShownColumnsCount(),
450                   "native column position out of range" );
451 
452     unsigned pos = order;
453     for ( unsigned n = 0; n < m_numColumns; n++ )
454     {
455         if ( n > pos )
456             break;
457 
458         if ( GetColumn(m_colIndices[n]).IsHidden() )
459             pos++;
460     }
461 
462     wxASSERT_MSG( MSWToNativeOrder(pos) == order, "logic error" );
463 
464     return pos;
465 }
466 
467 // ----------------------------------------------------------------------------
468 // wxHeaderCtrl events
469 // ----------------------------------------------------------------------------
470 
GetClickEventType(bool dblclk,int button)471 wxEventType wxHeaderCtrl::GetClickEventType(bool dblclk, int button)
472 {
473     wxEventType evtType;
474     switch ( button )
475     {
476         case 0:
477             evtType = dblclk ? wxEVT_HEADER_DCLICK
478                              : wxEVT_HEADER_CLICK;
479             break;
480 
481         case 1:
482             evtType = dblclk ? wxEVT_HEADER_RIGHT_DCLICK
483                              : wxEVT_HEADER_RIGHT_CLICK;
484             break;
485 
486         case 2:
487             evtType = dblclk ? wxEVT_HEADER_MIDDLE_DCLICK
488                              : wxEVT_HEADER_MIDDLE_CLICK;
489             break;
490 
491         default:
492             wxFAIL_MSG( wxS("unexpected event type") );
493             evtType = wxEVT_NULL;
494     }
495 
496     return evtType;
497 }
498 
MSWOnNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)499 bool wxHeaderCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
500 {
501     NMHEADER * const nmhdr = (NMHEADER *)lParam;
502 
503     wxEventType evtType = wxEVT_NULL;
504     int width = 0;
505     int order = -1;
506     bool veto = false;
507     const UINT code = nmhdr->hdr.code;
508 
509     // we don't have the index for all events, e.g. not for NM_RELEASEDCAPTURE
510     // so only access for header control events (and yes, the direction of
511     // comparisons with FIRST/LAST is correct even if it seems inverted)
512     int idx = code <= HDN_FIRST && code > HDN_LAST ? nmhdr->iItem : -1;
513     if ( idx != -1 )
514     {
515         // we also get bogus HDN_BEGINDRAG with -1 index so don't call
516         // MSWFromNativeIdx() unconditionally for nmhdr->iItem
517         idx = MSWFromNativeIdx(idx);
518     }
519 
520     switch ( code )
521     {
522         // click events
523         // ------------
524 
525         case HDN_ITEMCLICK:
526         case HDN_ITEMDBLCLICK:
527             evtType = GetClickEventType(code == HDN_ITEMDBLCLICK, nmhdr->iButton);
528 
529             // We're not dragging any more.
530             m_colBeingDragged = -1;
531             break;
532 
533             // although we should get the notifications about the right clicks
534             // via HDN_ITEM[DBL]CLICK too according to MSDN this simply doesn't
535             // happen in practice on any Windows system up to 2003
536         case NM_RCLICK:
537         case NM_RDBLCLK:
538             {
539                 POINT pt;
540                 idx = wxMSWGetColumnClicked(&nmhdr->hdr, &pt);
541                 if ( idx != wxNOT_FOUND )
542                 {
543                     idx = MSWFromNativeIdx(idx);
544 
545                     // due to a bug in mingw32 headers NM_RDBLCLK is signed
546                     // there so we need a cast to avoid warnings about signed/
547                     // unsigned comparison
548                     evtType = GetClickEventType(
549                                 code == static_cast<UINT>(NM_RDBLCLK), 1);
550                 }
551                 //else: ignore clicks outside any column
552             }
553             break;
554 
555         case HDN_DIVIDERDBLCLICK:
556             evtType = wxEVT_HEADER_SEPARATOR_DCLICK;
557             break;
558 
559 
560         // column resizing events
561         // ----------------------
562 
563         // see comments in wxListCtrl::MSWOnNotify() for why we catch both
564         // ASCII and Unicode versions of this message
565         case HDN_BEGINTRACKA:
566         case HDN_BEGINTRACKW:
567             // non-resizable columns can't be resized no matter what, don't
568             // even generate any events for them
569             if ( !GetColumn(idx).IsResizeable() )
570             {
571                 veto = true;
572                 break;
573             }
574 
575             evtType = wxEVT_HEADER_BEGIN_RESIZE;
576             // fall through
577 
578         case HDN_ENDTRACKA:
579         case HDN_ENDTRACKW:
580             width = nmhdr->pitem->cxy;
581 
582             if ( evtType == wxEVT_NULL )
583             {
584                 evtType = wxEVT_HEADER_END_RESIZE;
585 
586                 // don't generate events with invalid width
587                 const int minWidth = GetColumn(idx).GetMinWidth();
588                 if ( width < minWidth )
589                     width = minWidth;
590             }
591             break;
592 
593             // The control is not supposed to send HDN_TRACK when using
594             // HDS_FULLDRAG (which we do use) but apparently some versions of
595             // comctl32.dll still do it, see #13506, so catch both messages
596             // just in case we are dealing with one of these buggy versions.
597         case HDN_TRACK:
598         case HDN_ITEMCHANGING:
599             if ( nmhdr->pitem && (nmhdr->pitem->mask & HDI_WIDTH) )
600             {
601                 // prevent the column from being shrunk beneath its min width
602                 width = nmhdr->pitem->cxy;
603                 if ( width < GetColumn(idx).GetMinWidth() )
604                 {
605                     // don't generate any events and prevent the change from
606                     // happening
607                     veto = true;
608                 }
609                 else // width is acceptable
610                 {
611                     // generate the resizing event from here as we don't seem
612                     // to be getting HDN_TRACK events at all, at least with
613                     // comctl32.dll v6
614                     evtType = wxEVT_HEADER_RESIZING;
615                 }
616             }
617             break;
618 
619 
620         // column reordering events
621         // ------------------------
622 
623         case HDN_BEGINDRAG:
624             // Windows sometimes sends us events with invalid column indices
625             if ( nmhdr->iItem == -1 )
626                 break;
627 
628             // If we are dragging a column that is not draggable and the mouse
629             // is moved over a different column then we get the column number from
630             // the column under the mouse. This results in an unexpected behaviour
631             // if this column is draggable. To prevent this remember the column we
632             // are dragging for the complete drag and drop cycle.
633             if ( m_colBeingDragged == -1 )
634             {
635                 m_colBeingDragged = idx;
636             }
637 
638             // column must have the appropriate flag to be draggable
639             if ( !GetColumn(m_colBeingDragged).IsReorderable() )
640             {
641                 veto = true;
642                 break;
643             }
644 
645             evtType = wxEVT_HEADER_BEGIN_REORDER;
646             break;
647 
648         case HDN_ENDDRAG:
649             wxASSERT_MSG( nmhdr->pitem->mask & HDI_ORDER, "should have order" );
650             order = nmhdr->pitem->iOrder;
651 
652             // we also get messages with invalid order when column reordering
653             // is cancelled (e.g. by pressing Esc)
654             if ( order == -1 )
655                 break;
656 
657             order = MSWFromNativeOrder(order);
658 
659             evtType = wxEVT_HEADER_END_REORDER;
660 
661             // We (successfully) ended dragging the column.
662             m_colBeingDragged = -1;
663             break;
664 
665         case NM_RELEASEDCAPTURE:
666             evtType = wxEVT_HEADER_DRAGGING_CANCELLED;
667 
668             // Dragging the column was cancelled.
669             m_colBeingDragged = -1;
670             break;
671     }
672 
673 
674     // do generate the corresponding wx event
675     if ( evtType != wxEVT_NULL )
676     {
677         wxHeaderCtrlEvent event(evtType, GetId());
678         event.SetEventObject(this);
679         event.SetColumn(idx);
680         event.SetWidth(width);
681         if ( order != -1 )
682             event.SetNewOrder(order);
683 
684         const bool processed = GetEventHandler()->ProcessEvent(event);
685 
686         if ( processed && !event.IsAllowed() )
687             veto = true;
688 
689         if ( !veto )
690         {
691             // special post-processing for HDN_ENDDRAG: we need to update the
692             // internal column indices array if this is allowed to go ahead as
693             // the native control is going to reorder its columns now
694             if ( evtType == wxEVT_HEADER_END_REORDER )
695                 MoveColumnInOrderArray(m_colIndices, idx, order);
696 
697             if ( processed )
698             {
699                 // skip default processing below
700                 return true;
701             }
702         }
703     }
704 
705     if ( veto )
706     {
707         // all of HDN_BEGIN{DRAG,TRACK}, HDN_TRACK and HDN_ITEMCHANGING
708         // interpret TRUE return value as meaning to stop the control
709         // default handling of the message
710         *result = TRUE;
711 
712         return true;
713     }
714 
715     return wxHeaderCtrlBase::MSWOnNotify(idCtrl, lParam, result);
716 }
717 
718 #endif // wxHAS_GENERIC_HEADERCTRL
719 
720 #endif // wxUSE_HEADERCTRL
721