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()
146 m_isDragging
= false;
150 wxSlider::Create(wxWindow
*parent
,
158 const wxValidator
& validator
,
159 const wxString
& name
)
161 // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and
162 // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility
163 // reasons we can't really change it, instead try to infer the orientation
164 // from the flags given to us here
165 switch ( style
& (wxSL_LEFT
| wxSL_RIGHT
| wxSL_TOP
| wxSL_BOTTOM
) )
169 style
|= wxSL_VERTICAL
;
174 style
|= wxSL_HORIZONTAL
;
178 // no specific direction, do we have at least the orientation?
179 if ( !(style
& (wxSL_HORIZONTAL
| wxSL_VERTICAL
)) )
181 // no, choose default
182 style
|= wxSL_BOTTOM
| wxSL_HORIZONTAL
;
186 wxASSERT_MSG( !(style
& wxSL_VERTICAL
) || !(style
& wxSL_HORIZONTAL
),
187 _T("incompatible slider direction and orientation") );
190 // initialize everything
191 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
194 // ensure that we have correct values for GetLabelsSize()
195 m_rangeMin
= minValue
;
196 m_rangeMax
= maxValue
;
198 // create the labels first, so that our DoGetBestSize() could take them
201 // note that we could simply create 3 wxStaticTexts here but it could
202 // result in some observable side effects at wx level (e.g. the parent of
203 // wxSlider would have 3 more children than expected) and so we prefer not
204 // to do it like this
205 if ( m_windowStyle
& wxSL_LABELS
)
207 m_labels
= new wxSubwindows(SliderLabel_Last
);
208 m_labelIds
= new wxWindowIDRef
[SliderLabel_Last
];
210 HWND hwndParent
= GetHwndOf(parent
);
211 for ( size_t n
= 0; n
< SliderLabel_Last
; n
++ )
213 m_labelIds
[n
] = NewControlId();
215 (*m_labels
)[n
] = ::CreateWindow
219 WS_CHILD
| WS_VISIBLE
| SS_CENTER
,
222 (HMENU
)(wxWindowID
)m_labelIds
[n
],
228 m_labels
->SetFont(GetFont());
231 // now create the main control too
232 if ( !MSWCreateControl(TRACKBAR_CLASS
, wxEmptyString
, pos
, size
) )
235 // and initialize everything
236 SetRange(minValue
, maxValue
);
238 SetPageSize((maxValue
- minValue
)/10);
240 // we need to position the labels correctly if we have them and if
241 // SetSize() hadn't been called before (when best size was determined by
242 // MSWCreateControl()) as in this case they haven't been put in place yet
243 if ( m_labels
&& size
.x
!= wxDefaultCoord
&& size
.y
!= wxDefaultCoord
)
251 WXDWORD
wxSlider::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
253 WXDWORD msStyle
= wxControl::MSWGetStyle(style
, exstyle
);
255 // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity
256 msStyle
|= style
& wxSL_VERTICAL
? TBS_VERT
: TBS_HORZ
;
258 if ( style
& wxSL_BOTH
)
260 // this fully specifies the style combined with TBS_VERT/HORZ above
263 else // choose one direction
265 if ( style
& wxSL_LEFT
)
267 else if ( style
& wxSL_RIGHT
)
268 msStyle
|= TBS_RIGHT
;
269 else if ( style
& wxSL_TOP
)
271 else if ( style
& wxSL_BOTTOM
)
272 msStyle
|= TBS_BOTTOM
;
275 if ( style
& wxSL_AUTOTICKS
)
276 msStyle
|= TBS_AUTOTICKS
;
278 msStyle
|= TBS_NOTICKS
;
280 if ( style
& wxSL_SELRANGE
)
281 msStyle
|= TBS_ENABLESELRANGE
;
286 wxSlider::~wxSlider()
292 // ----------------------------------------------------------------------------
294 // ----------------------------------------------------------------------------
296 bool wxSlider::MSWOnScroll(int WXUNUSED(orientation
),
298 WXWORD
WXUNUSED(pos
),
301 wxEventType scrollEvent
;
305 scrollEvent
= wxEVT_SCROLL_TOP
;
309 scrollEvent
= wxEVT_SCROLL_BOTTOM
;
313 scrollEvent
= wxEVT_SCROLL_LINEUP
;
317 scrollEvent
= wxEVT_SCROLL_LINEDOWN
;
321 scrollEvent
= wxEVT_SCROLL_PAGEUP
;
325 scrollEvent
= wxEVT_SCROLL_PAGEDOWN
;
329 scrollEvent
= wxEVT_SCROLL_THUMBTRACK
;
333 case SB_THUMBPOSITION
:
336 scrollEvent
= wxEVT_SCROLL_THUMBRELEASE
;
337 m_isDragging
= false;
341 // this seems to only happen when the mouse wheel is used: in
342 // this case, as it might be unexpected to get THUMBRELEASE
343 // without preceding THUMBTRACKs, we don't generate it at all
344 // but generate CHANGED event because the control itself does
345 // not send us SB_ENDSCROLL for whatever reason when mouse
347 scrollEvent
= wxEVT_SCROLL_CHANGED
;
352 scrollEvent
= wxEVT_SCROLL_CHANGED
;
356 // unknown scroll event?
360 int newPos
= ValueInvertOrNot((int) ::SendMessage((HWND
) control
, TBM_GETPOS
, 0, 0));
361 if ( (newPos
< GetMin()) || (newPos
> GetMax()) )
363 // out of range - but we did process it
369 wxScrollEvent
event(scrollEvent
, m_windowId
);
370 event
.SetPosition(newPos
);
371 event
.SetEventObject( this );
372 HandleWindowEvent(event
);
374 wxCommandEvent
cevent( wxEVT_COMMAND_SLIDER_UPDATED
, GetId() );
375 cevent
.SetInt( newPos
);
376 cevent
.SetEventObject( this );
378 return HandleWindowEvent( cevent
);
381 void wxSlider::Command (wxCommandEvent
& event
)
383 SetValue (event
.GetInt());
384 ProcessCommand (event
);
387 // ----------------------------------------------------------------------------
389 // ----------------------------------------------------------------------------
391 wxRect
wxSlider::GetBoundingBox() const
393 // take care not to call our own functions which would call us recursively
395 wxSliderBase::DoGetPosition(&x
, &y
);
396 wxSliderBase::DoGetSize(&w
, &h
);
398 wxRect
rect(x
, y
, w
, h
);
401 wxRect lrect
= m_labels
->GetBoundingBox();
402 GetParent()->ScreenToClient(&lrect
.x
, &lrect
.y
);
409 void wxSlider::DoGetSize(int *width
, int *height
) const
411 wxRect rect
= GetBoundingBox();
416 *height
= rect
.height
;
419 void wxSlider::DoGetPosition(int *x
, int *y
) const
421 wxRect rect
= GetBoundingBox();
429 int wxSlider::GetLabelsSize(int *width
) const
435 // find the max label width
436 int wLabelMin
, wLabelMax
;
437 GetTextExtent(Format(m_rangeMin
), &wLabelMin
, &cy
);
438 GetTextExtent(Format(m_rangeMax
), &wLabelMax
, &cy
);
440 *width
= wxMax(wLabelMin
, wLabelMax
);
444 cy
= GetCharHeight();
447 return EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
);
450 void wxSlider::DoMoveWindow(int x
, int y
, int width
, int height
)
452 // all complications below are because we need to position the labels,
453 // without them everything is easy
456 wxSliderBase::DoMoveWindow(x
, y
, width
, height
);
460 // be careful to position the slider itself after moving the labels as
461 // otherwise our GetBoundingBox(), which is called from WM_SIZE handler,
462 // would return a wrong result and wrong size would be cached internally
463 if ( HasFlag(wxSL_VERTICAL
) )
466 int hLabel
= GetLabelsSize(&wLabel
);
468 int xLabel
= HasFlag(wxSL_LEFT
) ? x
+ width
- wLabel
: x
;
470 // position all labels: min at the top, value in the middle and max at
472 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
],
473 xLabel
, y
, wLabel
, hLabel
);
475 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
],
476 xLabel
, y
+ (height
- hLabel
)/2, wLabel
, hLabel
);
478 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
],
479 xLabel
, y
+ height
- hLabel
, wLabel
, hLabel
);
481 // position the slider itself along the left/right edge
482 wxSliderBase::DoMoveWindow(HasFlag(wxSL_LEFT
) ? x
: x
+ wLabel
+ HGAP
,
484 width
- wLabel
- HGAP
,
490 int hLabel
= GetLabelsSize(&wLabel
);
492 int yLabel
= HasFlag(wxSL_TOP
) ? y
+ height
- hLabel
: y
;
494 // position all labels: min on the left, value in the middle and max to
496 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Min
],
497 x
, yLabel
, wLabel
, hLabel
);
499 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Value
],
500 x
+ (width
- wLabel
)/2, yLabel
, wLabel
, hLabel
);
502 DoMoveSibling((HWND
)(*m_labels
)[SliderLabel_Max
],
503 x
+ width
- wLabel
, yLabel
, wLabel
, hLabel
);
505 // position the slider itself along the top/bottom edge
506 wxSliderBase::DoMoveWindow(x
,
507 HasFlag(wxSL_TOP
) ? y
: y
+ hLabel
,
513 wxSize
wxSlider::DoGetBestSize() const
515 // these values are arbitrary
516 static const int length
= 100;
517 static const int thumb
= 24;
518 static const int ticks
= 8;
522 if ( HasFlag(wxSL_VERTICAL
) )
531 int hLabel
= GetLabelsSize(&wLabel
);
533 // account for the labels
534 size
.x
+= HGAP
+ wLabel
;
536 // labels are indented relative to the slider itself
548 // labels add extra height
549 size
.y
+= GetLabelsSize();
553 // need extra space to show ticks
554 if ( HasFlag(wxSL_TICKS
) )
558 // and maybe twice as much if we show them on both sides
559 if ( HasFlag(wxSL_BOTH
) )
566 // ----------------------------------------------------------------------------
567 // slider-specific methods
568 // ----------------------------------------------------------------------------
570 int wxSlider::GetValue() const
572 return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS
, 0, 0));
575 void wxSlider::SetValue(int value
)
577 ::SendMessage(GetHwnd(), TBM_SETPOS
, (WPARAM
)TRUE
, (LPARAM
)ValueInvertOrNot(value
));
581 ::SetWindowText((*m_labels
)[SliderLabel_Value
], Format(value
).wx_str());
585 void wxSlider::SetRange(int minValue
, int maxValue
)
587 m_rangeMin
= minValue
;
588 m_rangeMax
= maxValue
;
590 ::SendMessage(GetHwnd(), TBM_SETRANGEMIN
, TRUE
, m_rangeMin
);
591 ::SendMessage(GetHwnd(), TBM_SETRANGEMAX
, TRUE
, m_rangeMax
);
595 ::SetWindowText((*m_labels
)[SliderLabel_Min
],
596 Format(ValueInvertOrNot(m_rangeMin
)).wx_str());
597 ::SetWindowText((*m_labels
)[SliderLabel_Max
],
598 Format(ValueInvertOrNot(m_rangeMax
)).wx_str());
602 void wxSlider::SetTickFreq(int n
, int pos
)
605 ::SendMessage( GetHwnd(), TBM_SETTICFREQ
, (WPARAM
) n
, (LPARAM
) pos
);
608 void wxSlider::SetPageSize(int pageSize
)
610 ::SendMessage( GetHwnd(), TBM_SETPAGESIZE
, (WPARAM
) 0, (LPARAM
) pageSize
);
611 m_pageSize
= pageSize
;
614 int wxSlider::GetPageSize() const
619 void wxSlider::ClearSel()
621 ::SendMessage(GetHwnd(), TBM_CLEARSEL
, (WPARAM
) TRUE
, (LPARAM
) 0);
624 void wxSlider::ClearTicks()
626 ::SendMessage(GetHwnd(), TBM_CLEARTICS
, (WPARAM
) TRUE
, (LPARAM
) 0);
629 void wxSlider::SetLineSize(int lineSize
)
631 m_lineSize
= lineSize
;
632 ::SendMessage(GetHwnd(), TBM_SETLINESIZE
, (WPARAM
) 0, (LPARAM
) lineSize
);
635 int wxSlider::GetLineSize() const
637 return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE
, 0, 0);
640 int wxSlider::GetSelEnd() const
642 return (int)::SendMessage(GetHwnd(), TBM_GETSELEND
, 0, 0);
645 int wxSlider::GetSelStart() const
647 return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART
, 0, 0);
650 void wxSlider::SetSelection(int minPos
, int maxPos
)
652 ::SendMessage(GetHwnd(), TBM_SETSEL
,
653 (WPARAM
) TRUE
/* redraw */,
654 (LPARAM
) MAKELONG( minPos
, maxPos
) );
657 void wxSlider::SetThumbLength(int len
)
659 ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH
, (WPARAM
) len
, (LPARAM
) 0);
662 int wxSlider::GetThumbLength() const
664 return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH
, 0, 0);
667 void wxSlider::SetTick(int tickPos
)
669 ::SendMessage( GetHwnd(), TBM_SETTIC
, (WPARAM
) 0, (LPARAM
) tickPos
);
672 // ----------------------------------------------------------------------------
673 // composite control methods
674 // ----------------------------------------------------------------------------
676 WXHWND
wxSlider::GetStaticMin() const
678 return m_labels
? (WXHWND
)(*m_labels
)[SliderLabel_Min
] : NULL
;
681 WXHWND
wxSlider::GetStaticMax() const
683 return m_labels
? (WXHWND
)(*m_labels
)[SliderLabel_Max
] : NULL
;
686 WXHWND
wxSlider::GetEditValue() const
688 return m_labels
? (WXHWND
)(*m_labels
)[SliderLabel_Value
] : NULL
;
691 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider
, wxSliderBase
, m_labels
)
693 #endif // wxUSE_SLIDER