]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/slider95.cpp
[ 1509599 ] 'Split pickers page in widgets sample' with more icons and rebaking.
[wxWidgets.git] / src / msw / slider95.cpp
... / ...
CommitLineData
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/brush.h"
34#endif
35
36#include "wx/msw/subwin.h"
37
38// include <commctrl.h> "properly"
39#include "wx/msw/wrapcctl.h"
40
41// ----------------------------------------------------------------------------
42// constants
43// ----------------------------------------------------------------------------
44
45// indices of labels in wxSlider::m_labels
46enum
47{
48 SliderLabel_Min,
49 SliderLabel_Max,
50 SliderLabel_Value,
51 SliderLabel_Last
52};
53
54// the gap between the slider and the labels, in pixels
55static const int HGAP = 5;
56
57// ----------------------------------------------------------------------------
58// XTI
59// ----------------------------------------------------------------------------
60
61#if wxUSE_EXTENDED_RTTI
62WX_DEFINE_FLAGS( wxSliderStyle )
63
64wxBEGIN_FLAGS( wxSliderStyle )
65 // new style border flags, we put them first to
66 // use them for streaming out
67 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
68 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
69 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
70 wxFLAGS_MEMBER(wxBORDER_RAISED)
71 wxFLAGS_MEMBER(wxBORDER_STATIC)
72 wxFLAGS_MEMBER(wxBORDER_NONE)
73
74 // old style border flags
75 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
76 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
77 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
78 wxFLAGS_MEMBER(wxRAISED_BORDER)
79 wxFLAGS_MEMBER(wxSTATIC_BORDER)
80 wxFLAGS_MEMBER(wxBORDER)
81
82 // standard window styles
83 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
84 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
85 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
86 wxFLAGS_MEMBER(wxWANTS_CHARS)
87 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
88 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
89 wxFLAGS_MEMBER(wxVSCROLL)
90 wxFLAGS_MEMBER(wxHSCROLL)
91
92 wxFLAGS_MEMBER(wxSL_HORIZONTAL)
93 wxFLAGS_MEMBER(wxSL_VERTICAL)
94 wxFLAGS_MEMBER(wxSL_AUTOTICKS)
95 wxFLAGS_MEMBER(wxSL_LABELS)
96 wxFLAGS_MEMBER(wxSL_LEFT)
97 wxFLAGS_MEMBER(wxSL_TOP)
98 wxFLAGS_MEMBER(wxSL_RIGHT)
99 wxFLAGS_MEMBER(wxSL_BOTTOM)
100 wxFLAGS_MEMBER(wxSL_BOTH)
101 wxFLAGS_MEMBER(wxSL_SELRANGE)
102 wxFLAGS_MEMBER(wxSL_INVERSE)
103
104wxEND_FLAGS( wxSliderStyle )
105
106IMPLEMENT_DYNAMIC_CLASS_XTI(wxSlider, wxControl,"wx/slider.h")
107
108wxBEGIN_PROPERTIES_TABLE(wxSlider)
109 wxEVENT_RANGE_PROPERTY( Scroll , wxEVT_SCROLL_TOP , wxEVT_SCROLL_CHANGED , wxScrollEvent )
110 wxEVENT_PROPERTY( Updated , wxEVT_COMMAND_SLIDER_UPDATED , wxCommandEvent )
111
112 wxPROPERTY( Value , int , SetValue, GetValue , 0, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
113 wxPROPERTY( Minimum , int , SetMin, GetMin, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
114 wxPROPERTY( Maximum , int , SetMax, GetMax, 0 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
115 wxPROPERTY( PageSize , int , SetPageSize, GetLineSize, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
116 wxPROPERTY( LineSize , int , SetLineSize, GetLineSize, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
117 wxPROPERTY( ThumbLength , int , SetThumbLength, GetThumbLength, 1 , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
118 wxPROPERTY_FLAGS( WindowStyle , wxSliderStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
119wxEND_PROPERTIES_TABLE()
120
121wxBEGIN_HANDLERS_TABLE(wxSlider)
122wxEND_HANDLERS_TABLE()
123
124wxCONSTRUCTOR_8( wxSlider , wxWindow* , Parent , wxWindowID , Id , int , Value , int , Minimum , int , Maximum , wxPoint , Position , wxSize , Size , long , WindowStyle )
125#else
126IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
127#endif
128
129// ============================================================================
130// wxSlider implementation
131// ============================================================================
132
133// ----------------------------------------------------------------------------
134// construction
135// ----------------------------------------------------------------------------
136
137void wxSlider::Init()
138{
139 m_labels = NULL;
140
141 m_pageSize = 1;
142 m_lineSize = 1;
143 m_rangeMax = 0;
144 m_rangeMin = 0;
145 m_tickFreq = 0;
146
147 m_isDragging = false;
148}
149
150bool
151wxSlider::Create(wxWindow *parent,
152 wxWindowID id,
153 int value,
154 int minValue,
155 int maxValue,
156 const wxPoint& pos,
157 const wxSize& size,
158 long style,
159 const wxValidator& validator,
160 const wxString& name)
161{
162 // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and
163 // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility
164 // reasons we can't really change it, instead try to infer the orientation
165 // from the flags given to us here
166 switch ( style & (wxSL_LEFT | wxSL_RIGHT | wxSL_TOP | wxSL_BOTTOM) )
167 {
168 case wxSL_LEFT:
169 case wxSL_RIGHT:
170 style |= wxSL_VERTICAL;
171 break;
172
173 case wxSL_TOP:
174 case wxSL_BOTTOM:
175 style |= wxSL_HORIZONTAL;
176 break;
177
178 case 0:
179 // no specific direction, do we have at least the orientation?
180 if ( !(style & (wxSL_HORIZONTAL | wxSL_VERTICAL)) )
181 {
182 // no, choose default
183 style |= wxSL_BOTTOM | wxSL_HORIZONTAL;
184 }
185 };
186
187 wxASSERT_MSG( !(style & wxSL_VERTICAL) || !(style & wxSL_HORIZONTAL),
188 _T("incompatible slider direction and orientation") );
189
190
191 // initialize everything
192 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
193 return false;
194
195 // ensure that we have correct values for GetLabelsSize()
196 m_rangeMin = minValue;
197 m_rangeMax = maxValue;
198
199 // create the labels first, so that our DoGetBestSize() could take them
200 // into account
201 //
202 // note that we could simply create 3 wxStaticTexts here but it could
203 // result in some observable side effects at wx level (e.g. the parent of
204 // wxSlider would have 3 more children than expected) and so we prefer not
205 // to do it like this
206 if ( m_windowStyle & wxSL_LABELS )
207 {
208 m_labels = new wxSubwindows(SliderLabel_Last);
209
210 HWND hwndParent = GetHwndOf(parent);
211 for ( size_t n = 0; n < SliderLabel_Last; n++ )
212 {
213 (*m_labels)[n] = ::CreateWindow
214 (
215 wxT("STATIC"),
216 NULL,
217 WS_CHILD | WS_VISIBLE | SS_CENTER,
218 0, 0, 0, 0,
219 hwndParent,
220 (HMENU)NewControlId(),
221 wxGetInstance(),
222 NULL
223 );
224 }
225
226 m_labels->SetFont(GetFont());
227 }
228
229 // now create the main control too
230 if ( !MSWCreateControl(TRACKBAR_CLASS, wxEmptyString, pos, size) )
231 return false;
232
233 // and initialize everything
234 SetRange(minValue, maxValue);
235 SetValue(value);
236 SetPageSize((maxValue - minValue)/10);
237
238 // we need to position the labels correctly if we have them and if
239 // SetSize() hadn't been called before (when best size was determined by
240 // MSWCreateControl()) as in this case they haven't been put in place yet
241 if ( m_labels && size.x != wxDefaultCoord && size.y != wxDefaultCoord )
242 {
243 SetSize(size);
244 }
245
246 return true;
247}
248
249WXDWORD wxSlider::MSWGetStyle(long style, WXDWORD *exstyle) const
250{
251 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
252
253 // TBS_HORZ, TBS_RIGHT and TBS_BOTTOM are 0 but do include them for clarity
254 msStyle |= style & wxSL_VERTICAL ? TBS_VERT : TBS_HORZ;
255
256 if ( style & wxSL_BOTH )
257 {
258 // this fully specifies the style combined with TBS_VERT/HORZ above
259 msStyle |= TBS_BOTH;
260 }
261 else // choose one direction
262 {
263 if ( style & wxSL_LEFT )
264 msStyle |= TBS_LEFT;
265 else if ( style & wxSL_RIGHT )
266 msStyle |= TBS_RIGHT;
267 else if ( style & wxSL_TOP )
268 msStyle |= TBS_TOP;
269 else if ( style & wxSL_BOTTOM )
270 msStyle |= TBS_BOTTOM;
271 }
272
273 if ( style & wxSL_AUTOTICKS )
274 msStyle |= TBS_AUTOTICKS;
275 else
276 msStyle |= TBS_NOTICKS;
277
278 if ( style & wxSL_SELRANGE )
279 msStyle |= TBS_ENABLESELRANGE;
280
281 return msStyle;
282}
283
284wxSlider::~wxSlider()
285{
286 delete m_labels;
287}
288
289// ----------------------------------------------------------------------------
290// event handling
291// ----------------------------------------------------------------------------
292
293bool wxSlider::MSWOnScroll(int WXUNUSED(orientation),
294 WXWORD wParam,
295 WXWORD WXUNUSED(pos),
296 WXHWND control)
297{
298 wxEventType scrollEvent;
299 switch ( wParam )
300 {
301 case SB_TOP:
302 scrollEvent = wxEVT_SCROLL_TOP;
303 break;
304
305 case SB_BOTTOM:
306 scrollEvent = wxEVT_SCROLL_BOTTOM;
307 break;
308
309 case SB_LINEUP:
310 scrollEvent = wxEVT_SCROLL_LINEUP;
311 break;
312
313 case SB_LINEDOWN:
314 scrollEvent = wxEVT_SCROLL_LINEDOWN;
315 break;
316
317 case SB_PAGEUP:
318 scrollEvent = wxEVT_SCROLL_PAGEUP;
319 break;
320
321 case SB_PAGEDOWN:
322 scrollEvent = wxEVT_SCROLL_PAGEDOWN;
323 break;
324
325 case SB_THUMBTRACK:
326 scrollEvent = wxEVT_SCROLL_THUMBTRACK;
327 m_isDragging = true;
328 break;
329
330 case SB_THUMBPOSITION:
331 if ( m_isDragging )
332 {
333 scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
334 m_isDragging = false;
335 }
336 else
337 {
338 // this seems to only happen when the mouse wheel is used: in
339 // this case, as it might be unexpected to get THUMBRELEASE
340 // without preceding THUMBTRACKs, we don't generate it at all
341 // but generate CHANGED event because the control itself does
342 // not send us SB_ENDSCROLL for whatever reason when mouse
343 // wheel is used
344 scrollEvent = wxEVT_SCROLL_CHANGED;
345 }
346 break;
347
348 case SB_ENDSCROLL:
349 scrollEvent = wxEVT_SCROLL_CHANGED;
350 break;
351
352 default:
353 // unknown scroll event?
354 return false;
355 }
356
357 int newPos = ValueInvertOrNot((int) ::SendMessage((HWND) control, TBM_GETPOS, 0, 0));
358 if ( (newPos < GetMin()) || (newPos > GetMax()) )
359 {
360 // out of range - but we did process it
361 return true;
362 }
363
364 SetValue(newPos);
365
366 wxScrollEvent event(scrollEvent, m_windowId);
367 event.SetPosition(newPos);
368 event.SetEventObject( this );
369 GetEventHandler()->ProcessEvent(event);
370
371 wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, GetId() );
372 cevent.SetInt( newPos );
373 cevent.SetEventObject( this );
374
375 return GetEventHandler()->ProcessEvent( cevent );
376}
377
378void wxSlider::Command (wxCommandEvent & event)
379{
380 SetValue (event.GetInt());
381 ProcessCommand (event);
382}
383
384// ----------------------------------------------------------------------------
385// geometry stuff
386// ----------------------------------------------------------------------------
387
388wxRect wxSlider::GetBoundingBox() const
389{
390 // take care not to call our own functions which would call us recursively
391 int x, y, w, h;
392 wxSliderBase::DoGetPosition(&x, &y);
393 wxSliderBase::DoGetSize(&w, &h);
394
395 wxRect rect(x, y, w, h);
396 if ( m_labels )
397 {
398 wxRect lrect = m_labels->GetBoundingBox();
399 GetParent()->ScreenToClient(&lrect.x, &lrect.y);
400 rect.Union(lrect);
401 }
402
403 return rect;
404}
405
406void wxSlider::DoGetSize(int *width, int *height) const
407{
408 wxRect rect = GetBoundingBox();
409
410 if ( width )
411 *width = rect.width;
412 if ( height )
413 *height = rect.height;
414}
415
416void wxSlider::DoGetPosition(int *x, int *y) const
417{
418 wxRect rect = GetBoundingBox();
419
420 if ( x )
421 *x = rect.x;
422 if ( y )
423 *y = rect.y;
424}
425
426int wxSlider::GetLabelsSize(int *width) const
427{
428 int cy;
429
430 if ( width )
431 {
432 // find the max label width
433 int wLabelMin, wLabelMax;
434 GetTextExtent(Format(m_rangeMin), &wLabelMin, &cy);
435 GetTextExtent(Format(m_rangeMax), &wLabelMax, &cy);
436
437 *width = wxMax(wLabelMin, wLabelMax);
438 }
439 else
440 {
441 cy = GetCharHeight();
442 }
443
444 return EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
445}
446
447void wxSlider::DoMoveWindow(int x, int y, int width, int height)
448{
449 // all complications below are because we need to position the labels,
450 // without them everything is easy
451 if ( !m_labels )
452 {
453 wxSliderBase::DoMoveWindow(x, y, width, height);
454 return;
455 }
456
457 // be careful to position the slider itself after moving the labels as
458 // otherwise our GetBoundingBox(), which is called from WM_SIZE handler,
459 // would return a wrong result and wrong size would be cached internally
460 if ( HasFlag(wxSL_VERTICAL) )
461 {
462 int wLabel;
463 int hLabel = GetLabelsSize(&wLabel);
464
465 int xLabel = HasFlag(wxSL_LEFT) ? x + width - wLabel : x;
466
467 // position all labels: min at the top, value in the middle and max at
468 // the bottom
469 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
470 xLabel, y, wLabel, hLabel);
471
472 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
473 xLabel, y + (height - hLabel)/2, wLabel, hLabel);
474
475 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
476 xLabel, y + height - hLabel, wLabel, hLabel);
477
478 // position the slider itself along the left/right edge
479 wxSliderBase::DoMoveWindow(HasFlag(wxSL_LEFT) ? x : x + wLabel + HGAP,
480 y + hLabel/2,
481 width - wLabel - HGAP,
482 height - hLabel);
483 }
484 else // horizontal
485 {
486 int wLabel;
487 int hLabel = GetLabelsSize(&wLabel);
488
489 int yLabel = HasFlag(wxSL_TOP) ? y + height - hLabel : y;
490
491 // position all labels: min on the left, value in the middle and max to
492 // the right
493 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Min],
494 x, yLabel, wLabel, hLabel);
495
496 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Value],
497 x + (width - wLabel)/2, yLabel, wLabel, hLabel);
498
499 DoMoveSibling((HWND)(*m_labels)[SliderLabel_Max],
500 x + width - wLabel, yLabel, wLabel, hLabel);
501
502 // position the slider itself along the top/bottom edge
503 wxSliderBase::DoMoveWindow(x,
504 HasFlag(wxSL_TOP) ? y : y + hLabel,
505 width,
506 height - hLabel);
507 }
508}
509
510wxSize wxSlider::DoGetBestSize() const
511{
512 // these values are arbitrary
513 static const int length = 100;
514 static const int thumb = 24;
515 static const int ticks = 8;
516
517 int *width;
518 wxSize size;
519 if ( HasFlag(wxSL_VERTICAL) )
520 {
521 size.x = thumb;
522 size.y = length;
523 width = &size.x;
524
525 if ( m_labels )
526 {
527 int wLabel;
528 int hLabel = GetLabelsSize(&wLabel);
529
530 // account for the labels
531 size.x += HGAP + wLabel;
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 // labels add extra height
546 size.y += GetLabelsSize();
547 }
548 }
549
550 // need extra space to show ticks
551 if ( HasFlag(wxSL_TICKS) )
552 {
553 *width += ticks;
554
555 // and maybe twice as much if we show them on both sides
556 if ( HasFlag(wxSL_BOTH) )
557 *width += ticks;
558 }
559
560 return size;
561}
562
563// ----------------------------------------------------------------------------
564// slider-specific methods
565// ----------------------------------------------------------------------------
566
567int wxSlider::GetValue() const
568{
569 return ValueInvertOrNot(::SendMessage(GetHwnd(), TBM_GETPOS, 0, 0));
570}
571
572void wxSlider::SetValue(int value)
573{
574 ::SendMessage(GetHwnd(), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)ValueInvertOrNot(value));
575
576 if ( m_labels )
577 {
578 ::SetWindowText((*m_labels)[SliderLabel_Value], Format(value));
579 }
580}
581
582void wxSlider::SetRange(int minValue, int maxValue)
583{
584 m_rangeMin = minValue;
585 m_rangeMax = maxValue;
586
587 ::SendMessage(GetHwnd(), TBM_SETRANGEMIN, TRUE, m_rangeMin);
588 ::SendMessage(GetHwnd(), TBM_SETRANGEMAX, TRUE, m_rangeMax);
589
590 if ( m_labels )
591 {
592 ::SetWindowText((*m_labels)[SliderLabel_Min], Format(ValueInvertOrNot(m_rangeMin)));
593 ::SetWindowText((*m_labels)[SliderLabel_Max], Format(ValueInvertOrNot(m_rangeMax)));
594 }
595}
596
597void wxSlider::SetTickFreq(int n, int pos)
598{
599 m_tickFreq = n;
600 ::SendMessage( GetHwnd(), TBM_SETTICFREQ, (WPARAM) n, (LPARAM) pos );
601}
602
603void wxSlider::SetPageSize(int pageSize)
604{
605 ::SendMessage( GetHwnd(), TBM_SETPAGESIZE, (WPARAM) 0, (LPARAM) pageSize );
606 m_pageSize = pageSize;
607}
608
609int wxSlider::GetPageSize() const
610{
611 return m_pageSize;
612}
613
614void wxSlider::ClearSel()
615{
616 ::SendMessage(GetHwnd(), TBM_CLEARSEL, (WPARAM) TRUE, (LPARAM) 0);
617}
618
619void wxSlider::ClearTicks()
620{
621 ::SendMessage(GetHwnd(), TBM_CLEARTICS, (WPARAM) TRUE, (LPARAM) 0);
622}
623
624void wxSlider::SetLineSize(int lineSize)
625{
626 m_lineSize = lineSize;
627 ::SendMessage(GetHwnd(), TBM_SETLINESIZE, (WPARAM) 0, (LPARAM) lineSize);
628}
629
630int wxSlider::GetLineSize() const
631{
632 return (int)::SendMessage(GetHwnd(), TBM_GETLINESIZE, 0, 0);
633}
634
635int wxSlider::GetSelEnd() const
636{
637 return (int)::SendMessage(GetHwnd(), TBM_GETSELEND, 0, 0);
638}
639
640int wxSlider::GetSelStart() const
641{
642 return (int)::SendMessage(GetHwnd(), TBM_GETSELSTART, 0, 0);
643}
644
645void wxSlider::SetSelection(int minPos, int maxPos)
646{
647 ::SendMessage(GetHwnd(), TBM_SETSEL,
648 (WPARAM) TRUE /* redraw */,
649 (LPARAM) MAKELONG( minPos, maxPos) );
650}
651
652void wxSlider::SetThumbLength(int len)
653{
654 ::SendMessage(GetHwnd(), TBM_SETTHUMBLENGTH, (WPARAM) len, (LPARAM) 0);
655}
656
657int wxSlider::GetThumbLength() const
658{
659 return (int)::SendMessage( GetHwnd(), TBM_GETTHUMBLENGTH, 0, 0);
660}
661
662void wxSlider::SetTick(int tickPos)
663{
664 ::SendMessage( GetHwnd(), TBM_SETTIC, (WPARAM) 0, (LPARAM) tickPos );
665}
666
667// ----------------------------------------------------------------------------
668// composite control methods
669// ----------------------------------------------------------------------------
670
671WXHWND wxSlider::GetStaticMin() const
672{
673 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Min] : NULL;
674}
675
676WXHWND wxSlider::GetStaticMax() const
677{
678 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Max] : NULL;
679}
680
681WXHWND wxSlider::GetEditValue() const
682{
683 return m_labels ? (WXHWND)(*m_labels)[SliderLabel_Value] : NULL;
684}
685
686WX_FORWARD_STD_METHODS_TO_SUBWINDOWS(wxSlider, wxSliderBase, m_labels)
687
688#endif // wxUSE_SLIDER