Preserve value when changing range of inverted wxSlider in wxMSW.
[wxWidgets.git] / src / msw / slider.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/slider.cpp
3 // Purpose: wxSlider, using the Win95 (and later) trackbar control
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart 1998
9 // Vadim Zeitlin 2004
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_SLIDER
29
30 #include "wx/slider.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
34 #include "wx/brush.h"
35 #endif
36
37 #include "wx/msw/subwin.h"
38
39 // ----------------------------------------------------------------------------
40 // constants
41 // ----------------------------------------------------------------------------
42
43 namespace
44 {
45
46 // indices of labels in wxSlider::m_labels
47 enum
48 {
49 SliderLabel_Min,
50 SliderLabel_Max,
51 SliderLabel_Value,
52 SliderLabel_Last
53 };
54
55 // the gaps between the slider and the labels, in pixels
56 const int HGAP = 5;
57 const int VGAP = 4;
58 // the width of the borders including white space
59 const int BORDERPAD = 8;
60 // these 2 values are arbitrary:
61 const int THUMB = 24;
62 const int TICK = 8;
63
64 } // anonymous namespace
65
66 // ----------------------------------------------------------------------------
67 // XTI
68 // ----------------------------------------------------------------------------
69
70 #if wxUSE_EXTENDED_RTTI
71 WX_DEFINE_FLAGS( wxSliderStyle )
72
73 wxBEGIN_FLAGS( wxSliderStyle )
74 // new style border flags, we put them first to
75 // use them for streaming out
76 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
77 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
78 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
79 wxFLAGS_MEMBER(wxBORDER_RAISED)
80 wxFLAGS_MEMBER(wxBORDER_STATIC)
81 wxFLAGS_MEMBER(wxBORDER_NONE)
82
83 // old style border flags
84 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
85 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
86 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
87 wxFLAGS_MEMBER(wxRAISED_BORDER)
88 wxFLAGS_MEMBER(wxSTATIC_BORDER)
89 wxFLAGS_MEMBER(wxBORDER)
90
91 // standard window styles
92 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
93 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
94 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
95 wxFLAGS_MEMBER(wxWANTS_CHARS)
96 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
97 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
98 wxFLAGS_MEMBER(wxVSCROLL)
99 wxFLAGS_MEMBER(wxHSCROLL)
100
101 wxFLAGS_MEMBER(wxSL_HORIZONTAL)
102 wxFLAGS_MEMBER(wxSL_VERTICAL)
103 wxFLAGS_MEMBER(wxSL_AUTOTICKS)
104 wxFLAGS_MEMBER(wxSL_LEFT)
105 wxFLAGS_MEMBER(wxSL_TOP)
106 wxFLAGS_MEMBER(wxSL_RIGHT)
107 wxFLAGS_MEMBER(wxSL_BOTTOM)
108 wxFLAGS_MEMBER(wxSL_BOTH)
109 wxFLAGS_MEMBER(wxSL_SELRANGE)
110 wxFLAGS_MEMBER(wxSL_INVERSE)
111 wxFLAGS_MEMBER(wxSL_MIN_MAX_LABELS)
112 wxFLAGS_MEMBER(wxSL_VALUE_LABEL)
113 wxFLAGS_MEMBER(wxSL_LABELS)
114
115 wxEND_FLAGS( wxSliderStyle )
116
117 IMPLEMENT_DYNAMIC_CLASS_XTI(wxSlider, wxControl,"wx/slider.h")
118
119 wxBEGIN_PROPERTIES_TABLE(wxSlider)
120 wxEVENT_RANGE_PROPERTY( Scroll , wxEVT_SCROLL_TOP , wxEVT_SCROLL_CHANGED , wxScrollEvent )
121 wxEVENT_PROPERTY( Updated , wxEVT_COMMAND_SLIDER_UPDATED , wxCommandEvent )
122
123 wxPROPERTY( Value , int , SetValue, GetValue , 0, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
124 wxPROPERTY( Minimum , int , SetMin, GetMin, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
125 wxPROPERTY( Maximum , int , SetMax, GetMax, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
126 wxPROPERTY( PageSize , int , SetPageSize, GetLineSize, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
127 wxPROPERTY( LineSize , int , SetLineSize, GetLineSize, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
128 wxPROPERTY( ThumbLength , int , SetThumbLength, GetThumbLength, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
129 wxPROPERTY_FLAGS( WindowStyle , wxSliderStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
130 wxEND_PROPERTIES_TABLE()
131
132 wxBEGIN_HANDLERS_TABLE(wxSlider)
133 wxEND_HANDLERS_TABLE()
134
135 wxCONSTRUCTOR_8( wxSlider , wxWindow* , Parent , wxWindowID , Id , int , Value , int , Minimum , int , Maximum , wxPoint , Position , wxSize , Size , long , WindowStyle )
136 #else
137 IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
138 #endif
139
140 // ============================================================================
141 // wxSlider implementation
142 // ============================================================================
143
144 // ----------------------------------------------------------------------------
145 // construction
146 // ----------------------------------------------------------------------------
147
148 void wxSlider::Init()
149 {
150 m_labels = NULL;
151
152 m_pageSize = 1;
153 m_lineSize = 1;
154 m_rangeMax = 0;
155 m_rangeMin = 0;
156 m_tickFreq = 0;
157
158 m_isDragging = false;
159 }
160
161 bool wxSlider::Create(wxWindow *parent,
162 wxWindowID id,
163 int value,
164 int minValue,
165 int maxValue,
166 const wxPoint& pos,
167 const wxSize& size,
168 long style,
169 const wxValidator& validator,
170 const wxString& name)
171 {
172 wxCHECK_MSG( minValue < maxValue, false,
173 wxT("Slider minimum must be strictly less than the maximum.") );
174
175 // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and
176 // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility
177 // reasons we can't really change it, instead try to infer the orientation
178 // from the flags given to us here
179 switch ( style & (wxSL_LEFT | wxSL_RIGHT | wxSL_TOP | wxSL_BOTTOM) )
180 {
181 case wxSL_LEFT:
182 case wxSL_RIGHT:
183 style |= wxSL_VERTICAL;
184 break;
185
186 case wxSL_TOP:
187 case wxSL_BOTTOM:
188 style |= wxSL_HORIZONTAL;
189 break;
190
191 case 0:
192 // no specific direction, do we have at least the orientation?
193 if ( !(style & (wxSL_HORIZONTAL | wxSL_VERTICAL)) )
194 {
195 // no, choose default
196 style |= wxSL_BOTTOM | wxSL_HORIZONTAL;
197 }
198 };
199
200 wxASSERT_MSG( !(style & wxSL_VERTICAL) || !(style & wxSL_HORIZONTAL),
201 wxT("incompatible slider direction and orientation") );
202
203
204 // initialize everything
205 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
206 return false;
207
208 // ensure that we have correct values for GetLabelsSize()
209 m_rangeMin = minValue;
210 m_rangeMax = maxValue;
211
212 // create the labels first, so that our DoGetBestSize() could take them
213 // into account
214 //
215 // note that we could simply create 3 wxStaticTexts here but it could
216 // result in some observable side effects at wx level (e.g. the parent of
217 // wxSlider would have 3 more children than expected) and so we prefer not
218 // to do it like this
219 if ( m_windowStyle & wxSL_LABELS )
220 {
221 m_labels = new wxSubwindows(SliderLabel_Last);
222
223 HWND hwndParent = GetHwndOf(parent);
224 for ( size_t n = 0; n < SliderLabel_Last; n++ )
225 {
226 wxWindowIDRef lblid = NewControlId();
227
228 HWND wnd = ::CreateWindow
229 (
230 wxT("STATIC"),
231 NULL,
232 WS_CHILD | WS_VISIBLE | SS_CENTER,
233 0, 0, 0, 0,
234 hwndParent,
235 (HMENU)wxUIntToPtr(lblid.GetValue()),
236 wxGetInstance(),
237 NULL
238 );
239
240 m_labels->Set(n, wnd, lblid);
241 }
242 m_labels->SetFont(GetFont());
243 }
244
245 // now create the main control too
246 if ( !MSWCreateControl(TRACKBAR_CLASS, wxEmptyString, pos, size) )
247 return false;
248
249 // and initialize everything
250 SetRange(minValue, maxValue);
251 SetValue(value);
252 SetPageSize((maxValue - minValue)/10);
253
254 // we need to position the labels correctly if we have them and if
255 // SetSize() hadn't been called before (when best size was determined by
256 // MSWCreateControl()) as in this case they haven't been put in place yet
257 if ( m_labels && size.x != wxDefaultCoord && size.y != wxDefaultCoord )
258 {
259 SetSize(size);
260 }
261
262 return true;
263 }
264
265 WXDWORD wxSlider::MSWGetStyle(long style, WXDWORD *exstyle) const
266 {
267 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
268
269 // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity
270 msStyle |= style & wxSL_VERTICAL ? TBS_VERT : TBS_HORZ;
271
272 if ( style & wxSL_BOTH )
273 {
274 // this fully specifies the style combined with TBS_VERT/HORZ above
275 msStyle |= TBS_BOTH;
276 }
277 else // choose one direction
278 {
279 if ( style & wxSL_LEFT )
280 msStyle |= TBS_LEFT;
281 else if ( style & wxSL_RIGHT )
282 msStyle |= TBS_RIGHT;
283 else if ( style & wxSL_TOP )
284 msStyle |= TBS_TOP;
285 else if ( style & wxSL_BOTTOM )
286 msStyle |= TBS_BOTTOM;
287 }
288
289 if ( style & wxSL_AUTOTICKS )
290 msStyle |= TBS_AUTOTICKS;
291 else
292 msStyle |= TBS_NOTICKS;
293
294 if ( style & wxSL_SELRANGE )
295 msStyle |= TBS_ENABLESELRANGE;
296
297 return msStyle;
298 }
299
300 wxSlider::~wxSlider()
301 {
302 delete m_labels;
303 }
304
305 // ----------------------------------------------------------------------------
306 // event handling
307 // ----------------------------------------------------------------------------
308
309 bool wxSlider::MSWOnScroll(int WXUNUSED(orientation),
310 WXWORD wParam,
311 WXWORD WXUNUSED(pos),
312 WXHWND control)
313 {
314 wxEventType scrollEvent;
315 switch ( wParam )
316 {
317 case SB_TOP:
318 scrollEvent = wxEVT_SCROLL_TOP;
319 break;
320
321 case SB_BOTTOM:
322 scrollEvent = wxEVT_SCROLL_BOTTOM;
323 break;
324
325 case SB_LINEUP:
326 scrollEvent = wxEVT_SCROLL_LINEUP;
327 break;
328
329 case SB_LINEDOWN:
330 scrollEvent = wxEVT_SCROLL_LINEDOWN;
331 break;
332
333 case SB_PAGEUP:
334 scrollEvent = wxEVT_SCROLL_PAGEUP;
335 break;
336
337 case SB_PAGEDOWN:
338 scrollEvent = wxEVT_SCROLL_PAGEDOWN;
339 break;
340
341 case SB_THUMBTRACK:
342 scrollEvent = wxEVT_SCROLL_THUMBTRACK;
343 m_isDragging = true;
344 break;
345
346 case SB_THUMBPOSITION:
347 if ( m_isDragging )
348 {
349 scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
350 m_isDragging = false;
351 }
352 else
353 {
354 // this seems to only happen when the mouse wheel is used: in
355 // this case, as it might be unexpected to get THUMBRELEASE
356 // without preceding THUMBTRACKs, we don't generate it at all
357 // but generate CHANGED event because the control itself does
358 // not send us SB_ENDSCROLL for whatever reason when mouse
359 // wheel is used
360 scrollEvent = wxEVT_SCROLL_CHANGED;
361 }
362 break;
363
364 case SB_ENDSCROLL:
365 scrollEvent = wxEVT_SCROLL_CHANGED;
366 break;
367
368 default:
369 // unknown scroll event?
370 return false;
371 }
372
373 int newPos = ValueInvertOrNot((int) ::SendMessage((HWND) control, TBM_GETPOS, 0, 0));
374 if ( (newPos < GetMin()) || (newPos > GetMax()) )
375 {
376 // out of range - but we did process it
377 return true;
378 }
379
380 SetValue(newPos);
381
382 wxScrollEvent event(scrollEvent, m_windowId);
383 event.SetPosition(newPos);
384 event.SetEventObject( this );
385 HandleWindowEvent(event);
386
387 wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, GetId() );
388 cevent.SetInt( newPos );
389 cevent.SetEventObject( this );
390
391 return HandleWindowEvent( cevent );
392 }
393
394 void wxSlider::Command (wxCommandEvent & event)
395 {
396 SetValue (event.GetInt());
397 ProcessCommand (event);
398 }
399
400 // ----------------------------------------------------------------------------
401 // geometry stuff
402 // ----------------------------------------------------------------------------
403
404 wxRect wxSlider::GetBoundingBox() const
405 {
406 // take care not to call our own functions which would call us recursively
407 int x, y, w, h;
408 wxSliderBase::DoGetPosition(&x, &y);
409 wxSliderBase::DoGetSize(&w, &h);
410
411 wxRect rect(x, y, w, h);
412 if ( m_labels )
413 {
414 wxRect lrect = m_labels->GetBoundingBox();
415 GetParent()->ScreenToClient(&lrect.x, &lrect.y);
416 rect.Union(lrect);
417 }
418
419 return rect;
420 }
421
422 void wxSlider::DoGetSize(int *width, int *height) const
423 {
424 wxRect rect = GetBoundingBox();
425
426 if ( width )
427 *width = rect.width;
428 if ( height )
429 *height = rect.height;
430 }
431
432 void wxSlider::DoGetPosition(int *x, int *y) const
433 {
434 wxRect rect = GetBoundingBox();
435
436 if ( x )
437 *x = rect.x;
438 if ( y )
439 *y = rect.y;
440 }
441
442 int wxSlider::GetLabelsSize(int *widthMin, int *widthMax) const
443 {
444 if ( widthMin && widthMax )
445 {
446 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
447 {
448 *widthMin = GetTextExtent(Format(m_rangeMin)).x;
449 *widthMax = GetTextExtent(Format(m_rangeMax)).x;
450 }
451 else
452 {
453 *widthMin =
454 *widthMax = 0;
455 }
456 }
457
458 return HasFlag(wxSL_LABELS) ? GetCharHeight() : 0;
459 }
460
461 void wxSlider::DoMoveWindow(int x, int y, int width, int height)
462 {
463 // all complications below are because we need to position the labels,
464 // without them everything is easy
465 if ( !m_labels )
466 {
467 wxSliderBase::DoMoveWindow(x, y, width, height);
468 return;
469 }
470
471 const int labelHeight = GetLabelsSize(&m_minLabelWidth, &m_maxLabelWidth);
472 const int maxLabelWidth = wxMax(m_minLabelWidth, m_maxLabelWidth);
473
474 int labelOffset = 0;
475 int tickOffset = 0;
476 if ( HasFlag(wxSL_TICKS))
477 tickOffset = TICK;
478 if ( HasFlag(wxSL_BOTH))
479 tickOffset *= 2;
480
481 // be careful to position the slider itself after moving the labels as
482 // otherwise our GetBoundingBox(), which is called from WM_SIZE handler,
483 // would return a wrong result and wrong size would be cached internally
484 if ( HasFlag(wxSL_VERTICAL) )
485 {
486 int holdTopWidth;
487 int holdTopX;
488 int holdBottomWidth;
489 int holdBottomX;
490 int xLabel = (wxMax((THUMB + (BORDERPAD * 2)), maxLabelWidth) / 2) -
491 (maxLabelWidth / 2) + x;
492 if ( HasFlag(wxSL_LEFT) )
493 {
494 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
495 {
496 holdTopX = xLabel;
497 holdTopWidth = m_minLabelWidth;
498 holdBottomX = xLabel - ((m_maxLabelWidth - m_minLabelWidth) / 2);
499 holdBottomWidth = m_maxLabelWidth;
500 if ( HasFlag(wxSL_INVERSE) )
501 {
502 wxSwap(holdTopWidth, holdBottomWidth);
503 wxSwap(holdTopX, holdBottomX);
504 }
505 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
506 holdTopX,
507 y,
508 holdTopWidth, labelHeight);
509 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
510 holdBottomX,
511 y + height - labelHeight,
512 holdBottomWidth, labelHeight);
513 }
514 if ( HasFlag(wxSL_VALUE_LABEL) )
515 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
516 x + THUMB + tickOffset + HGAP,
517 y + (height - labelHeight)/2,
518 maxLabelWidth, labelHeight);
519 }
520 else // wxSL_RIGHT
521 {
522 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
523 {
524 holdTopX = xLabel + maxLabelWidth + ((m_maxLabelWidth - m_minLabelWidth) / 2);
525 holdTopWidth = m_minLabelWidth;
526 holdBottomX = xLabel + maxLabelWidth;
527 holdBottomWidth = m_maxLabelWidth;
528 if ( HasFlag(wxSL_INVERSE) )
529 {
530 wxSwap(holdTopWidth, holdBottomWidth);
531 wxSwap(holdTopX, holdBottomX);
532 }
533 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
534 holdTopX,
535 y,
536 holdTopWidth, labelHeight);
537 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
538 holdBottomX,
539 y + height - labelHeight,
540 holdBottomWidth, labelHeight);
541 }
542 if ( HasFlag(wxSL_VALUE_LABEL) )
543 labelOffset = maxLabelWidth + HGAP;
544 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
545 x,
546 y + (height - labelHeight)/2,
547 maxLabelWidth, labelHeight);
548 }
549
550 // position the slider itself along the left/right edge
551 wxSliderBase::DoMoveWindow(
552 x + labelOffset,
553 y + labelHeight,
554 THUMB + tickOffset + HGAP,
555 height - (labelHeight * 2));
556 }
557 else // horizontal
558 {
559 int holdLeftWidth;
560 int holdLeftX;
561 int holdRightWidth;
562 int holdRightX;
563 int yLabelMinMax =
564 (y + ((THUMB + tickOffset) / 2)) - (labelHeight / 2);
565 int xLabelValue =
566 x + m_minLabelWidth +
567 ((width - (m_minLabelWidth + m_maxLabelWidth)) / 2) -
568 (m_maxLabelWidth / 2);
569
570 if ( HasFlag(wxSL_BOTTOM) )
571 {
572 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
573 {
574 holdLeftX = x;
575 holdLeftWidth = m_minLabelWidth;
576 holdRightX = x + width - m_maxLabelWidth;
577 holdRightWidth = m_maxLabelWidth;
578 if ( HasFlag(wxSL_INVERSE) )
579 {
580 wxSwap(holdLeftWidth, holdRightWidth);
581 wxSwap(holdLeftX, holdRightX);
582 }
583 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
584 holdLeftX,
585 yLabelMinMax,
586 holdLeftWidth, labelHeight);
587 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
588 holdRightX,
589 yLabelMinMax,
590 holdRightWidth, labelHeight);
591 }
592 if ( HasFlag(wxSL_VALUE_LABEL) )
593 {
594 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
595 xLabelValue,
596 y - labelHeight,
597 maxLabelWidth, labelHeight);
598 }
599 }
600 else // wxSL_TOP
601 {
602 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
603 {
604 holdLeftX = x;
605 holdLeftWidth = m_minLabelWidth;
606 holdRightX = x + width - m_maxLabelWidth;
607 holdRightWidth = m_maxLabelWidth;
608 if ( HasFlag(wxSL_INVERSE) )
609 {
610 wxSwap(holdLeftWidth, holdRightWidth);
611 wxSwap(holdLeftX, holdRightX);
612 }
613 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
614 holdLeftX,
615 yLabelMinMax,
616 holdLeftWidth, labelHeight);
617 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
618 holdRightX,
619 yLabelMinMax,
620 holdRightWidth, labelHeight);
621 }
622 if ( HasFlag(wxSL_VALUE_LABEL) )
623 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
624 xLabelValue,
625 y + THUMB + tickOffset,
626 maxLabelWidth, labelHeight);
627 }
628
629 // position the slider itself along the top/bottom edge
630 if ( HasFlag(wxSL_MIN_MAX_LABELS) || HasFlag(wxSL_VALUE_LABEL) )
631 labelOffset = labelHeight;
632 wxSliderBase::DoMoveWindow(
633 x + m_minLabelWidth + VGAP,
634 y,
635 width - (m_minLabelWidth + m_maxLabelWidth + (VGAP*2)),
636 THUMB + tickOffset);
637 }
638 }
639
640 wxSize wxSlider::DoGetBestSize() const
641 {
642 // this value is arbitrary:
643 static const int length = 100;
644
645 int *width;
646 wxSize size;
647 if ( HasFlag(wxSL_VERTICAL) )
648 {
649 size.x = THUMB;
650 size.y = length;
651 width = &size.x;
652
653 if ( m_labels )
654 {
655 int widthMin,
656 widthMax;
657 int hLabel = GetLabelsSize(&widthMin, &widthMax);
658
659 // account for the labels
660 size.x += HGAP + wxMax(widthMin, widthMax);
661
662 // labels are indented relative to the slider itself
663 size.y += hLabel;
664 }
665 }
666 else // horizontal
667 {
668 size.x = length;
669 size.y = THUMB;
670 width = &size.y;
671
672 if ( m_labels )
673 {
674 // labels add extra height
675 int labelSize = GetLabelsSize();
676 if ( HasFlag(wxSL_MIN_MAX_LABELS) )
677 size.y += labelSize;
678 if ( HasFlag(wxSL_VALUE_LABEL) )
679 size.y += labelSize*2.75;
680 }
681 }
682
683 // need extra space to show ticks
684 if ( HasFlag(wxSL_TICKS) )
685 {
686 *width += TICK;
687 // and maybe twice as much if we show them on both sides
688 if ( HasFlag(wxSL_BOTH) )
689 *width += TICK;
690 }
691 return size;
692 }
693
694 // ----------------------------------------------------------------------------
695 // slider-specific methods
696 // ----------------------------------------------------------------------------
697
698 int wxSlider::GetValue() const
699 {
700 return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS, 0, 0));
701 }
702
703 void wxSlider::SetValue(int value)
704 {
705 ::SendMessage(GetHwnd(), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)ValueInvertOrNot(value));
706
707 if ( m_labels )
708 {
709 ::SetWindowText((*m_labels)[SliderLabel_Value], Format(value).wx_str());
710 }
711 }
712
713 void wxSlider::SetRange(int minValue, int maxValue)
714 {
715 // Remember the old logical value if we need to update the physical control
716 // value after changing its range in wxSL_INVERSE case (and avoid an
717 // unnecessary call to GetValue() otherwise as it's just not needed).
718 const int valueOld = HasFlag(wxSL_INVERSE) ? GetValue() : 0;
719
720 m_rangeMin = minValue;
721 m_rangeMax = maxValue;
722
723 ::SendMessage(GetHwnd(), TBM_SETRANGEMIN, TRUE, m_rangeMin);
724 ::SendMessage(GetHwnd(), TBM_SETRANGEMAX, TRUE, m_rangeMax);
725
726 if ( m_labels )
727 {
728 ::SetWindowText((*m_labels)[SliderLabel_Min],
729 Format(ValueInvertOrNot(m_rangeMin)).wx_str());
730 ::SetWindowText((*m_labels)[SliderLabel_Max],
731 Format(ValueInvertOrNot(m_rangeMax)).wx_str());
732 }
733
734 // When emulating wxSL_INVERSE style in wxWidgets, we need to update the
735 // value after changing the range to ensure that the value seen by the user
736 // code, i.e. the one returned by GetValue(), does not change.
737 if ( HasFlag(wxSL_INVERSE) )
738 {
739 ::SendMessage(GetHwnd(), TBM_SETPOS, TRUE, ValueInvertOrNot(valueOld));
740 }
741 }
742
743 void wxSlider::SetTickFreq(int n, int pos)
744 {
745 m_tickFreq = n;
746 ::SendMessage( GetHwnd(), TBM_SETTICFREQ, (WPARAM) n, (LPARAM) pos );
747 }
748
749 void wxSlider::SetPageSize(int pageSize)
750 {
751 ::SendMessage( GetHwnd(), TBM_SETPAGESIZE, (WPARAM) 0, (LPARAM) pageSize );
752 m_pageSize = pageSize;
753 }
754
755 int wxSlider::GetPageSize() const
756 {
757 return m_pageSize;
758 }
759
760 void wxSlider::ClearSel()
761 {
762 ::SendMessage(GetHwnd(), TBM_CLEARSEL, (WPARAM) TRUE, (LPARAM) 0);
763 }
764
765 void wxSlider::ClearTicks()
766 {
767 ::SendMessage(GetHwnd(), TBM_CLEARTICS, (WPARAM) TRUE, (LPARAM) 0);
768 }
769
770 void wxSlider::SetLineSize(int lineSize)
771 {
772 m_lineSize = lineSize;
773 ::SendMessage(GetHwnd(), TBM_SETLINESIZE, (WPARAM) 0, (LPARAM) lineSize);
774 }
775
776 int wxSlider::GetLineSize() const
777 {
778 return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE, 0, 0);
779 }
780
781 int wxSlider::GetSelEnd() const
782 {
783 return (int)::SendMessage(GetHwnd(), TBM_GETSELEND, 0, 0);
784 }
785
786 int wxSlider::GetSelStart() const
787 {
788 return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART, 0, 0);
789 }
790
791 void wxSlider::SetSelection(int minPos, int maxPos)
792 {
793 ::SendMessage(GetHwnd(), TBM_SETSEL,
794 (WPARAM) TRUE /* redraw */,
795 (LPARAM) MAKELONG( minPos, maxPos) );
796 }
797
798 void wxSlider::SetThumbLength(int len)
799 {
800 ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH, (WPARAM) len, (LPARAM) 0);
801 }
802
803 int wxSlider::GetThumbLength() const
804 {
805 return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH, 0, 0);
806 }
807
808 void wxSlider::SetTick(int tickPos)
809 {
810 ::SendMessage( GetHwnd(), TBM_SETTIC, (WPARAM) 0, (LPARAM) tickPos );
811 }
812
813 // ----------------------------------------------------------------------------
814 // composite control methods
815 // ----------------------------------------------------------------------------
816
817 WXHWND wxSlider::GetStaticMin() const
818 {
819 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Min] : NULL;
820 }
821
822 WXHWND wxSlider::GetStaticMax() const
823 {
824 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Max] : NULL;
825 }
826
827 WXHWND wxSlider::GetEditValue() const
828 {
829 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Value] : NULL;
830 }
831
832 WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider, wxSliderBase, m_labels)
833
834 #endif // wxUSE_SLIDER