1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/generic/headerctrlg.cpp 
   3 // Purpose:     generic wxHeaderCtrl implementation 
   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" 
  29 #include "wx/headerctrl.h" 
  31 #ifdef wxHAS_GENERIC_HEADERCTRL 
  33 #include "wx/dcbuffer.h" 
  34 #include "wx/renderer.h" 
  36 // ---------------------------------------------------------------------------- 
  38 // ---------------------------------------------------------------------------- 
  43 const unsigned NO_SORT 
= (unsigned)-1; 
  45 const unsigned COL_NONE 
= (unsigned)-1; 
  47 } // anonymous namespace 
  49 // ============================================================================ 
  50 // wxHeaderCtrl implementation 
  51 // ============================================================================ 
  53 // ---------------------------------------------------------------------------- 
  54 // wxHeaderCtrl creation 
  55 // ---------------------------------------------------------------------------- 
  57 void wxHeaderCtrl::Init() 
  61     m_colBeingResized 
= COL_NONE
; 
  65 bool wxHeaderCtrl::Create(wxWindow 
*parent
, 
  72     if ( !wxHeaderCtrlBase::Create(parent
, id
, pos
, size
, 
  73                                    style
, wxDefaultValidator
, name
) ) 
  76     // tell the system to not paint the background at all to avoid flicker as 
  77     // we paint the entire window area in our OnPaint() 
  78     SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
  83 wxHeaderCtrl::~wxHeaderCtrl() 
  87 // ---------------------------------------------------------------------------- 
  88 // wxHeaderCtrl columns manipulation 
  89 // ---------------------------------------------------------------------------- 
  91 void wxHeaderCtrl::DoSetCount(unsigned int count
) 
  98 unsigned int wxHeaderCtrl::DoGetCount() const 
 103 void wxHeaderCtrl::DoUpdate(unsigned int idx
) 
 105     // we need to refresh not only this column but also the ones after it in 
 106     // case it was shown or hidden or its width changed -- it would be nice to 
 107     // avoid doing this unnecessary by storing the old column width (TODO) 
 108     RefreshColsAfter(idx
); 
 111 // ---------------------------------------------------------------------------- 
 112 // wxHeaderCtrl scrolling 
 113 // ---------------------------------------------------------------------------- 
 115 void wxHeaderCtrl::DoScrollHorz(int dx
) 
 117     m_scrollOffset 
+= dx
; 
 119     // don't call our own version which calls this function! 
 120     wxControl::ScrollWindow(dx
, 0); 
 123 // ---------------------------------------------------------------------------- 
 124 // wxHeaderCtrl geometry 
 125 // ---------------------------------------------------------------------------- 
 127 wxSize 
wxHeaderCtrl::DoGetBestSize() const 
 129     // the vertical size is rather arbitrary but it looks better if we leave 
 130     // some space around the text 
 131     return wxSize(GetColStart(GetColumnCount()), (7*GetCharHeight())/4); 
 134 int wxHeaderCtrl::GetColStart(unsigned int idx
) const 
 136     wxHeaderCtrl 
* const self 
= const_cast<wxHeaderCtrl 
*>(this); 
 138     int pos 
= m_scrollOffset
; 
 139     for ( unsigned n 
= 0; n 
< idx
; n
++ ) 
 141         const wxHeaderColumnBase
& col 
= self
->GetColumn(n
); 
 143             pos 
+= col
.GetWidth(); 
 149 int wxHeaderCtrl::FindColumnAtPos(int x
, bool& onSeparator
) const 
 151     wxHeaderCtrl 
* const self 
= const_cast<wxHeaderCtrl 
*>(this); 
 154     const unsigned count 
= GetColumnCount(); 
 155     for ( unsigned n 
= 0; n 
< count
; n
++ ) 
 157         const wxHeaderColumnBase
& col 
= self
->GetColumn(n
); 
 158         if ( col
.IsHidden() ) 
 161         pos 
+= col
.GetWidth(); 
 163         // if the column is resizeable, check if we're approximatively over the 
 164         // line separating it from the next column 
 166         // TODO: don't hardcode sensitivity 
 167         if ( col
.IsResizeable() && abs(x 
- pos
) < 8 ) 
 173         // inside this column? 
 184 // ---------------------------------------------------------------------------- 
 185 // wxHeaderCtrl repainting 
 186 // ---------------------------------------------------------------------------- 
 188 void wxHeaderCtrl::RefreshCol(unsigned int idx
) 
 190     wxRect rect 
= GetClientRect(); 
 191     rect
.x 
+= GetColStart(idx
); 
 192     rect
.width 
= GetColumn(idx
).GetWidth(); 
 197 void wxHeaderCtrl::RefreshColIfNotNone(unsigned int idx
) 
 199     if ( idx 
!= COL_NONE 
) 
 203 void wxHeaderCtrl::RefreshColsAfter(unsigned int idx
) 
 205     wxRect rect 
= GetClientRect(); 
 206     const int ofs 
= GetColStart(idx
); 
 213 // ---------------------------------------------------------------------------- 
 214 // wxHeaderCtrl dragging 
 215 // ---------------------------------------------------------------------------- 
 217 void wxHeaderCtrl::UpdateResizingMarker(int xPhysical
) 
 219     // unfortunately drawing the marker over the parent window doesn't work as 
 220     // it's usually covered by another window (the main control view) so just 
 221     // draw the marker over the header itself, even if it makes it not very 
 225     wxDCOverlay 
dcover(m_overlay
, &dc
); 
 228     if ( xPhysical 
!= -1 ) 
 230         dc
.SetPen(*wxLIGHT_GREY_PEN
); 
 231         dc
.DrawLine(xPhysical
, 0, xPhysical
, GetClientSize().y
); 
 235 void wxHeaderCtrl::EndDragging() 
 237     UpdateResizingMarker(-1); 
 241     // don't use the special dragging cursor any more 
 242     SetCursor(wxNullCursor
); 
 245 void wxHeaderCtrl::EndResizing(int width
) 
 247     wxASSERT_MSG( m_colBeingResized 
!= COL_NONE
, 
 248                   "shouldn't be called if we're not resizing" ); 
 252     // if dragging was cancelled we must have already lost the mouse capture so 
 253     // don't try to release it 
 257     wxHeaderCtrlEvent 
event(wxEVT_COMMAND_HEADER_END_DRAG
, GetId()); 
 258     event
.SetEventObject(this); 
 259     event
.SetColumn(m_colBeingResized
); 
 261         event
.SetCancelled(); 
 263         event
.SetWidth(width
); 
 265     GetEventHandler()->ProcessEvent(event
); 
 267     m_colBeingResized 
= COL_NONE
; 
 270 // ---------------------------------------------------------------------------- 
 271 // wxHeaderCtrl event handlers 
 272 // ---------------------------------------------------------------------------- 
 274 BEGIN_EVENT_TABLE(wxHeaderCtrl
, wxHeaderCtrlBase
) 
 275     EVT_PAINT(wxHeaderCtrl::OnPaint
) 
 277     EVT_MOUSE_EVENTS(wxHeaderCtrl::OnMouse
) 
 279     EVT_MOUSE_CAPTURE_LOST(wxHeaderCtrl::OnCaptureLost
) 
 282 void wxHeaderCtrl::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
 285     GetClientSize(&w
, &h
); 
 287     wxAutoBufferedPaintDC 
dc(this); 
 289     dc
.SetBackground(GetBackgroundColour()); 
 292     // account for the horizontal scrollbar offset in the parent window 
 293     dc
.SetDeviceOrigin(m_scrollOffset
, 0); 
 295     const unsigned int count 
= m_numColumns
; 
 297     for ( unsigned int i 
= 0; i 
< count
; i
++ ) 
 299         const wxHeaderColumnBase
& col 
= GetColumn(i
); 
 300         if ( col
.IsHidden() ) 
 303         const int colWidth 
= col
.GetWidth(); 
 305         wxHeaderSortIconType sortArrow
; 
 306         if ( col
.IsSortKey() ) 
 308             sortArrow 
= col
.IsSortOrderAscending() ? wxHDR_SORT_ICON_UP
 
 309                                                    : wxHDR_SORT_ICON_DOWN
; 
 311         else // not sorting by this column 
 313             sortArrow 
= wxHDR_SORT_ICON_NONE
; 
 320                 state 
= wxCONTROL_CURRENT
; 
 324             state 
= wxCONTROL_DISABLED
; 
 327         wxHeaderButtonParams params
; 
 328         params
.m_labelText 
= col
.GetTitle(); 
 329         params
.m_labelBitmap 
= col
.GetBitmap(); 
 330         params
.m_labelAlignment 
= col
.GetAlignment(); 
 332         wxRendererNative::Get().DrawHeaderButton
 
 336                                     wxRect(xpos
, 0, colWidth
, h
), 
 346 void wxHeaderCtrl::OnCaptureLost(wxMouseCaptureLostEvent
& WXUNUSED(event
)) 
 348     if ( m_colBeingResized 
!= COL_NONE 
) 
 352 void wxHeaderCtrl::OnMouse(wxMouseEvent
& mevent
) 
 354     // do this in advance to allow simply returning if we're not interested, 
 355     // we'll undo it if we do handle the event below 
 359     // account for the control displacement 
 360     const int xPhysical 
= mevent
.GetX(); 
 361     const int xLogical 
= xPhysical 
- m_scrollOffset
; 
 363     // first deal with the [continuation of any] dragging operations in 
 365     if ( m_colBeingResized 
!= COL_NONE 
) 
 367         if ( mevent
.LeftUp() ) 
 368             EndResizing(xPhysical 
- GetColStart(m_colBeingResized
)); 
 369         else // update the live separator position 
 370             UpdateResizingMarker(xPhysical
); 
 376     // find if the event is over a column at all 
 378     const unsigned col 
= mevent
.Leaving() 
 379                             ? (onSeparator 
= false, COL_NONE
) 
 380                             : FindColumnAtPos(xLogical
, onSeparator
); 
 383     // update the highlighted column if it changed 
 384     if ( col 
!= m_hover 
) 
 386         const unsigned hoverOld 
= m_hover
; 
 389         RefreshColIfNotNone(hoverOld
); 
 390         RefreshColIfNotNone(m_hover
); 
 393     // update mouse cursor as it moves around 
 394     if ( mevent
.Moving() ) 
 396         SetCursor(onSeparator 
? wxCursor(wxCURSOR_SIZEWE
) : wxNullCursor
); 
 400     // all the other events only make sense when they happen over a column 
 401     if ( col 
== COL_NONE 
) 
 405     // enter various dragging modes on left mouse press 
 406     if ( mevent
.LeftDown() ) 
 410             // start resizing the column 
 411             m_colBeingResized 
= col
; 
 412             SetCursor(wxCursor(wxCURSOR_SIZEWE
)); 
 414             UpdateResizingMarker(xPhysical
); 
 416         else // on column itself 
 425     // determine the type of header event corresponding to click events 
 426     wxEventType evtType 
= wxEVT_NULL
; 
 427     const bool click 
= mevent
.ButtonUp(), 
 428                dblclk 
= mevent
.ButtonDClick(); 
 429     if ( click 
|| dblclk 
) 
 431         switch ( mevent
.GetButton() ) 
 433             case wxMOUSE_BTN_LEFT
: 
 434                 // treat left double clicks on separator specially 
 435                 if ( onSeparator 
&& dblclk 
) 
 437                     evtType 
= wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
; 
 439                 else // not double click on separator 
 441                     evtType 
= click 
? wxEVT_COMMAND_HEADER_CLICK
 
 442                                     : wxEVT_COMMAND_HEADER_DCLICK
; 
 446             case wxMOUSE_BTN_RIGHT
: 
 447                 evtType 
= click 
? wxEVT_COMMAND_HEADER_RIGHT_CLICK
 
 448                                 : wxEVT_COMMAND_HEADER_RIGHT_DCLICK
; 
 451             case wxMOUSE_BTN_MIDDLE
: 
 452                 evtType 
= click 
? wxEVT_COMMAND_HEADER_MIDDLE_CLICK
 
 453                                 : wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
; 
 457                 // ignore clicks from other mouse buttons 
 462     if ( evtType 
== wxEVT_NULL 
) 
 465     wxHeaderCtrlEvent 
event(evtType
, GetId()); 
 466     event
.SetEventObject(this); 
 467     event
.SetColumn(col
); 
 469     if ( GetEventHandler()->ProcessEvent(event
) ) 
 473 #endif // wxHAS_GENERIC_HEADERCTRL