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 // ---------------------------------------------------------------------------- 
  43 // indices of labels in wxSlider::m_labels 
  52 // the gap between the slider and the labels, in pixels 
  53 static const int HGAP 
= 5; 
  55 // ---------------------------------------------------------------------------- 
  57 // ---------------------------------------------------------------------------- 
  59 #if wxUSE_EXTENDED_RTTI 
  60 WX_DEFINE_FLAGS( wxSliderStyle 
) 
  62 wxBEGIN_FLAGS( wxSliderStyle 
) 
  63     // new style border flags, we put them first to 
  64     // use them for streaming out 
  65     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  66     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  67     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  68     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  69     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  70     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  72     // old style border flags 
  73     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  74     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  75     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  76     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  77     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  78     wxFLAGS_MEMBER(wxBORDER
) 
  80     // standard window styles 
  81     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  82     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  83     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  84     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  85     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  86     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  87     wxFLAGS_MEMBER(wxVSCROLL
) 
  88     wxFLAGS_MEMBER(wxHSCROLL
) 
  90     wxFLAGS_MEMBER(wxSL_HORIZONTAL
) 
  91     wxFLAGS_MEMBER(wxSL_VERTICAL
) 
  92     wxFLAGS_MEMBER(wxSL_AUTOTICKS
) 
  93     wxFLAGS_MEMBER(wxSL_LABELS
) 
  94     wxFLAGS_MEMBER(wxSL_LEFT
) 
  95     wxFLAGS_MEMBER(wxSL_TOP
) 
  96     wxFLAGS_MEMBER(wxSL_RIGHT
) 
  97     wxFLAGS_MEMBER(wxSL_BOTTOM
) 
  98     wxFLAGS_MEMBER(wxSL_BOTH
) 
  99     wxFLAGS_MEMBER(wxSL_SELRANGE
) 
 100     wxFLAGS_MEMBER(wxSL_INVERSE
) 
 102 wxEND_FLAGS( wxSliderStyle 
) 
 104 IMPLEMENT_DYNAMIC_CLASS_XTI(wxSlider
, wxControl
,"wx/slider.h") 
 106 wxBEGIN_PROPERTIES_TABLE(wxSlider
) 
 107     wxEVENT_RANGE_PROPERTY( Scroll 
, wxEVT_SCROLL_TOP 
, wxEVT_SCROLL_CHANGED 
, wxScrollEvent 
) 
 108     wxEVENT_PROPERTY( Updated 
, wxEVT_COMMAND_SLIDER_UPDATED 
, wxCommandEvent 
) 
 110     wxPROPERTY( Value 
, int , SetValue
, GetValue 
, 0, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 111     wxPROPERTY( Minimum 
, int , SetMin
, GetMin
, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 112     wxPROPERTY( Maximum 
, int , SetMax
, GetMax
, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 113     wxPROPERTY( PageSize 
, int , SetPageSize
, GetLineSize
, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 114     wxPROPERTY( LineSize 
, int , SetLineSize
, GetLineSize
, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 115     wxPROPERTY( ThumbLength 
, int , SetThumbLength
, GetThumbLength
, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 116     wxPROPERTY_FLAGS( WindowStyle 
, wxSliderStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 117 wxEND_PROPERTIES_TABLE() 
 119 wxBEGIN_HANDLERS_TABLE(wxSlider
) 
 120 wxEND_HANDLERS_TABLE() 
 122 wxCONSTRUCTOR_8( wxSlider 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, int , Value 
, int , Minimum 
, int , Maximum 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle 
) 
 124 IMPLEMENT_DYNAMIC_CLASS(wxSlider
, wxControl
) 
 127 // ============================================================================ 
 128 // wxSlider implementation 
 129 // ============================================================================ 
 131 // ---------------------------------------------------------------------------- 
 133 // ---------------------------------------------------------------------------- 
 135 void wxSlider::Init() 
 145     m_isDragging 
= false; 
 149 wxSlider::Create(wxWindow 
*parent
, 
 157                  const wxValidator
& validator
, 
 158                  const wxString
& name
) 
 160     // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and 
 161     // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility 
 162     // reasons we can't really change it, instead try to infer the orientation 
 163     // from the flags given to us here 
 164     switch ( style 
& (wxSL_LEFT 
| wxSL_RIGHT 
| wxSL_TOP 
| wxSL_BOTTOM
) ) 
 168             style 
|= wxSL_VERTICAL
; 
 173             style 
|= wxSL_HORIZONTAL
; 
 177             // no specific direction, do we have at least the orientation? 
 178             if ( !(style 
& (wxSL_HORIZONTAL 
| wxSL_VERTICAL
)) ) 
 180                 // no, choose default 
 181                 style 
|= wxSL_BOTTOM 
| wxSL_HORIZONTAL
; 
 185     wxASSERT_MSG( !(style 
& wxSL_VERTICAL
) || !(style 
& wxSL_HORIZONTAL
), 
 186                     _T("incompatible slider direction and orientation") ); 
 189     // initialize everything 
 190     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 193     // ensure that we have correct values for GetLabelsSize() 
 194     m_rangeMin 
= minValue
; 
 195     m_rangeMax 
= maxValue
; 
 197     // create the labels first, so that our DoGetBestSize() could take them 
 200     // note that we could simply create 3 wxStaticTexts here but it could 
 201     // result in some observable side effects at wx level (e.g. the parent of 
 202     // wxSlider would have 3 more children than expected) and so we prefer not 
 203     // to do it like this 
 204     if ( m_windowStyle 
& wxSL_LABELS 
) 
 206         m_labels 
= new wxSubwindows(SliderLabel_Last
); 
 208         HWND hwndParent 
= GetHwndOf(parent
); 
 209         for ( size_t n 
= 0; n 
< SliderLabel_Last
; n
++ ) 
 211             (*m_labels
)[n
] = ::CreateWindow
 
 215                                     WS_CHILD 
| WS_VISIBLE 
| SS_CENTER
, 
 218                                     (HMENU
)NewControlId(), 
 224         m_labels
->SetFont(GetFont()); 
 227     // now create the main control too 
 228     if ( !MSWCreateControl(TRACKBAR_CLASS
, wxEmptyString
, pos
, size
) ) 
 231     // and initialize everything 
 232     SetRange(minValue
, maxValue
); 
 234     SetPageSize((maxValue 
- minValue
)/10); 
 236     // we need to position the labels correctly if we have them and if 
 237     // SetSize() hadn't been called before (when best size was determined by 
 238     // MSWCreateControl()) as in this case they haven't been put in place yet 
 239     if ( m_labels 
&& size
.x 
!= wxDefaultCoord 
&& size
.y 
!= wxDefaultCoord 
) 
 247 WXDWORD 
wxSlider::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 249     WXDWORD msStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 251     // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity 
 252     msStyle 
|= style 
& wxSL_VERTICAL 
? TBS_VERT 
: TBS_HORZ
; 
 254     if ( style 
& wxSL_BOTH 
) 
 256         // this fully specifies the style combined with TBS_VERT/HORZ above 
 259     else // choose one direction 
 261         if ( style 
& wxSL_LEFT 
) 
 263         else if ( style 
& wxSL_RIGHT 
) 
 264             msStyle 
|= TBS_RIGHT
; 
 265         else if ( style 
& wxSL_TOP 
) 
 267         else if ( style 
& wxSL_BOTTOM 
) 
 268             msStyle 
|= TBS_BOTTOM
; 
 271     if ( style 
& wxSL_AUTOTICKS 
) 
 272         msStyle 
|= TBS_AUTOTICKS
; 
 274         msStyle 
|= TBS_NOTICKS
; 
 276     if ( style 
& wxSL_SELRANGE 
) 
 277         msStyle 
|= TBS_ENABLESELRANGE
; 
 282 wxSlider::~wxSlider() 
 287 // ---------------------------------------------------------------------------- 
 289 // ---------------------------------------------------------------------------- 
 291 bool wxSlider::MSWOnScroll(int WXUNUSED(orientation
), 
 293                            WXWORD 
WXUNUSED(pos
), 
 296     wxEventType scrollEvent
; 
 300             scrollEvent 
= wxEVT_SCROLL_TOP
; 
 304             scrollEvent 
= wxEVT_SCROLL_BOTTOM
; 
 308             scrollEvent 
= wxEVT_SCROLL_LINEUP
; 
 312             scrollEvent 
= wxEVT_SCROLL_LINEDOWN
; 
 316             scrollEvent 
= wxEVT_SCROLL_PAGEUP
; 
 320             scrollEvent 
= wxEVT_SCROLL_PAGEDOWN
; 
 324             scrollEvent 
= wxEVT_SCROLL_THUMBTRACK
; 
 328         case SB_THUMBPOSITION
: 
 331                 scrollEvent 
= wxEVT_SCROLL_THUMBRELEASE
; 
 332                 m_isDragging 
= false; 
 336                 // this seems to only happen when the mouse wheel is used: in 
 337                 // this case, as it might be unexpected to get THUMBRELEASE 
 338                 // without preceding THUMBTRACKs, we don't generate it at all 
 339                 // but generate CHANGED event because the control itself does 
 340                 // not send us SB_ENDSCROLL for whatever reason when mouse 
 342                 scrollEvent 
= wxEVT_SCROLL_CHANGED
; 
 347             scrollEvent 
= wxEVT_SCROLL_CHANGED
; 
 351             // unknown scroll event? 
 355     int newPos 
= ValueInvertOrNot((int) ::SendMessage((HWND
) control
, TBM_GETPOS
, 0, 0)); 
 356     if ( (newPos 
< GetMin()) || (newPos 
> GetMax()) ) 
 358         // out of range - but we did process it 
 364     wxScrollEvent 
event(scrollEvent
, m_windowId
); 
 365     event
.SetPosition(newPos
); 
 366     event
.SetEventObject( this ); 
 367     GetEventHandler()->ProcessEvent(event
); 
 369     wxCommandEvent 
cevent( wxEVT_COMMAND_SLIDER_UPDATED
, GetId() ); 
 370     cevent
.SetInt( newPos 
); 
 371     cevent
.SetEventObject( this ); 
 373     return GetEventHandler()->ProcessEvent( cevent 
); 
 376 void wxSlider::Command (wxCommandEvent 
& event
) 
 378     SetValue (event
.GetInt()); 
 379     ProcessCommand (event
); 
 382 // ---------------------------------------------------------------------------- 
 384 // ---------------------------------------------------------------------------- 
 386 wxRect 
wxSlider::GetBoundingBox() const 
 388     // take care not to call our own functions which would call us recursively 
 390     wxSliderBase::DoGetPosition(&x
, &y
); 
 391     wxSliderBase::DoGetSize(&w
, &h
); 
 393     wxRect 
rect(x
, y
, w
, h
); 
 396         wxRect lrect 
= m_labels
->GetBoundingBox(); 
 397         GetParent()->ScreenToClient(&lrect
.x
, &lrect
.y
); 
 404 void wxSlider::DoGetSize(int *width
, int *height
) const 
 406     wxRect rect 
= GetBoundingBox(); 
 411         *height 
= rect
.height
; 
 414 void wxSlider::DoGetPosition(int *x
, int *y
) const 
 416     wxRect rect 
= GetBoundingBox(); 
 424 int wxSlider::GetLabelsSize(int *width
) const 
 430         // find the max label width 
 431         int wLabelMin
, wLabelMax
; 
 432         GetTextExtent(Format(m_rangeMin
), &wLabelMin
, &cy
); 
 433         GetTextExtent(Format(m_rangeMax
), &wLabelMax
, &cy
); 
 435         *width 
= wxMax(wLabelMin
, wLabelMax
); 
 439         cy 
= GetCharHeight(); 
 442     return EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
); 
 445 void wxSlider::DoMoveWindow(int x
, int y
, int width
, int height
) 
 447     // all complications below are because we need to position the labels, 
 448     // without them everything is easy 
 451         wxSliderBase::DoMoveWindow(x
, y
, width
, height
); 
 455     // be careful to position the slider itself after moving the labels as 
 456     // otherwise our GetBoundingBox(), which is called from WM_SIZE handler, 
 457     // would return a wrong result and wrong size would be cached internally 
 458     if ( HasFlag(wxSL_VERTICAL
) ) 
 461         int hLabel 
= GetLabelsSize(&wLabel
); 
 463         int xLabel 
= HasFlag(wxSL_LEFT
) ? x 
+ width 
- wLabel 
: x
; 
 465         // position all labels: min at the top, value in the middle and max at 
 467         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 468                      xLabel
, y
, wLabel
, hLabel
); 
 470         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 471                      xLabel
, y 
+ (height 
- hLabel
)/2, wLabel
, hLabel
); 
 473         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 474                       xLabel
, y 
+ height 
- hLabel
, wLabel
, hLabel
); 
 476         // position the slider itself along the left/right edge 
 477         wxSliderBase::DoMoveWindow(HasFlag(wxSL_LEFT
) ? x 
: x 
+ wLabel 
+ HGAP
, 
 479                                    width 
- wLabel 
- HGAP
, 
 485         int hLabel 
= GetLabelsSize(&wLabel
); 
 487         int yLabel 
= HasFlag(wxSL_TOP
) ? y 
+ height 
- hLabel 
: y
; 
 489         // position all labels: min on the left, value in the middle and max to 
 491         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
], 
 492                       x
, yLabel
, wLabel
, hLabel
); 
 494         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
], 
 495                       x 
+ (width 
- wLabel
)/2, yLabel
, wLabel
, hLabel
); 
 497         DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
], 
 498                       x 
+ width 
- wLabel
, yLabel
, wLabel
, hLabel
); 
 500         // position the slider itself along the top/bottom edge 
 501         wxSliderBase::DoMoveWindow(x
, 
 502                                    HasFlag(wxSL_TOP
) ? y 
: y 
+ hLabel
, 
 508 wxSize 
wxSlider::DoGetBestSize() const 
 510     // these values are arbitrary 
 511     static const int length 
= 100; 
 512     static const int thumb 
= 24; 
 513     static const int ticks 
= 8; 
 517     if ( HasFlag(wxSL_VERTICAL
) ) 
 526             int hLabel 
= GetLabelsSize(&wLabel
); 
 528             // account for the labels 
 529             size
.x 
+= HGAP 
+ wLabel
; 
 531             // labels are indented relative to the slider itself 
 543             // labels add extra height 
 544             size
.y 
+= GetLabelsSize(); 
 548     // need extra space to show ticks 
 549     if ( HasFlag(wxSL_TICKS
) ) 
 553         // and maybe twice as much if we show them on both sides 
 554         if ( HasFlag(wxSL_BOTH
) ) 
 561 // ---------------------------------------------------------------------------- 
 562 // slider-specific methods 
 563 // ---------------------------------------------------------------------------- 
 565 int wxSlider::GetValue() const 
 567     return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS
, 0, 0)); 
 570 void wxSlider::SetValue(int value
) 
 572     ::SendMessage(GetHwnd(), TBM_SETPOS
, (WPARAM
)TRUE
, (LPARAM
)ValueInvertOrNot(value
)); 
 576         ::SetWindowText((*m_labels
)[SliderLabel_Value
], Format(value
)); 
 580 void wxSlider::SetRange(int minValue
, int maxValue
) 
 582     m_rangeMin 
= minValue
; 
 583     m_rangeMax 
= maxValue
; 
 585     ::SendMessage(GetHwnd(), TBM_SETRANGEMIN
, TRUE
, m_rangeMin
); 
 586     ::SendMessage(GetHwnd(), TBM_SETRANGEMAX
, TRUE
, m_rangeMax
); 
 590         ::SetWindowText((*m_labels
)[SliderLabel_Min
], Format(ValueInvertOrNot(m_rangeMin
))); 
 591         ::SetWindowText((*m_labels
)[SliderLabel_Max
], Format(ValueInvertOrNot(m_rangeMax
))); 
 595 void wxSlider::SetTickFreq(int n
, int pos
) 
 598     ::SendMessage( GetHwnd(), TBM_SETTICFREQ
, (WPARAM
) n
, (LPARAM
) pos 
); 
 601 void wxSlider::SetPageSize(int pageSize
) 
 603     ::SendMessage( GetHwnd(), TBM_SETPAGESIZE
, (WPARAM
) 0, (LPARAM
) pageSize 
); 
 604     m_pageSize 
= pageSize
; 
 607 int wxSlider::GetPageSize() const 
 612 void wxSlider::ClearSel() 
 614     ::SendMessage(GetHwnd(), TBM_CLEARSEL
, (WPARAM
) TRUE
, (LPARAM
) 0); 
 617 void wxSlider::ClearTicks() 
 619     ::SendMessage(GetHwnd(), TBM_CLEARTICS
, (WPARAM
) TRUE
, (LPARAM
) 0); 
 622 void wxSlider::SetLineSize(int lineSize
) 
 624     m_lineSize 
= lineSize
; 
 625     ::SendMessage(GetHwnd(), TBM_SETLINESIZE
, (WPARAM
) 0, (LPARAM
) lineSize
); 
 628 int wxSlider::GetLineSize() const 
 630     return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE
, 0, 0); 
 633 int wxSlider::GetSelEnd() const 
 635     return (int)::SendMessage(GetHwnd(), TBM_GETSELEND
, 0, 0); 
 638 int wxSlider::GetSelStart() const 
 640     return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART
, 0, 0); 
 643 void wxSlider::SetSelection(int minPos
, int maxPos
) 
 645     ::SendMessage(GetHwnd(), TBM_SETSEL
, 
 646                   (WPARAM
) TRUE 
/* redraw */, 
 647                   (LPARAM
) MAKELONG( minPos
, maxPos
) ); 
 650 void wxSlider::SetThumbLength(int len
) 
 652     ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH
, (WPARAM
) len
, (LPARAM
) 0); 
 655 int wxSlider::GetThumbLength() const 
 657     return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH
, 0, 0); 
 660 void wxSlider::SetTick(int tickPos
) 
 662     ::SendMessage( GetHwnd(), TBM_SETTIC
, (WPARAM
) 0, (LPARAM
) tickPos 
); 
 665 // ---------------------------------------------------------------------------- 
 666 // composite control methods 
 667 // ---------------------------------------------------------------------------- 
 669 WXHWND 
wxSlider::GetStaticMin() const 
 671     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Min
] : NULL
; 
 674 WXHWND 
wxSlider::GetStaticMax() const 
 676     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Max
] : NULL
; 
 679 WXHWND 
wxSlider::GetEditValue() const 
 681     return m_labels 
? (WXHWND
)(*m_labels
)[SliderLabel_Value
] : NULL
; 
 684 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider
, wxSliderBase
, m_labels
) 
 686 #endif // wxUSE_SLIDER