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); 
 242 void wxHeaderCtrl::EndResizing(int width
) 
 244     wxASSERT_MSG( m_colBeingResized 
!= COL_NONE
, 
 245                   "shouldn't be called if we're not resizing" ); 
 249     wxHeaderCtrlEvent 
event(wxEVT_COMMAND_HEADER_END_DRAG
, GetId()); 
 250     event
.SetEventObject(this); 
 251     event
.SetColumn(m_colBeingResized
); 
 253         event
.SetCancelled(); 
 255         event
.SetWidth(width
); 
 257     GetEventHandler()->ProcessEvent(event
); 
 259     m_colBeingResized 
= COL_NONE
; 
 262 // ---------------------------------------------------------------------------- 
 263 // wxHeaderCtrl event handlers 
 264 // ---------------------------------------------------------------------------- 
 266 BEGIN_EVENT_TABLE(wxHeaderCtrl
, wxHeaderCtrlBase
) 
 267     EVT_PAINT(wxHeaderCtrl::OnPaint
) 
 269     EVT_MOUSE_EVENTS(wxHeaderCtrl::OnMouse
) 
 271     EVT_MOUSE_CAPTURE_LOST(wxHeaderCtrl::OnCaptureLost
) 
 274 void wxHeaderCtrl::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
 277     GetClientSize(&w
, &h
); 
 279     wxAutoBufferedPaintDC 
dc(this); 
 281     dc
.SetBackground(GetBackgroundColour()); 
 284     // account for the horizontal scrollbar offset in the parent window 
 285     dc
.SetDeviceOrigin(m_scrollOffset
, 0); 
 287     const unsigned int count 
= m_numColumns
; 
 289     for ( unsigned int i 
= 0; i 
< count
; i
++ ) 
 291         const wxHeaderColumnBase
& col 
= GetColumn(i
); 
 292         if ( col
.IsHidden() ) 
 295         const int colWidth 
= col
.GetWidth(); 
 297         wxHeaderSortIconType sortArrow
; 
 298         if ( col
.IsSortKey() ) 
 300             sortArrow 
= col
.IsSortOrderAscending() ? wxHDR_SORT_ICON_UP
 
 301                                                    : wxHDR_SORT_ICON_DOWN
; 
 303         else // not sorting by this column 
 305             sortArrow 
= wxHDR_SORT_ICON_NONE
; 
 312                 state 
= wxCONTROL_CURRENT
; 
 316             state 
= wxCONTROL_DISABLED
; 
 319         wxHeaderButtonParams params
; 
 320         params
.m_labelText 
= col
.GetTitle(); 
 321         params
.m_labelBitmap 
= col
.GetBitmap(); 
 322         params
.m_labelAlignment 
= col
.GetAlignment(); 
 324         wxRendererNative::Get().DrawHeaderButton
 
 328                                     wxRect(xpos
, 0, colWidth
, h
), 
 338 void wxHeaderCtrl::OnCaptureLost(wxMouseCaptureLostEvent
& WXUNUSED(event
)) 
 340     if ( m_colBeingResized 
!= COL_NONE 
) 
 344 void wxHeaderCtrl::OnMouse(wxMouseEvent
& mevent
) 
 346     // do this in advance to allow simply returning if we're not interested, 
 347     // we'll undo it if we do handle the event below 
 351     // account for the control displacement 
 352     const int xPhysical 
= mevent
.GetX(); 
 353     const int xLogical 
= xPhysical 
- m_scrollOffset
; 
 355     // first deal with the [continuation of any] dragging operations in 
 357     if ( m_colBeingResized 
!= COL_NONE 
) 
 359         if ( mevent
.LeftUp() ) 
 360             EndResizing(xPhysical 
- GetColStart(m_colBeingResized
)); 
 361         else // update the live separator position 
 362             UpdateResizingMarker(xPhysical
); 
 368     // find if the event is over a column at all 
 370     const unsigned col 
= mevent
.Leaving() 
 371                             ? (onSeparator 
= false, COL_NONE
) 
 372                             : FindColumnAtPos(xLogical
, onSeparator
); 
 375     // update the highlighted column if it changed 
 376     if ( col 
!= m_hover 
) 
 378         const unsigned hoverOld 
= m_hover
; 
 381         RefreshColIfNotNone(hoverOld
); 
 382         RefreshColIfNotNone(m_hover
); 
 385     // update mouse cursor as it moves around 
 386     if ( mevent
.Moving() ) 
 388         SetCursor(onSeparator 
? wxCursor(wxCURSOR_SIZEWE
) : wxNullCursor
); 
 392     // all the other events only make sense when they happen over a column 
 393     if ( col 
== COL_NONE 
) 
 397     // enter various dragging modes on left mouse press 
 398     if ( mevent
.LeftDown() ) 
 402             // start resizing the column 
 403             m_colBeingResized 
= col
; 
 404             UpdateResizingMarker(xPhysical
); 
 406         else // on column itself 
 415     // determine the type of header event corresponding to click events 
 416     wxEventType evtType 
= wxEVT_NULL
; 
 417     const bool click 
= mevent
.ButtonUp(), 
 418                dblclk 
= mevent
.ButtonDClick(); 
 419     if ( click 
|| dblclk 
) 
 421         switch ( mevent
.GetButton() ) 
 423             case wxMOUSE_BTN_LEFT
: 
 424                 // treat left double clicks on separator specially 
 425                 if ( onSeparator 
&& dblclk 
) 
 427                     evtType 
= wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
; 
 429                 else // not double click on separator 
 431                     evtType 
= click 
? wxEVT_COMMAND_HEADER_CLICK
 
 432                                     : wxEVT_COMMAND_HEADER_DCLICK
; 
 436             case wxMOUSE_BTN_RIGHT
: 
 437                 evtType 
= click 
? wxEVT_COMMAND_HEADER_RIGHT_CLICK
 
 438                                 : wxEVT_COMMAND_HEADER_RIGHT_DCLICK
; 
 441             case wxMOUSE_BTN_MIDDLE
: 
 442                 evtType 
= click 
? wxEVT_COMMAND_HEADER_MIDDLE_CLICK
 
 443                                 : wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
; 
 447                 // ignore clicks from other mouse buttons 
 452     if ( evtType 
== wxEVT_NULL 
) 
 455     wxHeaderCtrlEvent 
event(evtType
, GetId()); 
 456     event
.SetEventObject(this); 
 457     event
.SetColumn(col
); 
 459     if ( GetEventHandler()->ProcessEvent(event
) ) 
 463 #endif // wxHAS_GENERIC_HEADERCTRL