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