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