1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/slider.cpp 
   3 // Purpose:     wxSlider, using the Win95 (and later) trackbar control 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 1998 
  10 // Licence:     wxWindows licence 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  21 // For compilers that support precompilation, includes "wx.h". 
  22 #include "wx/wxprec.h" 
  30 #include "wx/slider.h" 
  33     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  37 #include "wx/msw/subwin.h" 
  39 // ---------------------------------------------------------------------------- 
  41 // ---------------------------------------------------------------------------- 
  46 // indices of labels in wxSlider::m_labels 
  55 // the gaps between the slider and the labels, in pixels 
  58 // the width of the borders including white space 
  59 const int BORDERPAD 
= 8; 
  60 // these 2 values are arbitrary: 
  64 } // anonymous namespace 
  66 // ============================================================================ 
  67 // wxSlider implementation 
  68 // ============================================================================ 
  70 // ---------------------------------------------------------------------------- 
  72 // ---------------------------------------------------------------------------- 
  87 bool wxSlider::Create(wxWindow 
*parent
, 
  95                       const wxValidator
& validator
, 
  98     wxCHECK_MSG( minValue 
< maxValue
, false, 
  99         wxT("Slider minimum must be strictly less than the maximum.") ); 
 101     // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and 
 102     // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility 
 103     // reasons we can't really change it, instead try to infer the orientation 
 104     // from the flags given to us here 
 105     switch ( style 
& (wxSL_LEFT 
| wxSL_RIGHT 
| wxSL_TOP 
| wxSL_BOTTOM
) ) 
 109             style 
|= wxSL_VERTICAL
; 
 114             style 
|= wxSL_HORIZONTAL
; 
 118             // no specific direction, do we have at least the orientation? 
 119             if ( !(style 
& (wxSL_HORIZONTAL 
| wxSL_VERTICAL
)) ) 
 121                 // no, choose default 
 122                 style 
|= wxSL_BOTTOM 
| wxSL_HORIZONTAL
; 
 126     wxASSERT_MSG( !(style 
& wxSL_VERTICAL
) || !(style 
& wxSL_HORIZONTAL
), 
 127                     wxT("incompatible slider direction and orientation") ); 
 130     // initialize everything 
 131     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 134     // ensure that we have correct values for GetLabelsSize() 
 135     m_rangeMin 
= minValue
; 
 136     m_rangeMax 
= maxValue
; 
 138     // create the labels first, so that our DoGetBestSize() could take them 
 141     // note that we could simply create 3 wxStaticTexts here but it could 
 142     // result in some observable side effects at wx level (e.g. the parent of 
 143     // wxSlider would have 3 more children than expected) and so we prefer not 
 144     // to do it like this 
 145     if ( m_windowStyle 
& wxSL_LABELS 
) 
 147         m_labels 
= new wxSubwindows(SliderLabel_Last
); 
 149         HWND hwndParent 
= GetHwndOf(parent
); 
 150         for ( size_t n 
= 0; n 
< SliderLabel_Last
; n
++ ) 
 152             wxWindowIDRef lblid 
= NewControlId(); 
 154             HWND wnd 
= ::CreateWindow
 
 158                             WS_CHILD 
| WS_VISIBLE 
| SS_CENTER
, 
 161                             (HMENU
)wxUIntToPtr(lblid
.GetValue()), 
 166             m_labels
->Set(n
, wnd
, lblid
); 
 168         m_labels
->SetFont(GetFont()); 
 171     // now create the main control too 
 172     if ( !MSWCreateControl(TRACKBAR_CLASS
, wxEmptyString
, pos
, size
) ) 
 175     // and initialize everything 
 176     SetRange(minValue
, maxValue
); 
 178     SetPageSize((maxValue 
- minValue
)/10); 
 180     // we need to position the labels correctly if we have them and if 
 181     // SetSize() hadn't been called before (when best size was determined by 
 182     // MSWCreateControl()) as in this case they haven't been put in place yet 
 183     if ( m_labels 
&& size
.x 
!= wxDefaultCoord 
&& size
.y 
!= wxDefaultCoord 
) 
 191 WXDWORD 
wxSlider::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 193     WXDWORD msStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 195     // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity 
 196     msStyle 
|= style 
& wxSL_VERTICAL 
? TBS_VERT 
: TBS_HORZ
; 
 198     if ( style 
& wxSL_BOTH 
) 
 200         // this fully specifies the style combined with TBS_VERT/HORZ above 
 203     else // choose one direction 
 205         if ( style 
& wxSL_LEFT 
) 
 207         else if ( style 
& wxSL_RIGHT 
) 
 208             msStyle 
|= TBS_RIGHT
; 
 209         else if ( style 
& wxSL_TOP 
) 
 211         else if ( style 
& wxSL_BOTTOM 
) 
 212             msStyle 
|= TBS_BOTTOM
; 
 215     if ( style 
& wxSL_AUTOTICKS 
) 
 216         msStyle 
|= TBS_AUTOTICKS
; 
 218         msStyle 
|= TBS_NOTICKS
; 
 220     if ( style 
& wxSL_SELRANGE 
) 
 221         msStyle 
|= TBS_ENABLESELRANGE
; 
 226 wxSlider::~wxSlider() 
 231 // ---------------------------------------------------------------------------- 
 233 // ---------------------------------------------------------------------------- 
 235 bool wxSlider::MSWOnScroll(int WXUNUSED(orientation
), 
 237                            WXWORD 
WXUNUSED(pos
), 
 240     wxEventType scrollEvent
; 
 244             scrollEvent 
= wxEVT_SCROLL_TOP
; 
 248             scrollEvent 
= wxEVT_SCROLL_BOTTOM
; 
 252             scrollEvent 
= wxEVT_SCROLL_LINEUP
; 
 256             scrollEvent 
= wxEVT_SCROLL_LINEDOWN
; 
 260             scrollEvent 
= wxEVT_SCROLL_PAGEUP
; 
 264             scrollEvent 
= wxEVT_SCROLL_PAGEDOWN
; 
 268             scrollEvent 
= wxEVT_SCROLL_THUMBTRACK
; 
 272         case SB_THUMBPOSITION
: 
 275                 scrollEvent 
= wxEVT_SCROLL_THUMBRELEASE
; 
 276                 m_isDragging 
= false; 
 280                 // this seems to only happen when the mouse wheel is used: in 
 281                 // this case, as it might be unexpected to get THUMBRELEASE 
 282                 // without preceding THUMBTRACKs, we don't generate it at all 
 283                 // but generate CHANGED event because the control itself does 
 284                 // not send us SB_ENDSCROLL for whatever reason when mouse 
 286                 scrollEvent 
= wxEVT_SCROLL_CHANGED
; 
 291             scrollEvent 
= wxEVT_SCROLL_CHANGED
; 
 295             // unknown scroll event? 
 299     int newPos 
= ValueInvertOrNot((int) ::SendMessage((HWND
) control
, TBM_GETPOS
, 0, 0)); 
 300     if ( (newPos 
< GetMin()) || (newPos 
> GetMax()) ) 
 302         // out of range - but we did process it 
 308     wxScrollEvent 
event(scrollEvent
, m_windowId
); 
 309     event
.SetPosition(newPos
); 
 310     event
.SetEventObject( this ); 
 311     HandleWindowEvent(event
); 
 313     wxCommandEvent 
cevent( wxEVT_COMMAND_SLIDER_UPDATED
, GetId() ); 
 314     cevent
.SetInt( newPos 
); 
 315     cevent
.SetEventObject( this ); 
 317     return HandleWindowEvent( cevent 
); 
 320 void wxSlider::Command (wxCommandEvent 
& event
) 
 322     SetValue (event
.GetInt()); 
 323     ProcessCommand (event
); 
 326 // ---------------------------------------------------------------------------- 
 328 // ---------------------------------------------------------------------------- 
 330 wxRect 
wxSlider::GetBoundingBox() const 
 332     // take care not to call our own functions which would call us recursively 
 334     wxSliderBase::DoGetPosition(&x
, &y
); 
 335     wxSliderBase::DoGetSize(&w
, &h
); 
 337     wxRect 
rect(x
, y
, w
, h
); 
 340         wxRect lrect 
= m_labels
->GetBoundingBox(); 
 341         GetParent()->ScreenToClient(&lrect
.x
, &lrect
.y
); 
 348 void wxSlider::DoGetSize(int *width
, int *height
) const 
 350     wxRect rect 
= GetBoundingBox(); 
 355         *height 
= rect
.height
; 
 358 void wxSlider::DoGetPosition(int *x
, int *y
) const 
 360     wxRect rect 
= GetBoundingBox(); 
 368 int wxSlider::GetLabelsSize(int *widthMin
, int *widthMax
) const 
 370     if ( widthMin 
&& widthMax 
) 
 372         *widthMin 
= GetTextExtent(Format(m_rangeMin
)).x
; 
 373         *widthMax 
= GetTextExtent(Format(m_rangeMax
)).x
; 
 376     return HasFlag(wxSL_LABELS
) ? GetCharHeight() : 0; 
 379 void wxSlider::DoMoveWindow(int x
, int y
, int width
, int height
) 
 381     // all complications below are because we need to position the labels, 
 382     // without them everything is easy 
 385         wxSliderBase::DoMoveWindow(x
, y
, width
, height
); 
 391     const int labelHeight 
= GetLabelsSize(&minLabelWidth
, &maxLabelWidth
); 
 392     const int longestLabelWidth 
= wxMax(minLabelWidth
, maxLabelWidth
); 
 393     if ( !HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 401     if ( HasFlag(wxSL_TICKS
)) 
 403     if ( HasFlag(wxSL_BOTH
)) 
 406     // be careful to position the slider itself after moving the labels as 
 407     // otherwise our GetBoundingBox(), which is called from WM_SIZE handler, 
 408     // would return a wrong result and wrong size would be cached internally 
 409     if ( HasFlag(wxSL_VERTICAL
) ) 
 415         int xLabel 
= (wxMax((THUMB 
+ (BORDERPAD 
* 2)), longestLabelWidth
) / 2) - 
 416             (longestLabelWidth 
/ 2) + x
; 
 417         if ( HasFlag(wxSL_LEFT
) ) 
 419             if ( HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 422                 holdTopWidth 
= minLabelWidth
; 
 423                 holdBottomX 
= xLabel 
- ((maxLabelWidth 
- minLabelWidth
) / 2); 
 424                 holdBottomWidth 
= maxLabelWidth
; 
 425                 if ( HasFlag(wxSL_INVERSE
) ) 
 427                     wxSwap(holdTopWidth
, holdBottomWidth
); 
 428                     wxSwap(holdTopX
, holdBottomX
); 
 430                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 433                     holdTopWidth
, labelHeight
); 
 434                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 436                     y 
+ height 
- labelHeight
, 
 437                     holdBottomWidth
, labelHeight
); 
 439             if ( HasFlag(wxSL_VALUE_LABEL
) ) 
 440                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 441                     x 
+ THUMB 
+ tickOffset 
+ HGAP
, 
 442                     y 
+ (height 
- labelHeight
)/2, 
 443                     longestLabelWidth
, labelHeight
); 
 447             if ( HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 449                 holdTopX 
= xLabel 
+ longestLabelWidth 
+ ((maxLabelWidth 
- minLabelWidth
) / 2); 
 450                 holdTopWidth 
= minLabelWidth
; 
 451                 holdBottomX 
= xLabel 
+ longestLabelWidth
; 
 452                 holdBottomWidth 
= maxLabelWidth
; 
 453                 if ( HasFlag(wxSL_INVERSE
) ) 
 455                     wxSwap(holdTopWidth
, holdBottomWidth
); 
 456                     wxSwap(holdTopX
, holdBottomX
); 
 458                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 461                     holdTopWidth
, labelHeight
); 
 462                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 464                     y 
+ height 
- labelHeight
, 
 465                     holdBottomWidth
, labelHeight
); 
 467             if ( HasFlag(wxSL_VALUE_LABEL
) ) 
 468                 labelOffset 
= longestLabelWidth 
+ HGAP
; 
 469                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 471                 y 
+ (height 
- labelHeight
)/2, 
 472                 longestLabelWidth
, labelHeight
); 
 475         // position the slider itself along the left/right edge 
 476         wxSliderBase::DoMoveWindow( 
 479             THUMB 
+ tickOffset 
+ HGAP
, 
 480             height 
- (labelHeight 
* 2)); 
 489             (y 
+ ((THUMB 
+ tickOffset
) / 2)) - (labelHeight 
/ 2); 
 492             ((width  
- (minLabelWidth 
+ maxLabelWidth
)) / 2) - 
 497         if ( HasFlag(wxSL_BOTTOM
) ) 
 499             if ( HasFlag(wxSL_VALUE_LABEL
) ) 
 501                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 504                     longestLabelWidth
, labelHeight
); 
 506                 ySlider 
+= labelHeight
; 
 507                 yLabelMinMax 
+= labelHeight
; 
 510             if ( HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 513                 holdLeftWidth 
= minLabelWidth
; 
 514                 holdRightX 
= x 
+ width 
- maxLabelWidth
; 
 515                 holdRightWidth 
= maxLabelWidth
; 
 516                 if ( HasFlag(wxSL_INVERSE
) ) 
 518                     wxSwap(holdLeftWidth
, holdRightWidth
); 
 519                     wxSwap(holdLeftX
, holdRightX
); 
 521                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 524                     holdLeftWidth
, labelHeight
); 
 525                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 528                     holdRightWidth
, labelHeight
); 
 533             if ( HasFlag(wxSL_VALUE_LABEL
) ) 
 535                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 537                     y 
+ THUMB 
+ tickOffset
, 
 538                     longestLabelWidth
, labelHeight
); 
 541             if ( HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 544                 holdLeftWidth 
= minLabelWidth
; 
 545                 holdRightX 
= x 
+ width 
- maxLabelWidth
; 
 546                 holdRightWidth 
= maxLabelWidth
; 
 547                 if ( HasFlag(wxSL_INVERSE
) ) 
 549                     wxSwap(holdLeftWidth
, holdRightWidth
); 
 550                     wxSwap(holdLeftX
, holdRightX
); 
 552                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 555                     holdLeftWidth
, labelHeight
); 
 556                 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 559                     holdRightWidth
, labelHeight
); 
 563         // position the slider itself along the top/bottom edge 
 564         if ( HasFlag(wxSL_MIN_MAX_LABELS
) || HasFlag(wxSL_VALUE_LABEL
) ) 
 565             labelOffset 
= labelHeight
; 
 566         wxSliderBase::DoMoveWindow( 
 567             x 
+ minLabelWidth 
+ VGAP
, 
 569             width  
- (minLabelWidth 
+ maxLabelWidth  
+ (VGAP
*2)), 
 574 wxSize 
wxSlider::DoGetBestSize() const 
 576     // this value is arbitrary: 
 577     static const int length 
= 100; 
 581     if ( HasFlag(wxSL_VERTICAL
) ) 
 591             int hLabel 
= GetLabelsSize(&widthMin
, &widthMax
); 
 593             // account for the labels 
 594             if ( HasFlag(wxSL_MIN_MAX_LABELS
) ) 
 595                 size
.x 
+= HGAP 
+ wxMax(widthMin
, widthMax
); 
 597             // labels are indented relative to the slider itself 
 609             int labelSize 
= GetLabelsSize(); 
 611             // Min/max labels are compensated by the ticks so we don't need 
 612             // extra space for them if we're also showing ticks. 
 613             if ( HasFlag(wxSL_MIN_MAX_LABELS
) && !HasFlag(wxSL_TICKS
) ) 
 616             // The value label is always on top of the control and so does need 
 617             // extra space in any case. 
 618             if ( HasFlag(wxSL_VALUE_LABEL
) ) 
 623     // need extra space to show ticks 
 624     if ( HasFlag(wxSL_TICKS
) ) 
 627         // and maybe twice as much if we show them on both sides 
 628         if ( HasFlag(wxSL_BOTH
) ) 
 634 // ---------------------------------------------------------------------------- 
 635 // slider-specific methods 
 636 // ---------------------------------------------------------------------------- 
 638 int wxSlider::GetValue() const 
 640     return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS
, 0, 0)); 
 643 void wxSlider::SetValue(int value
) 
 645     ::SendMessage(GetHwnd(), TBM_SETPOS
, (WPARAM
)TRUE
, (LPARAM
)ValueInvertOrNot(value
)); 
 649         ::SetWindowText((*m_labels
)[SliderLabel_Value
], Format(value
).wx_str()); 
 653 void wxSlider::SetRange(int minValue
, int maxValue
) 
 655     // Remember the old logical value if we need to update the physical control 
 656     // value after changing its range in wxSL_INVERSE case (and avoid an 
 657     // unnecessary call to GetValue() otherwise as it's just not needed). 
 658     const int valueOld 
= HasFlag(wxSL_INVERSE
) ? GetValue() : 0; 
 660     m_rangeMin 
= minValue
; 
 661     m_rangeMax 
= maxValue
; 
 663     ::SendMessage(GetHwnd(), TBM_SETRANGEMIN
, TRUE
, m_rangeMin
); 
 664     ::SendMessage(GetHwnd(), TBM_SETRANGEMAX
, TRUE
, m_rangeMax
); 
 668         ::SetWindowText((*m_labels
)[SliderLabel_Min
], 
 669                         Format(ValueInvertOrNot(m_rangeMin
)).wx_str()); 
 670         ::SetWindowText((*m_labels
)[SliderLabel_Max
], 
 671                         Format(ValueInvertOrNot(m_rangeMax
)).wx_str()); 
 674     // When emulating wxSL_INVERSE style in wxWidgets, we need to update the 
 675     // value after changing the range to ensure that the value seen by the user 
 676     // code, i.e. the one returned by GetValue(), does not change. 
 677     if ( HasFlag(wxSL_INVERSE
) ) 
 679         ::SendMessage(GetHwnd(), TBM_SETPOS
, TRUE
, ValueInvertOrNot(valueOld
)); 
 683 void wxSlider::DoSetTickFreq(int n
) 
 686     ::SendMessage( GetHwnd(), TBM_SETTICFREQ
, (WPARAM
) n
, (LPARAM
) 0 ); 
 689 void wxSlider::SetPageSize(int pageSize
) 
 691     ::SendMessage( GetHwnd(), TBM_SETPAGESIZE
, (WPARAM
) 0, (LPARAM
) pageSize 
); 
 692     m_pageSize 
= pageSize
; 
 695 int wxSlider::GetPageSize() const 
 700 void wxSlider::ClearSel() 
 702     ::SendMessage(GetHwnd(), TBM_CLEARSEL
, (WPARAM
) TRUE
, (LPARAM
) 0); 
 705 void wxSlider::ClearTicks() 
 707     ::SendMessage(GetHwnd(), TBM_CLEARTICS
, (WPARAM
) TRUE
, (LPARAM
) 0); 
 710 void wxSlider::SetLineSize(int lineSize
) 
 712     m_lineSize 
= lineSize
; 
 713     ::SendMessage(GetHwnd(), TBM_SETLINESIZE
, (WPARAM
) 0, (LPARAM
) lineSize
); 
 716 int wxSlider::GetLineSize() const 
 718     return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE
, 0, 0); 
 721 int wxSlider::GetSelEnd() const 
 723     return (int)::SendMessage(GetHwnd(), TBM_GETSELEND
, 0, 0); 
 726 int wxSlider::GetSelStart() const 
 728     return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART
, 0, 0); 
 731 void wxSlider::SetSelection(int minPos
, int maxPos
) 
 733     ::SendMessage(GetHwnd(), TBM_SETSEL
, 
 734                   (WPARAM
) TRUE 
/* redraw */, 
 735                   (LPARAM
) MAKELONG( minPos
, maxPos
) ); 
 738 void wxSlider::SetThumbLength(int len
) 
 740     ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH
, (WPARAM
) len
, (LPARAM
) 0); 
 743 int wxSlider::GetThumbLength() const 
 745     return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH
, 0, 0); 
 748 void wxSlider::SetTick(int tickPos
) 
 750     ::SendMessage( GetHwnd(), TBM_SETTIC
, (WPARAM
) 0, (LPARAM
) tickPos 
); 
 753 // ---------------------------------------------------------------------------- 
 754 // composite control methods 
 755 // ---------------------------------------------------------------------------- 
 757 WXHWND 
wxSlider::GetStaticMin() const 
 759     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Min
] : NULL
; 
 762 WXHWND 
wxSlider::GetStaticMax() const 
 764     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Max
] : NULL
; 
 767 WXHWND 
wxSlider::GetEditValue() const 
 769     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Value
] : NULL
; 
 772 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider
, wxSliderBase
, m_labels
) 
 774 #endif // wxUSE_SLIDER