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