]> git.saurik.com Git - wxWidgets.git/blame - src/univ/slider.cpp
Patch from Bo for recent API changes
[wxWidgets.git] / src / univ / slider.cpp
CommitLineData
1e6feb95 1///////////////////////////////////////////////////////////////////////////////
faa94f3e 2// Name: src/univ/slider.cpp
1e6feb95
VZ
3// Purpose: implementation of the universal version of wxSlider
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 09.02.01
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
65571936 9// Licence: wxWindows licence
1e6feb95
VZ
10///////////////////////////////////////////////////////////////////////////////
11
12/*
13 There is some discrepancy in wxSL_LABELS style handling between wxMSW and
14 wxGTK: the latter handles it natively and shows only the current value of
15 the slider on the side corresponding to wxSL_TOP/BOTTOM/LEFT/RIGHT style
16 given (which can be combined with wxSL_HORIZONTAL/VERTICAL) while wxMSW
17 emulates this somehow and shows the min and max values near the ends of the
18 slider and the current value in a separate static box nearby.
19
20 We currently follow wxGTK except that wxSL_HORIZONTAL slider can only have
21 the label displayed on top or bottom of it and wxSL_VERTICAL - to the left
22 or right.
23
24 What we really need is probably a more fine grain control on labels, i.e. we
d6922577 25 should be able to select if we show nothing at all, the current value only
1e6feb95 26 or the value and the limits - the current approach is just that of the
d6922577 27 lowest common denominator.
1e6feb95
VZ
28
29 TODO:
30
31 +0. add ticks support
32 1. support for all orientations
33 2. draw the slider thumb highlighted when it is dragged
34 ?3. manual ticks support?
35 */
36
37// ============================================================================
38// declarations
39// ============================================================================
40
41// ----------------------------------------------------------------------------
42// headers
43// ----------------------------------------------------------------------------
44
1e6feb95
VZ
45#include "wx/wxprec.h"
46
47#ifdef __BORLANDC__
48 #pragma hdrstop
49#endif
50
f4da9a94 51#if wxUSE_SLIDER
1e6feb95
VZ
52
53#include "wx/slider.h"
54
f4da9a94
WS
55#ifndef WX_PRECOMP
56 #include "wx/dc.h"
57#endif
1e6feb95
VZ
58
59#include "wx/univ/renderer.h"
60#include "wx/univ/inphand.h"
61#include "wx/univ/theme.h"
62
9467bdb7
VZ
63// ----------------------------------------------------------------------------
64// wxStdSliderInputHandler: default slider input handling
65// ----------------------------------------------------------------------------
66
67class WXDLLEXPORT wxStdSliderInputHandler : public wxStdInputHandler
68{
69public:
70 // default ctor
71 wxStdSliderInputHandler(wxInputHandler *inphand)
72 : wxStdInputHandler(inphand)
73 {
74 }
75
76 // base class methods
77 virtual bool HandleKey(wxInputConsumer *consumer,
78 const wxKeyEvent& event,
79 bool pressed);
80 virtual bool HandleMouse(wxInputConsumer *consumer,
81 const wxMouseEvent& event);
82 virtual bool HandleMouseMove(wxInputConsumer *consumer,
83 const wxMouseEvent& event);
84
85 virtual bool HandleFocus(wxInputConsumer *consumer, const wxFocusEvent& event);
86};
87
1e6feb95
VZ
88// ----------------------------------------------------------------------------
89// constants
90// ----------------------------------------------------------------------------
91
92// the margin between the slider and the label (FIXME: hardcoded)
93static const wxCoord SLIDER_LABEL_MARGIN = 2;
94
95// ============================================================================
96// implementation of wxSlider
97// ============================================================================
98
99IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
100
101BEGIN_EVENT_TABLE(wxSlider, wxControl)
102 EVT_SIZE(wxSlider::OnSize)
103END_EVENT_TABLE()
104
105// ----------------------------------------------------------------------------
106// wxSlider creation
107// ----------------------------------------------------------------------------
108
109#ifdef __VISUALC__
110 // warning C4355: 'this' : used in base member initializer list
111 #pragma warning(disable:4355)
112#endif
113
114wxSlider::wxSlider()
115 : m_thumb(this)
116{
117 Init();
118}
119
120wxSlider::wxSlider(wxWindow *parent,
121 wxWindowID id,
122 int value, int minValue, int maxValue,
123 const wxPoint& pos,
124 const wxSize& size,
125 long style,
126 const wxValidator& validator,
127 const wxString& name)
128 : m_thumb(this)
129{
130 Init();
131
132 (void)Create(parent, id, value, minValue, maxValue,
133 pos, size, style, validator, name);
134}
135
136#ifdef __VISUALC__
137 // warning C4355: 'this' : used in base member initializer list
138 #pragma warning(default:4355)
139#endif
140
141void wxSlider::Init()
142{
143 m_min =
144 m_max =
145 m_value = 0;
146
147 m_tickFreq = 1;
148
149 m_lineSize =
150 m_pageSize = 0;
151
152 m_thumbSize = 0;
153 m_thumbFlags = 0;
154}
155
156bool wxSlider::Create(wxWindow *parent,
157 wxWindowID id,
158 int value, int minValue, int maxValue,
159 const wxPoint& pos,
160 const wxSize& size,
161 long style,
162 const wxValidator& validator,
163 const wxString& name)
164{
165 if ( !wxSliderBase::Create(parent, id, pos, size, style,
166 validator, name) )
a290fa5a 167 return false;
1e6feb95
VZ
168
169 SetRange(minValue, maxValue);
170 SetValue(value);
171
172 // call this after setting the range as the best size depends (at least if
173 // we have wxSL_LABELS style) on the range
170acdc9 174 SetInitialSize(size);
1e6feb95
VZ
175
176 CreateInputHandler(wxINP_HANDLER_SLIDER);
177
a290fa5a 178 return true;
1e6feb95
VZ
179}
180
181// ----------------------------------------------------------------------------
182// wxSlider range and value
183// ----------------------------------------------------------------------------
184
185int wxSlider::GetValue() const
186{
187 return m_value;
188}
189
190int wxSlider::NormalizeValue(int value) const
191{
192 if ( value < m_min )
193 return m_min;
194 else if ( value > m_max )
195 return m_max;
196 else
197 return value;
198}
199
200bool wxSlider::ChangeValueBy(int inc)
201{
202 return ChangeValueTo(NormalizeValue(m_value + inc));
203}
204
205bool wxSlider::ChangeValueTo(int value)
206{
6766e5d1 207 // check if the value is going to change at all
c4edb7ac
VZ
208 if (value == m_value)
209 return false;
6766e5d1 210
1e6feb95
VZ
211 // this method is protected and we should only call it with normalized
212 // value!
a290fa5a 213 wxCHECK_MSG( IsInRange(value), false, _T("invalid slider value") );
1e6feb95 214
1e6feb95
VZ
215 m_value = value;
216
6766e5d1 217 Refresh();
1e6feb95 218
c4edb7ac
VZ
219 // generate the events: both a specific scroll event and a command event
220 wxScrollEvent eventScroll(wxEVT_SCROLL_CHANGED, GetId());
221 eventScroll.SetPosition(m_value);
222 eventScroll.SetEventObject( this );
223 (void)GetEventHandler()->ProcessEvent(eventScroll);
224
1e6feb95
VZ
225 wxCommandEvent event(wxEVT_COMMAND_SLIDER_UPDATED, GetId());
226 event.SetInt(m_value);
227 event.SetEventObject(this);
1e6feb95
VZ
228 (void)GetEventHandler()->ProcessEvent(event);
229
a290fa5a 230 return true;
1e6feb95
VZ
231}
232
233void wxSlider::SetValue(int value)
234{
235 value = NormalizeValue(value);
236
237 if ( m_value != value )
238 {
239 m_value = value;
240
241 Refresh();
242 }
243}
244
245void wxSlider::SetRange(int minValue, int maxValue)
246{
247 if ( minValue > maxValue )
248 {
249 // swap them, we always want min to be less than max
250 int tmp = minValue;
251 minValue = maxValue;
252 maxValue = tmp;
253 }
254
255 if ( m_min != minValue || m_max != maxValue )
256 {
257 m_min = minValue;
258 m_max = maxValue;
259
260 // reset the value to make sure it is in the new range
261 SetValue(m_value);
262
263 // the size of the label rect might have changed
264 if ( HasLabels() )
265 {
266 CalcGeometry();
267 }
268
269 Refresh();
270 }
271 //else: nothing changed
272}
273
274int wxSlider::GetMin() const
275{
276 return m_min;
277}
278
279int wxSlider::GetMax() const
280{
281 return m_max;
282}
283
284// ----------------------------------------------------------------------------
285// wxSlider line/page/thumb size
286// ----------------------------------------------------------------------------
287
288void wxSlider::SetLineSize(int lineSize)
289{
6766e5d1 290 wxCHECK_RET( lineSize >= 0, _T("invalid slider line size") );
1e6feb95
VZ
291
292 m_lineSize = lineSize;
293}
294
295void wxSlider::SetPageSize(int pageSize)
296{
6766e5d1 297 wxCHECK_RET( pageSize >= 0, _T("invalid slider page size") );
1e6feb95
VZ
298
299 m_pageSize = pageSize;
300}
301
302int wxSlider::GetLineSize() const
303{
304 if ( !m_lineSize )
305 {
306 // the default line increment is 1
307 wxConstCast(this, wxSlider)->m_lineSize = 1;
308 }
309
310 return m_lineSize;
311}
312
313int wxSlider::GetPageSize() const
314{
315 if ( !m_pageSize )
316 {
6766e5d1
JS
317 // the default page increment is m_tickFreq
318 wxConstCast(this, wxSlider)->m_pageSize = m_tickFreq;
1e6feb95
VZ
319 }
320
321 return m_pageSize;
322}
323
324void wxSlider::SetThumbLength(int lenPixels)
325{
6766e5d1 326 wxCHECK_RET( lenPixels >= 0, _T("invalid slider thumb size") );
1e6feb95
VZ
327
328 // use m_thumbSize here directly and not GetThumbLength() to avoid setting
329 // it to the default value as we don't need it
330 if ( lenPixels != m_thumbSize )
331 {
332 m_thumbSize = lenPixels;
333
334 Refresh();
335 }
336}
337
338int wxSlider::GetThumbLength() const
339{
6766e5d1
JS
340 wxSize sz = GetDefaultThumbSize();
341 int len = (IsVert() ? sz.x : sz.y);
32b13913 342 if (m_thumbSize > len)
6766e5d1
JS
343 {
344 return m_thumbSize;
345 }
346 else
1e6feb95 347 {
6766e5d1 348 return len;
1e6feb95
VZ
349 }
350
1e6feb95
VZ
351}
352
353// ----------------------------------------------------------------------------
354// wxSlider ticks
355// ----------------------------------------------------------------------------
356
357void wxSlider::SetTickFreq(int n, int WXUNUSED(dummy))
358{
6766e5d1
JS
359 wxCHECK_RET (n > 0, _T("invalid slider tick frequency"));
360
1e6feb95
VZ
361 if ( n != m_tickFreq )
362 {
363 m_tickFreq = n;
364
365 Refresh();
366 }
367}
368
369// ----------------------------------------------------------------------------
370// wxSlider geometry
371// ----------------------------------------------------------------------------
372
373wxSize wxSlider::CalcLabelSize() const
374{
375 wxSize size;
376
377 // there is no sense in trying to calc the labels size if we haven't got
378 // any, the caller must check for it
379 wxCHECK_MSG( HasLabels(), size, _T("shouldn't be called") );
380
381 wxCoord w1, h1, w2, h2;
382 GetTextExtent(FormatValue(m_min), &w1, &h1);
383 GetTextExtent(FormatValue(m_max), &w2, &h2);
384
385 size.x = wxMax(w1, w2);
386 size.y = wxMax(h1, h2);
387
388 return size;
389}
390
391wxSize wxSlider::DoGetBestClientSize() const
392{
393 // this dimension is completely arbitrary
6766e5d1
JS
394 static const wxCoord SLIDER_WIDTH = 40;
395
396 long style = GetWindowStyle();
1e6feb95
VZ
397
398 // first calculate the size of the slider itself: i.e. the shaft and the
399 // thumb
400 wxCoord height = GetRenderer()->GetSliderDim();
401
402 wxSize size;
403 if ( IsVert() )
404 {
405 size.x = height;
406 size.y = SLIDER_WIDTH;
407 }
408 else // horizontal
409 {
410 size.x = SLIDER_WIDTH;
411 size.y = height;
412 }
413
414 // add space for ticks
415 if ( HasTicks() )
416 {
417 wxCoord lenTick = GetRenderer()->GetSliderTickLen();
32b13913 418 if (style & wxSL_BOTH)
6766e5d1
JS
419 {
420 lenTick = 2 * lenTick;
421 }
1e6feb95
VZ
422
423 if ( IsVert() )
424 size.x += lenTick;
425 else
426 size.y += lenTick;
427 }
428
429 // if we have the label, reserve enough space for it
430 if ( HasLabels() )
431 {
432 wxSize sizeLabels = CalcLabelSize();
433
6766e5d1
JS
434 if (style & (wxSL_LEFT|wxSL_RIGHT))
435 {
1e6feb95 436 size.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
6766e5d1
JS
437 }
438 else if (style & (wxSL_TOP|wxSL_BOTTOM))
439 {
1e6feb95 440 size.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
6766e5d1 441 }
1e6feb95
VZ
442 }
443
444 return size;
445}
446
447void wxSlider::OnSize(wxSizeEvent& event)
448{
449 CalcGeometry();
450
451 event.Skip();
452}
453
454const wxRect& wxSlider::GetSliderRect() const
455{
456 if ( m_rectSlider.width < 0 )
457 {
458 wxConstCast(this, wxSlider)->CalcGeometry();
459 }
460
461 return m_rectSlider;
462}
463
464void wxSlider::CalcGeometry()
465{
466 /*
467 recalc the label and slider positions, this looks like this for
468 wxSL_HORIZONTAL | wxSL_TOP slider:
469
470 LLL lll
471 -------------------------
472 | T | <-- this is the slider rect
473 | HHHHHHHHHHHHHHHTHHHHH |
474 | T |
475 | * * * * * * * *|
476 -------------------------
477
478 LLL is m_rectLabel as calculated here and lll is the real rect used for
479 label drawing in OnDraw() (TTT indicated the thumb position and *s are
480 the ticks)
481
482 in the wxSL_VERTICAL | wxSL_RIGHT case the picture is like this:
483
484 ------ LLL
485 | H |
486 | H *|
487 | H |
488 | H *|
489 | H |
490 | H *|
491 | H |
492 |TTT*| lll
493 | H |
494 | H *|
495 ------
496 */
6766e5d1 497 long style = GetWindowStyle();
1e6feb95 498
6766e5d1 499 // initialize to the full client rect
1e6feb95 500 wxRect rectTotal = GetClientRect();
6766e5d1
JS
501 m_rectSlider = rectTotal;
502 wxSize sizeThumb = GetThumbSize();
503
504 // Labels reduce the size of the slider rect
1e6feb95
VZ
505 if ( HasLabels() )
506 {
6766e5d1 507 wxSize sizeLabels = CalcLabelSize();
1e6feb95 508
1e6feb95
VZ
509 m_rectLabel = wxRect(rectTotal.GetPosition(), sizeLabels);
510
32b13913 511 if (style & wxSL_TOP)
1e6feb95 512 {
6766e5d1
JS
513 // shrink and offset the slider to the bottom
514 m_rectSlider.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
515 m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
1e6feb95 516 }
32b13913 517 else if (style & wxSL_BOTTOM)
1e6feb95 518 {
6766e5d1
JS
519 // shrink the slider and move the label to the bottom
520 m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
521 m_rectLabel.y += m_rectSlider.height + SLIDER_LABEL_MARGIN;
522 }
32b13913 523 else if (style & wxSL_LEFT)
6766e5d1
JS
524 {
525 // shrink and offset the slider to the right
526 m_rectSlider.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
527 m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
528 }
32b13913 529 else if (style & wxSL_RIGHT)
6766e5d1
JS
530 {
531 // shrink the slider and move the label to the right
532 m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
533 m_rectLabel.x += m_rectSlider.width + SLIDER_LABEL_MARGIN;
1e6feb95 534 }
1e6feb95
VZ
535 }
536
6766e5d1 537 // calculate ticks too
1e6feb95
VZ
538 if ( HasTicks() )
539 {
540 wxCoord lenTick = GetRenderer()->GetSliderTickLen();
541
1e6feb95
VZ
542 // it
543 m_rectTicks = GetShaftRect();
544
545 if ( IsVert() )
546 {
32b13913 547 if (style & (wxSL_LEFT|wxSL_BOTH))
6766e5d1
JS
548 {
549 m_rectTicks.x = m_rectSlider.x;
550 }
551 else
552 { // wxSL_RIGHT
553 m_rectTicks.x = m_rectSlider.x + m_rectSlider.width - lenTick;
554 }
1e6feb95
VZ
555 m_rectTicks.width = lenTick;
556 }
557 else // horizontal
558 {
32b13913 559 if (style & (wxSL_TOP|wxSL_BOTH))
6766e5d1
JS
560 {
561 m_rectTicks.y = m_rectSlider.y;
562 }
563 else
564 { // wxSL_BOTTOM
565 m_rectTicks.y = m_rectSlider.y + m_rectSlider.height - lenTick;
566 }
1e6feb95
VZ
567 m_rectTicks.height = lenTick;
568 }
6766e5d1 569 }
1e6feb95 570
6766e5d1
JS
571 // slider is never smaller than thumb size unless rectTotal
572 if ( IsVert() )
573 {
574 wxCoord width = wxMin ( rectTotal.width, sizeThumb.x );
575 m_rectSlider.width = wxMax ( m_rectSlider.width, width );
576 }
577 else
578 {
579 wxCoord height = wxMin ( rectTotal.height, sizeThumb.y );
580 m_rectSlider.height = wxMax ( m_rectSlider.height, height );
1e6feb95
VZ
581 }
582}
583
584wxSize wxSlider::GetDefaultThumbSize() const
585{
6766e5d1
JS
586 // Default size has no styles (arrows)
587 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), 0, GetOrientation());
1e6feb95
VZ
588}
589
590wxSize wxSlider::GetThumbSize() const
591{
6766e5d1 592 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), m_thumbSize, GetOrientation());
1e6feb95
VZ
593}
594
595// ----------------------------------------------------------------------------
596// wxSlider thumb geometry
597// ----------------------------------------------------------------------------
598
599wxRect wxSlider::GetShaftRect() const
600{
6766e5d1 601 return GetRenderer()->GetSliderShaftRect(m_rectSlider, m_thumbSize, GetOrientation(), GetWindowStyle());
1e6feb95
VZ
602}
603
604void wxSlider::CalcThumbRect(const wxRect *rectShaftIn,
605 wxRect *rectThumbOut,
606 wxRect *rectLabelOut,
607 int value) const
608{
609 if ( value == INVALID_THUMB_VALUE )
610 {
611 // use the current if not specified
612 value = m_value;
613 }
614
615 bool isVertical = IsVert();
616
617 wxRect rectShaft;
618 if ( rectShaftIn )
619 {
620 rectShaft = *rectShaftIn;
621 }
622 else // no shaft rect provided, calc it
623 {
624 rectShaft = GetShaftRect();
625 }
626
627 wxCoord lenShaft,
32b13913
WS
628 lenThumb;
629 wxCoord *p;
630
1e6feb95
VZ
631 wxRect rectThumb(rectShaft.GetPosition(), GetThumbSize());
632 if ( isVertical )
633 {
634 rectThumb.x += (rectShaft.width - rectThumb.width) / 2;
635
636 lenThumb = rectThumb.height;
637 lenShaft = rectShaft.height;
638 p = &rectThumb.y;
639 }
640 else // horz
641 {
642 rectThumb.y += (rectShaft.height - rectThumb.height) / 2;
643
644 lenThumb = rectThumb.width;
645 lenShaft = rectShaft.width;
646 p = &rectThumb.x;
647 }
648
649 // the thumb must always be entirely inside the shaft limits, so the max
650 // position is not at lenShaft but at lenShaft - thumbSize
651 if ( m_max != m_min )
652 {
6766e5d1
JS
653 if ( isVertical )
654 {
655 *p += ((lenShaft - lenThumb)*(m_max - value))/(m_max - m_min);
656 }
657 else
658 { // horz
659 *p += ((lenShaft - lenThumb)*(value - m_min))/(m_max - m_min);
660 }
1e6feb95
VZ
661 }
662
663 // calc the label rect
664 if ( HasLabels() && rectLabelOut )
665 {
6766e5d1 666 long style = GetWindowStyle();
1e6feb95
VZ
667 wxRect rectLabel = m_rectLabel;
668
669 // centre the label relatively to the thumb position
6766e5d1 670 if (style & (wxSL_TOP|wxSL_BOTTOM))
1e6feb95 671 {
6766e5d1 672 rectLabel.x = rectThumb.x + (rectThumb.width - m_rectLabel.width)/2;
1e6feb95 673 }
6766e5d1 674 else if (style & (wxSL_LEFT|wxSL_RIGHT))
1e6feb95 675 {
6766e5d1 676 rectLabel.y = rectThumb.y + (rectThumb.height - m_rectLabel.height)/2;
1e6feb95
VZ
677 }
678
679 *rectLabelOut = rectLabel;
680 }
681
682 if ( rectThumbOut )
6766e5d1 683
1e6feb95
VZ
684 *rectThumbOut = rectThumb;
685}
686
687// ----------------------------------------------------------------------------
688// wxSlider drawing
689// ----------------------------------------------------------------------------
690
691wxString wxSlider::FormatValue(int value) const
692{
693 return wxString::Format(_T("%d"), value);
694}
695
696void wxSlider::DoDraw(wxControlRenderer *renderer)
697{
698 wxRenderer *rend = GetRenderer();
699 wxDC& dc = renderer->GetDC();
700 wxRect rectUpdate = GetUpdateClientRect();
701
1e6feb95
VZ
702 wxOrientation orient = GetOrientation();
703 int flags = GetStateFlags();
6766e5d1
JS
704 long style = GetWindowStyle();
705
706 wxSize sz = GetThumbSize();
707 int len = IsVert() ? sz.x : sz.y;
1e6feb95
VZ
708
709 // first draw the shaft
6766e5d1 710 wxRect rectShaft = rend->GetSliderShaftRect(m_rectSlider, len, orient, style);
1e6feb95
VZ
711 if ( rectUpdate.Intersects(rectShaft) )
712 {
6766e5d1 713 rend->DrawSliderShaft(dc, m_rectSlider, len, orient, flags, style);
1e6feb95
VZ
714 }
715
716 // calculate the thumb position in pixels and draw it
717 wxRect rectThumb, rectLabel;
718 CalcThumbRect(&rectShaft, &rectThumb, &rectLabel);
719
6766e5d1
JS
720 // then draw the ticks
721 if ( HasTicks() && rectUpdate.Intersects(m_rectTicks) )
1e6feb95 722 {
6766e5d1
JS
723 rend->DrawSliderTicks(dc, m_rectSlider, len, orient,
724 m_min, m_max, m_tickFreq, flags, style);
1e6feb95
VZ
725 }
726
6766e5d1
JS
727 // then draw the thumb
728 if ( rectUpdate.Intersects(rectThumb) )
1e6feb95 729 {
6766e5d1 730 rend->DrawSliderThumb(dc, rectThumb, orient, flags | m_thumbFlags, style);
1e6feb95
VZ
731 }
732
733 // finally, draw the label near the thumb
734 if ( HasLabels() && rectUpdate.Intersects(rectLabel) )
735 {
736 // align it to be close to the shaft
6766e5d1 737 int align = 0;
32b13913 738 if (style & wxSL_TOP)
1e6feb95 739 {
6766e5d1 740 align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_TOP;
1e6feb95 741 }
32b13913 742 else if (style & wxSL_BOTTOM)
1e6feb95 743 {
6766e5d1
JS
744 align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_BOTTOM;
745 }
32b13913 746 else if (style & wxSL_LEFT)
6766e5d1
JS
747 {
748 align = wxALIGN_CENTRE_VERTICAL|wxALIGN_LEFT;
749 }
32b13913 750 else if (style & wxSL_RIGHT)
6766e5d1
JS
751 {
752 align = wxALIGN_CENTRE_VERTICAL|wxALIGN_RIGHT;
1e6feb95
VZ
753 }
754
755 dc.SetFont(GetFont());
756 dc.SetTextForeground(GetForegroundColour());
757
758 // the slider label is never drawn focused
759 rend->DrawLabel(dc, FormatValue(m_value), rectLabel,
760 flags & ~wxCONTROL_FOCUSED, align);
761 }
762}
763
1e6feb95
VZ
764// ----------------------------------------------------------------------------
765// wxSlider input processing
766// ----------------------------------------------------------------------------
767
768bool wxSlider::PerformAction(const wxControlAction& action,
769 long numArg,
770 const wxString& strArg)
771{
c4edb7ac
VZ
772 wxEventType scrollEvent = wxEVT_NULL;
773 int value;
774 bool valueChanged = true;
775
88d2e567 776 if ( action == wxACTION_SLIDER_START )
1e6feb95 777 {
c4edb7ac
VZ
778 scrollEvent = wxEVT_SCROLL_TOP;
779 value = m_min;
1e6feb95
VZ
780 }
781 else if ( action == wxACTION_SLIDER_END )
782 {
c4edb7ac
VZ
783 scrollEvent = wxEVT_SCROLL_BOTTOM;
784 value = m_max;
1e6feb95
VZ
785 }
786 else if ( action == wxACTION_SLIDER_PAGE_CHANGE )
787 {
c4edb7ac 788 value = NormalizeValue(m_value + numArg * GetPageSize());
1e6feb95
VZ
789 }
790 else if ( action == wxACTION_SLIDER_LINE_UP )
791 {
c4edb7ac
VZ
792 scrollEvent = wxEVT_SCROLL_LINEUP;
793 value = NormalizeValue(m_value + +GetLineSize());
1e6feb95 794 }
6766e5d1 795 else if ( action == wxACTION_SLIDER_LINE_DOWN )
1e6feb95 796 {
c4edb7ac
VZ
797 scrollEvent = wxEVT_SCROLL_LINEDOWN;
798 value = NormalizeValue(m_value + -GetLineSize());
1e6feb95 799 }
6766e5d1 800 else if ( action == wxACTION_SLIDER_PAGE_UP )
1e6feb95 801 {
c4edb7ac
VZ
802 scrollEvent = wxEVT_SCROLL_PAGEUP;
803 value = NormalizeValue(m_value + +GetPageSize());
1e6feb95
VZ
804 }
805 else if ( action == wxACTION_SLIDER_PAGE_DOWN )
806 {
c4edb7ac
VZ
807 scrollEvent = wxEVT_SCROLL_PAGEDOWN;
808 value = NormalizeValue(m_value + -GetPageSize());
1e6feb95 809 }
c4edb7ac
VZ
810 else if ( action == wxACTION_SLIDER_THUMB_DRAG ||
811 action == wxACTION_SLIDER_THUMB_MOVE )
1e6feb95 812 {
c4edb7ac
VZ
813 scrollEvent = wxEVT_SCROLL_THUMBTRACK;
814
815 // we shouldn't generate a command event about this change but we still
816 // should update our value and the slider appearance
817 valueChanged = false;
818 m_value =
819 value = (int)numArg;
820 Refresh();
1e6feb95 821 }
c4edb7ac 822 else if ( action == wxACTION_SLIDER_THUMB_RELEASE )
1e6feb95 823 {
c4edb7ac
VZ
824 scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
825 value = (int)numArg;
1e6feb95
VZ
826 }
827 else
828 {
829 return wxControl::PerformAction(action, numArg, strArg);
830 }
831
c4edb7ac
VZ
832 // update wxSlider current value and generate wxCommandEvent, except while
833 // dragging the thumb
834 if ( valueChanged )
835 ChangeValueTo(value);
836
837 // also generate more precise wxScrollEvent if applicable
838 if ( scrollEvent != wxEVT_NULL )
839 {
840 wxScrollEvent event(scrollEvent, GetId());
841 event.SetPosition(value);
842 event.SetEventObject( this );
843 GetEventHandler()->ProcessEvent(event);
844 }
845
a290fa5a 846 return true;
1e6feb95
VZ
847}
848
9467bdb7
VZ
849/* static */
850wxInputHandler *wxSlider::GetStdInputHandler(wxInputHandler *handlerDef)
851{
852 static wxStdSliderInputHandler s_handler(handlerDef);
853
854 return &s_handler;
855}
856
1e6feb95
VZ
857// ----------------------------------------------------------------------------
858// wxSlider implementation of wxControlWithThumb interface
859// ----------------------------------------------------------------------------
860
861wxScrollThumb::Shaft wxSlider::HitTest(const wxPoint& pt) const
862{
863 wxRect rectShaft = GetShaftRect();
2b5f62a0
VZ
864 wxRect rectThumb;
865 CalcThumbRect(&rectShaft, &rectThumb, NULL);
866
6766e5d1 867 // check for possible shaft or thumb hit
22a35096 868 if (!rectShaft.Contains(pt) && !rectThumb.Contains(pt))
1e6feb95
VZ
869 {
870 return wxScrollThumb::Shaft_None;
871 }
872
1e6feb95 873 // the position to test and the start and end of the thumb
6766e5d1 874 wxCoord x, x1, x2, x3, x4;
32b13913 875 if (IsVert())
1e6feb95
VZ
876 {
877 x = pt.y;
6766e5d1
JS
878 x1 = rectThumb.GetBottom();
879 x2 = rectShaft.GetBottom();
880 x3 = rectShaft.GetTop();
881 x4 = rectThumb.GetTop();
1e6feb95 882 }
6766e5d1
JS
883 else
884 { // horz
1e6feb95 885 x = pt.x;
6766e5d1
JS
886 x1 = rectShaft.GetLeft();
887 x2 = rectThumb.GetLeft();
888 x3 = rectThumb.GetRight();
889 x4 = rectShaft.GetRight();
1e6feb95 890 }
88d2e567 891 if ((x1 <= x) && (x < x2))
1e6feb95
VZ
892 {
893 // or to the left
894 return wxScrollThumb::Shaft_Above;
895 }
896
88d2e567 897 if ((x3 < x) && (x <= x4)) {
1e6feb95
VZ
898 // or to the right
899 return wxScrollThumb::Shaft_Below;
900 }
901
902 // where else can it be?
903 return wxScrollThumb::Shaft_Thumb;
904}
905
906wxCoord wxSlider::ThumbPosToPixel() const
907{
908 wxRect rectThumb;
909 CalcThumbRect(NULL, &rectThumb, NULL);
910
911 return IsVert() ? rectThumb.y : rectThumb.x;
912}
913
914int wxSlider::PixelToThumbPos(wxCoord x) const
915{
916 wxRect rectShaft = GetShaftRect();
917 wxSize sizeThumb = GetThumbSize();
918
919 wxCoord x0, len;
920 if ( IsVert() )
921 {
922 x0 = rectShaft.y;
923 len = rectShaft.height - sizeThumb.y;
924 }
925 else // horz
926 {
927 x0 = rectShaft.x;
928 len = rectShaft.width - sizeThumb.x;
929 }
930
931 int pos = m_min;
932 if ( len > 0 )
933 {
934 if ( x > x0 )
935 {
936 pos += ((x - x0) * (m_max - m_min)) / len;
937 if ( pos > m_max )
938 pos = m_max;
939 }
940 //else: x <= x0, leave pos = min
941 }
942
943 return pos;
944}
945
946void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart,
947 int flag,
948 bool set)
949{
950 // for now we ignore the flags for the shaft as no renderer uses them
951 // anyhow
952 if ( shaftPart == wxScrollThumb::Shaft_Thumb )
953 {
954 if ( set )
955 m_thumbFlags |= flag;
956 else
957 m_thumbFlags &= ~flag;
958
6766e5d1 959 Refresh();
1e6feb95
VZ
960 }
961}
962
963void wxSlider::OnThumbDragStart(int pos)
964{
32b13913 965 if (IsVert())
6766e5d1
JS
966 {
967 PerformAction(wxACTION_SLIDER_THUMB_DRAG, m_max - pos);
968 }
969 else
970 {
971 PerformAction(wxACTION_SLIDER_THUMB_DRAG, pos);
972 }
1e6feb95
VZ
973}
974
975void wxSlider::OnThumbDrag(int pos)
976{
32b13913 977 if (IsVert())
6766e5d1
JS
978 {
979 PerformAction(wxACTION_SLIDER_THUMB_MOVE, m_max - pos);
980 }
981 else
982 {
983 PerformAction(wxACTION_SLIDER_THUMB_MOVE, pos);
984 }
1e6feb95
VZ
985}
986
987void wxSlider::OnThumbDragEnd(int pos)
988{
32b13913 989 if (IsVert())
6766e5d1
JS
990 {
991 PerformAction(wxACTION_SLIDER_THUMB_RELEASE, m_max - pos);
992 }
993 else
994 {
995 PerformAction(wxACTION_SLIDER_THUMB_RELEASE, pos);
996 }
1e6feb95
VZ
997}
998
999void wxSlider::OnPageScrollStart()
1000{
1001 // we do nothing here
1002}
1003
1004bool wxSlider::OnPageScroll(int pageInc)
1005{
1006 int value = GetValue();
1007 PerformAction(wxACTION_SLIDER_PAGE_CHANGE, pageInc);
1008
1009 return GetValue() != value;
1010}
1011
1012// ----------------------------------------------------------------------------
9467bdb7 1013// wxStdSliderInputHandler
1e6feb95
VZ
1014// ----------------------------------------------------------------------------
1015
9467bdb7 1016bool wxStdSliderInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
1017 const wxKeyEvent& event,
1018 bool pressed)
1019{
88d2e567 1020 if ( pressed )
1e6feb95
VZ
1021 {
1022 int keycode = event.GetKeyCode();
1023
1024 wxControlAction action;
1025 switch ( keycode )
1026 {
1027 case WXK_HOME:
6766e5d1 1028 action = wxACTION_SLIDER_END;
1e6feb95
VZ
1029 break;
1030
1031 case WXK_END:
6766e5d1 1032 action = wxACTION_SLIDER_START;
1e6feb95
VZ
1033 break;
1034
6766e5d1 1035 case WXK_RIGHT:
1e6feb95
VZ
1036 case WXK_UP:
1037 action = wxACTION_SLIDER_LINE_UP;
1038 break;
1039
6766e5d1 1040 case WXK_LEFT:
1e6feb95
VZ
1041 case WXK_DOWN:
1042 action = wxACTION_SLIDER_LINE_DOWN;
1043 break;
1044
187c183c 1045 case WXK_PAGEUP:
1e6feb95
VZ
1046 action = wxACTION_SLIDER_PAGE_UP;
1047 break;
1048
187c183c 1049 case WXK_PAGEDOWN:
1e6feb95
VZ
1050 action = wxACTION_SLIDER_PAGE_DOWN;
1051 break;
1052 }
1053
a290fa5a 1054 if ( !action.IsEmpty() )
1e6feb95 1055 {
23645bfa 1056 consumer->PerformAction(action);
1e6feb95 1057
a290fa5a 1058 return true;
1e6feb95
VZ
1059 }
1060 }
1061
23645bfa 1062 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
1063}
1064
9467bdb7 1065bool wxStdSliderInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
1066 const wxMouseEvent& event)
1067{
23645bfa 1068 wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
1e6feb95
VZ
1069
1070 if ( slider->GetThumb().HandleMouse(event) )
1071 {
1072 // processed by the thumb
a290fa5a 1073 return false;
1e6feb95
VZ
1074 }
1075
23645bfa 1076 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
1077}
1078
9467bdb7 1079bool wxStdSliderInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
1080 const wxMouseEvent& event)
1081{
23645bfa 1082 wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
1e6feb95
VZ
1083
1084 if ( slider->GetThumb().HandleMouseMove(event) )
1085 {
1086 // processed by the thumb
a290fa5a 1087 return false;
1e6feb95
VZ
1088 }
1089
23645bfa 1090 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
1091}
1092
61fef19b 1093bool
9467bdb7 1094wxStdSliderInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
61fef19b 1095 const wxFocusEvent& WXUNUSED(event))
1e6feb95 1096{
9467bdb7 1097 // slider appearance changes when it gets/loses focus
a290fa5a 1098 return true;
1e6feb95
VZ
1099}
1100
1101#endif // wxUSE_SLIDER