]> git.saurik.com Git - wxWidgets.git/blob - src/univ/scrolbar.cpp
use WXSIZEOF(x) instead of sizeof(x)/sizeof(x[0]) (closes #10701)
[wxWidgets.git] / src / univ / scrolbar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/scrolbar.cpp
3 // Purpose: wxScrollBar implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 20.08.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_SCROLLBAR
27
28 #include "wx/scrolbar.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/timer.h"
32 #include "wx/dcclient.h"
33 #include "wx/validate.h"
34 #include "wx/log.h"
35 #endif
36
37 #include "wx/univ/scrtimer.h"
38
39 #include "wx/univ/renderer.h"
40 #include "wx/univ/inphand.h"
41 #include "wx/univ/theme.h"
42
43 #if wxDEBUG_LEVEL >= 2
44 #define WXDEBUG_SCROLLBAR
45 #endif
46
47 #if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
48 #include "wx/msw/private.h"
49 #endif
50
51 // ----------------------------------------------------------------------------
52 // wxScrollBarTimer: this class is used to repeatedly scroll the scrollbar
53 // when the mouse is help pressed on the arrow or on the bar. It generates the
54 // given scroll action command periodically.
55 // ----------------------------------------------------------------------------
56
57 class wxScrollBarTimer : public wxScrollTimer
58 {
59 public:
60 wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
61 const wxControlAction& action,
62 wxScrollBar *control);
63
64 protected:
65 virtual bool DoNotify();
66
67 private:
68 wxStdScrollBarInputHandler *m_handler;
69 wxControlAction m_action;
70 wxScrollBar *m_control;
71 };
72
73 // ============================================================================
74 // implementation
75 // ============================================================================
76
77 IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl)
78
79 BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase)
80 END_EVENT_TABLE()
81
82 // ----------------------------------------------------------------------------
83 // creation
84 // ----------------------------------------------------------------------------
85
86 #ifdef __VISUALC__
87 // warning C4355: 'this' : used in base member initializer list
88 #pragma warning(disable:4355) // so what? disable it...
89 #endif
90
91 wxScrollBar::wxScrollBar()
92 : m_arrows(this)
93 {
94 Init();
95 }
96
97 wxScrollBar::wxScrollBar(wxWindow *parent,
98 wxWindowID id,
99 const wxPoint& pos,
100 const wxSize& size,
101 long style,
102 const wxValidator& validator,
103 const wxString& name)
104 : m_arrows(this)
105 {
106 Init();
107
108 (void)Create(parent, id, pos, size, style, validator, name);
109 }
110
111 #ifdef __VISUALC__
112 // warning C4355: 'this' : used in base member initializer list
113 #pragma warning(default:4355)
114 #endif
115
116 void wxScrollBar::Init()
117 {
118 m_range =
119 m_thumbSize =
120 m_thumbPos =
121 m_pageSize = 0;
122
123 m_thumbPosOld = -1;
124
125 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
126 {
127 m_elementsState[n] = 0;
128 }
129
130 m_dirty = false;
131 }
132
133 bool wxScrollBar::Create(wxWindow *parent,
134 wxWindowID id,
135 const wxPoint &pos,
136 const wxSize &size,
137 long style,
138 const wxValidator& validator,
139 const wxString &name)
140 {
141 // the scrollbars never have the border
142 style &= ~wxBORDER_MASK;
143
144 if ( !wxControl::Create(parent, id, pos, size, style, validator, name) )
145 return false;
146
147 SetInitialSize(size);
148
149 // override the cursor of the target window (if any)
150 SetCursor(wxCURSOR_ARROW);
151
152 CreateInputHandler(wxINP_HANDLER_SCROLLBAR);
153
154 return true;
155 }
156
157 wxScrollBar::~wxScrollBar()
158 {
159 }
160
161 // ----------------------------------------------------------------------------
162 // misc accessors
163 // ----------------------------------------------------------------------------
164
165 bool wxScrollBar::IsStandalone() const
166 {
167 wxWindow *parent = GetParent();
168 if ( !parent )
169 {
170 return true;
171 }
172
173 return (parent->GetScrollbar(wxHORIZONTAL) != this) &&
174 (parent->GetScrollbar(wxVERTICAL) != this);
175 }
176
177 bool wxScrollBar::AcceptsFocus() const
178 {
179 // the window scrollbars never accept focus
180 return wxScrollBarBase::AcceptsFocus() && IsStandalone();
181 }
182
183 // ----------------------------------------------------------------------------
184 // scrollbar API
185 // ----------------------------------------------------------------------------
186
187 void wxScrollBar::DoSetThumb(int pos)
188 {
189 // don't assert hecks here, we're a private function which is meant to be
190 // called with any args at all
191 if ( pos < 0 )
192 {
193 pos = 0;
194 }
195 else if ( pos > m_range - m_thumbSize )
196 {
197 pos = m_range - m_thumbSize;
198 }
199
200 if ( m_thumbPos == pos )
201 {
202 // nothing changed, avoid refreshes which would provoke flicker
203 return;
204 }
205
206 if ( m_thumbPosOld == -1 )
207 {
208 // remember the old thumb position
209 m_thumbPosOld = m_thumbPos;
210 }
211
212 m_thumbPos = pos;
213
214 // we have to refresh the part of the bar which was under the thumb and the
215 // thumb itself
216 m_elementsState[Element_Thumb] |= wxCONTROL_DIRTY;
217 m_elementsState[m_thumbPos > m_thumbPosOld
218 ? Element_Bar_1 : Element_Bar_2] |= wxCONTROL_DIRTY;
219 m_dirty = true;
220 }
221
222 int wxScrollBar::GetThumbPosition() const
223 {
224 return m_thumbPos;
225 }
226
227 int wxScrollBar::GetThumbSize() const
228 {
229 return m_thumbSize;
230 }
231
232 int wxScrollBar::GetPageSize() const
233 {
234 return m_pageSize;
235 }
236
237 int wxScrollBar::GetRange() const
238 {
239 return m_range;
240 }
241
242 void wxScrollBar::SetThumbPosition(int pos)
243 {
244 wxCHECK_RET( pos >= 0 && pos <= m_range, _T("thumb position out of range") );
245
246 DoSetThumb(pos);
247 }
248
249 void wxScrollBar::SetScrollbar(int position, int thumbSize,
250 int range, int pageSize,
251 bool refresh)
252 {
253 // we only refresh everything when the range changes, thumb position
254 // changes are handled in OnIdle
255 bool needsRefresh = (range != m_range) ||
256 (thumbSize != m_thumbSize) ||
257 (pageSize != m_pageSize);
258
259 // set all parameters
260 m_range = range;
261 m_thumbSize = thumbSize;
262 SetThumbPosition(position);
263 m_pageSize = pageSize;
264
265 // ignore refresh parameter unless we really need to refresh everything -
266 // there ir a lot of existing code which just calls SetScrollbar() without
267 // specifying the last parameter even though it doesn't need at all to
268 // refresh the window immediately
269 if ( refresh && needsRefresh )
270 {
271 // and update the window
272 Refresh();
273 Update();
274 }
275 }
276
277 // ----------------------------------------------------------------------------
278 // geometry
279 // ----------------------------------------------------------------------------
280
281 wxSize wxScrollBar::DoGetBestClientSize() const
282 {
283 // this dimension is completely arbitrary
284 static const wxCoord SIZE = 140;
285
286 wxSize size = m_renderer->GetScrollbarArrowSize();
287 if ( IsVertical() )
288 {
289 size.y = SIZE;
290 }
291 else // horizontal
292 {
293 size.x = SIZE;
294 }
295
296 return size;
297 }
298
299 wxScrollArrows::Arrow wxScrollBar::HitTestArrow(const wxPoint& pt) const
300 {
301 switch ( HitTestBar(pt) )
302 {
303 case wxHT_SCROLLBAR_ARROW_LINE_1:
304 return wxScrollArrows::Arrow_First;
305
306 case wxHT_SCROLLBAR_ARROW_LINE_2:
307 return wxScrollArrows::Arrow_Second;
308
309 default:
310 return wxScrollArrows::Arrow_None;
311 }
312 }
313
314 wxHitTest wxScrollBar::HitTestBar(const wxPoint& pt) const
315 {
316 // we only need to work with either x or y coord depending on the
317 // orientation, choose one (but still check the other one to verify if the
318 // mouse is in the window at all)
319 const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize();
320
321 wxCoord coord, sizeArrow, sizeTotal;
322 wxSize size = GetSize();
323 if ( GetWindowStyle() & wxVERTICAL )
324 {
325 if ( pt.x < 0 || pt.x > size.x )
326 return wxHT_NOWHERE;
327
328 coord = pt.y;
329 sizeArrow = sizeArrowSB.y;
330 sizeTotal = size.y;
331 }
332 else // horizontal
333 {
334 if ( pt.y < 0 || pt.y > size.y )
335 return wxHT_NOWHERE;
336
337 coord = pt.x;
338 sizeArrow = sizeArrowSB.x;
339 sizeTotal = size.x;
340 }
341
342 // test for the arrows first as it's faster
343 if ( coord < 0 || coord > sizeTotal )
344 {
345 return wxHT_NOWHERE;
346 }
347 else if ( coord < sizeArrow )
348 {
349 return wxHT_SCROLLBAR_ARROW_LINE_1;
350 }
351 else if ( coord > sizeTotal - sizeArrow )
352 {
353 return wxHT_SCROLLBAR_ARROW_LINE_2;
354 }
355 else
356 {
357 // calculate the thumb position in pixels
358 sizeTotal -= 2*sizeArrow;
359 wxCoord thumbStart, thumbEnd;
360 int range = GetRange();
361 if ( !range )
362 {
363 // clicking the scrollbar without range has no effect
364 return wxHT_NOWHERE;
365 }
366 else
367 {
368 GetScrollBarThumbSize(sizeTotal,
369 GetThumbPosition(),
370 GetThumbSize(),
371 range,
372 &thumbStart,
373 &thumbEnd);
374 }
375
376 // now compare with the thumb position
377 coord -= sizeArrow;
378 if ( coord < thumbStart )
379 return wxHT_SCROLLBAR_BAR_1;
380 else if ( coord > thumbEnd )
381 return wxHT_SCROLLBAR_BAR_2;
382 else
383 return wxHT_SCROLLBAR_THUMB;
384 }
385 }
386
387 /* static */
388 void wxScrollBar::GetScrollBarThumbSize(wxCoord length,
389 int thumbPos,
390 int thumbSize,
391 int range,
392 wxCoord *thumbStart,
393 wxCoord *thumbEnd)
394 {
395 // the thumb can't be made less than this number of pixels
396 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
397
398 *thumbStart = (length*thumbPos) / range;
399 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
400
401 if ( *thumbEnd - *thumbStart < thumbMinWidth )
402 {
403 // adjust the end if possible
404 if ( *thumbStart <= length - thumbMinWidth )
405 {
406 // yes, just make it wider
407 *thumbEnd = *thumbStart + thumbMinWidth;
408 }
409 else // it is at the bottom of the scrollbar
410 {
411 // so move it a bit up
412 *thumbStart = length - thumbMinWidth;
413 *thumbEnd = length;
414 }
415 }
416 }
417
418 wxRect wxScrollBar::GetScrollbarRect(wxScrollBar::Element elem,
419 int thumbPos) const
420 {
421 if ( thumbPos == -1 )
422 {
423 thumbPos = GetThumbPosition();
424 }
425
426 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
427
428 wxSize sizeTotal = GetClientSize();
429 wxCoord *start, *width;
430 wxCoord length, arrow;
431 wxRect rect;
432 if ( IsVertical() )
433 {
434 rect.x = 0;
435 rect.width = sizeTotal.x;
436 length = sizeTotal.y;
437 start = &rect.y;
438 width = &rect.height;
439 arrow = sizeArrow.y;
440 }
441 else // horizontal
442 {
443 rect.y = 0;
444 rect.height = sizeTotal.y;
445 length = sizeTotal.x;
446 start = &rect.x;
447 width = &rect.width;
448 arrow = sizeArrow.x;
449 }
450
451 switch ( elem )
452 {
453 case wxScrollBar::Element_Arrow_Line_1:
454 *start = 0;
455 *width = arrow;
456 break;
457
458 case wxScrollBar::Element_Arrow_Line_2:
459 *start = length - arrow;
460 *width = arrow;
461 break;
462
463 case wxScrollBar::Element_Arrow_Page_1:
464 case wxScrollBar::Element_Arrow_Page_2:
465 // we don't have them at all
466 break;
467
468 case wxScrollBar::Element_Thumb:
469 case wxScrollBar::Element_Bar_1:
470 case wxScrollBar::Element_Bar_2:
471 // we need to calculate the thumb position - do it
472 {
473 length -= 2*arrow;
474 wxCoord thumbStart, thumbEnd;
475 int range = GetRange();
476 if ( !range )
477 {
478 thumbStart =
479 thumbEnd = 0;
480 }
481 else
482 {
483 GetScrollBarThumbSize(length,
484 thumbPos,
485 GetThumbSize(),
486 range,
487 &thumbStart,
488 &thumbEnd);
489 }
490
491 if ( elem == wxScrollBar::Element_Thumb )
492 {
493 *start = thumbStart;
494 *width = thumbEnd - thumbStart;
495 }
496 else if ( elem == wxScrollBar::Element_Bar_1 )
497 {
498 *start = 0;
499 *width = thumbStart;
500 }
501 else // elem == wxScrollBar::Element_Bar_2
502 {
503 *start = thumbEnd;
504 *width = length - thumbEnd;
505 }
506
507 // everything is relative to the start of the shaft so far
508 *start += arrow;
509 }
510 break;
511
512 case wxScrollBar::Element_Max:
513 default:
514 wxFAIL_MSG( _T("unknown scrollbar element") );
515 }
516
517 return rect;
518 }
519
520 wxCoord wxScrollBar::GetScrollbarSize() const
521 {
522 const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize();
523
524 wxCoord sizeArrow, sizeTotal;
525 if ( GetWindowStyle() & wxVERTICAL )
526 {
527 sizeArrow = sizeArrowSB.y;
528 sizeTotal = GetSize().y;
529 }
530 else // horizontal
531 {
532 sizeArrow = sizeArrowSB.x;
533 sizeTotal = GetSize().x;
534 }
535
536 return sizeTotal - 2*sizeArrow;
537 }
538
539
540 wxCoord wxScrollBar::ScrollbarToPixel(int thumbPos)
541 {
542 int range = GetRange();
543 if ( !range )
544 {
545 // the only valid position anyhow
546 return 0;
547 }
548
549 if ( thumbPos == -1 )
550 {
551 // by default use the current thumb position
552 thumbPos = GetThumbPosition();
553 }
554
555 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
556 return (thumbPos * GetScrollbarSize()) / range
557 + (IsVertical() ? sizeArrow.y : sizeArrow.x);
558 }
559
560 int wxScrollBar::PixelToScrollbar(wxCoord coord)
561 {
562 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
563 return ((coord - (IsVertical() ? sizeArrow.y : sizeArrow.x)) *
564 GetRange() ) / GetScrollbarSize();
565 }
566
567 // ----------------------------------------------------------------------------
568 // drawing
569 // ----------------------------------------------------------------------------
570
571 void wxScrollBar::OnInternalIdle()
572 {
573 UpdateThumb();
574 wxControl::OnInternalIdle();
575 }
576
577 void wxScrollBar::UpdateThumb()
578 {
579 if ( m_dirty )
580 {
581 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
582 {
583 if ( m_elementsState[n] & wxCONTROL_DIRTY )
584 {
585 wxRect rect = GetScrollbarRect((Element)n);
586
587 if ( rect.width && rect.height )
588 {
589 // we try to avoid redrawing the entire shaft (which might
590 // be quite long) if possible by only redrawing the area
591 // wich really changed
592 if ( (n == Element_Bar_1 || n == Element_Bar_2) &&
593 (m_thumbPosOld != -1) )
594 {
595 // the less efficient but more reliable (i.e. this will
596 // probably work everywhere) version: refresh the
597 // distance covered by thumb since the last update
598 #if 0
599 wxRect rectOld =
600 GetRenderer()->GetScrollbarRect(this,
601 (Element)n,
602 m_thumbPosOld);
603 if ( IsVertical() )
604 {
605 if ( n == Element_Bar_1 )
606 rect.SetTop(rectOld.GetBottom());
607 else
608 rect.SetBottom(rectOld.GetBottom());
609 }
610 else // horizontal
611 {
612 if ( n == Element_Bar_1 )
613 rect.SetLeft(rectOld.GetRight());
614 else
615 rect.SetRight(rectOld.GetRight());
616 }
617 #else // efficient version: only repaint the area occupied by
618 // the thumb previously - we can't do better than this
619 rect = GetScrollbarRect(Element_Thumb, m_thumbPosOld);
620 #endif // 0/1
621 }
622
623 #ifdef WXDEBUG_SCROLLBAR
624 static bool s_refreshDebug = false;
625 if ( s_refreshDebug )
626 {
627 wxClientDC dc(this);
628 dc.SetBrush(*wxCYAN_BRUSH);
629 dc.SetPen(*wxTRANSPARENT_PEN);
630 dc.DrawRectangle(rect);
631
632 // under Unix we use "--sync" X option for this
633 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
634 ::GdiFlush();
635 ::Sleep(200);
636 #endif // __WXMSW__
637 }
638 #endif // WXDEBUG_SCROLLBAR
639
640 Refresh(false, &rect);
641 }
642
643 m_elementsState[n] &= ~wxCONTROL_DIRTY;
644 }
645 }
646
647 m_dirty = false;
648 }
649 }
650
651 void wxScrollBar::DoDraw(wxControlRenderer *renderer)
652 {
653 renderer->DrawScrollbar(this, m_thumbPosOld);
654
655 // clear all dirty flags
656 m_dirty = false;
657 m_thumbPosOld = -1;
658 }
659
660 // ----------------------------------------------------------------------------
661 // state flags
662 // ----------------------------------------------------------------------------
663
664 static inline wxScrollBar::Element ElementForArrow(wxScrollArrows::Arrow arrow)
665 {
666 return arrow == wxScrollArrows::Arrow_First
667 ? wxScrollBar::Element_Arrow_Line_1
668 : wxScrollBar::Element_Arrow_Line_2;
669 }
670
671 int wxScrollBar::GetArrowState(wxScrollArrows::Arrow arrow) const
672 {
673 return GetState(ElementForArrow(arrow));
674 }
675
676 void wxScrollBar::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
677 {
678 Element which = ElementForArrow(arrow);
679 int state = GetState(which);
680 if ( set )
681 state |= flag;
682 else
683 state &= ~flag;
684
685 SetState(which, state);
686 }
687
688 int wxScrollBar::GetState(Element which) const
689 {
690 // if the entire scrollbar is disabled, all of its elements are too
691 int flags = m_elementsState[which];
692 if ( !IsEnabled() )
693 flags |= wxCONTROL_DISABLED;
694
695 return flags;
696 }
697
698 void wxScrollBar::SetState(Element which, int flags)
699 {
700 if ( (int)(m_elementsState[which] & ~wxCONTROL_DIRTY) != flags )
701 {
702 m_elementsState[which] = flags | wxCONTROL_DIRTY;
703
704 m_dirty = true;
705 }
706 }
707
708 // ----------------------------------------------------------------------------
709 // input processing
710 // ----------------------------------------------------------------------------
711
712 bool wxScrollBar::OnArrow(wxScrollArrows::Arrow arrow)
713 {
714 int oldThumbPos = GetThumbPosition();
715 PerformAction(arrow == wxScrollArrows::Arrow_First
716 ? wxACTION_SCROLL_LINE_UP
717 : wxACTION_SCROLL_LINE_DOWN);
718
719 // did we scroll till the end?
720 return GetThumbPosition() != oldThumbPos;
721 }
722
723 bool wxScrollBar::PerformAction(const wxControlAction& action,
724 long numArg,
725 const wxString& strArg)
726 {
727 int thumbOld = m_thumbPos;
728
729 bool notify = false; // send an event about the change?
730
731 wxEventType scrollType;
732
733 // test for thumb move first as these events happen in quick succession
734 if ( action == wxACTION_SCROLL_THUMB_MOVE )
735 {
736 DoSetThumb(numArg);
737
738 // VS: we have to force redraw here, otherwise the thumb will lack
739 // behind mouse cursor
740 UpdateThumb();
741
742 scrollType = wxEVT_SCROLLWIN_THUMBTRACK;
743 }
744 else if ( action == wxACTION_SCROLL_LINE_UP )
745 {
746 scrollType = wxEVT_SCROLLWIN_LINEUP;
747 ScrollLines(-1);
748 }
749 else if ( action == wxACTION_SCROLL_LINE_DOWN )
750 {
751 scrollType = wxEVT_SCROLLWIN_LINEDOWN;
752 ScrollLines(1);
753 }
754 else if ( action == wxACTION_SCROLL_PAGE_UP )
755 {
756 scrollType = wxEVT_SCROLLWIN_PAGEUP;
757 ScrollPages(-1);
758 }
759 else if ( action == wxACTION_SCROLL_PAGE_DOWN )
760 {
761 scrollType = wxEVT_SCROLLWIN_PAGEDOWN;
762 ScrollPages(1);
763 }
764 else if ( action == wxACTION_SCROLL_START )
765 {
766 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
767 ScrollToStart();
768 }
769 else if ( action == wxACTION_SCROLL_END )
770 {
771 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
772 ScrollToEnd();
773 }
774 else if ( action == wxACTION_SCROLL_THUMB_DRAG )
775 {
776 // we won't use it but this line suppresses the compiler
777 // warning about "variable may be used without having been
778 // initialized"
779 scrollType = wxEVT_NULL;
780 }
781 else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
782 {
783 // always notify about this
784 notify = true;
785 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
786 }
787 else
788 return wxControl::PerformAction(action, numArg, strArg);
789
790 // has scrollbar position changed?
791 bool changed = m_thumbPos != thumbOld;
792 if ( notify || changed )
793 {
794 if ( IsStandalone() )
795 {
796 // we should generate EVT_SCROLL events for the standalone
797 // scrollbars and not the EVT_SCROLLWIN ones
798 //
799 // NB: we assume that scrollbar events are sequentially numbered
800 // but this should be ok as other code relies on this as well
801 scrollType += wxEVT_SCROLL_TOP - wxEVT_SCROLLWIN_TOP;
802 wxScrollEvent event(scrollType, this->GetId(), m_thumbPos,
803 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
804 event.SetEventObject(this);
805 GetEventHandler()->ProcessEvent(event);
806 }
807 else // part of the window
808 {
809 wxScrollWinEvent event(scrollType, m_thumbPos,
810 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
811 event.SetEventObject(this);
812 GetParent()->GetEventHandler()->ProcessEvent(event);
813 }
814 }
815
816 return true;
817 }
818
819 void wxScrollBar::ScrollToStart()
820 {
821 DoSetThumb(0);
822 }
823
824 void wxScrollBar::ScrollToEnd()
825 {
826 DoSetThumb(m_range - m_thumbSize);
827 }
828
829 bool wxScrollBar::ScrollLines(int nLines)
830 {
831 DoSetThumb(m_thumbPos + nLines);
832 return true;
833 }
834
835 bool wxScrollBar::ScrollPages(int nPages)
836 {
837 DoSetThumb(m_thumbPos + nPages*m_pageSize);
838 return true;
839 }
840
841 /* static */
842 wxInputHandler *wxScrollBar::GetStdInputHandler(wxInputHandler *handlerDef)
843 {
844 static wxStdScrollBarInputHandler
845 s_handler(wxTheme::Get()->GetRenderer(), handlerDef);
846
847 return &s_handler;
848 }
849
850 // ============================================================================
851 // scroll bar input handler
852 // ============================================================================
853
854 // ----------------------------------------------------------------------------
855 // wxScrollBarTimer
856 // ----------------------------------------------------------------------------
857
858 wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
859 const wxControlAction& action,
860 wxScrollBar *control)
861 {
862 m_handler = handler;
863 m_action = action;
864 m_control = control;
865 }
866
867 bool wxScrollBarTimer::DoNotify()
868 {
869 return m_handler->OnScrollTimer(m_control, m_action);
870 }
871
872 // ----------------------------------------------------------------------------
873 // wxStdScrollBarInputHandler
874 // ----------------------------------------------------------------------------
875
876 wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
877 wxInputHandler *handler)
878 : wxStdInputHandler(handler)
879 {
880 m_renderer = renderer;
881 m_winCapture = NULL;
882 m_htLast = wxHT_NOWHERE;
883 m_timerScroll = NULL;
884 }
885
886 wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
887 {
888 // normally, it's NULL by now but just in case the user somehow managed to
889 // keep the mouse captured until now...
890 delete m_timerScroll;
891 }
892
893 void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
894 int flag,
895 bool doIt)
896 {
897 if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
898 {
899 wxScrollBar::Element
900 elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);
901
902 int flags = control->GetState(elem);
903 if ( doIt )
904 flags |= flag;
905 else
906 flags &= ~flag;
907 control->SetState(elem, flags);
908 }
909 }
910
911 bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
912 const wxControlAction& action)
913 {
914 int oldThumbPos = scrollbar->GetThumbPosition();
915 scrollbar->PerformAction(action);
916 if ( scrollbar->GetThumbPosition() != oldThumbPos )
917 return true;
918
919 // we scrolled till the end
920 m_timerScroll->Stop();
921
922 return false;
923 }
924
925 void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
926 {
927 // return everything to the normal state
928 if ( m_winCapture )
929 {
930 m_winCapture->ReleaseMouse();
931 m_winCapture = NULL;
932 }
933
934 m_btnCapture = -1;
935
936 if ( m_timerScroll )
937 {
938 delete m_timerScroll;
939 m_timerScroll = NULL;
940 }
941
942 // unpress the arrow and highlight the current element
943 Press(control, false);
944 }
945
946 wxCoord
947 wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
948 const wxMouseEvent& event) const
949 {
950 wxPoint pt = event.GetPosition();
951 return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
952 }
953
954 void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
955 const wxMouseEvent& event)
956 {
957 int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
958 thumbPos = scrollbar->PixelToScrollbar(thumbPos);
959 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
960 }
961
962 bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
963 const wxKeyEvent& event,
964 bool pressed)
965 {
966 // we only react to the key presses here
967 if ( pressed )
968 {
969 wxControlAction action;
970 switch ( event.GetKeyCode() )
971 {
972 case WXK_DOWN:
973 case WXK_RIGHT: action = wxACTION_SCROLL_LINE_DOWN; break;
974 case WXK_UP:
975 case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break;
976 case WXK_HOME: action = wxACTION_SCROLL_START; break;
977 case WXK_END: action = wxACTION_SCROLL_END; break;
978 case WXK_PAGEUP: action = wxACTION_SCROLL_PAGE_UP; break;
979 case WXK_PAGEDOWN: action = wxACTION_SCROLL_PAGE_DOWN; break;
980 }
981
982 if ( !action.IsEmpty() )
983 {
984 consumer->PerformAction(action);
985
986 return true;
987 }
988 }
989
990 return wxStdInputHandler::HandleKey(consumer, event, pressed);
991 }
992
993 bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
994 const wxMouseEvent& event)
995 {
996 // is this a click event from an acceptable button?
997 int btn = event.GetButton();
998 if ( btn == wxMOUSE_BTN_LEFT )
999 {
1000 // determine which part of the window mouse is in
1001 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
1002 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition());
1003
1004 // when the mouse is pressed on any scrollbar element, we capture it
1005 // and hold capture until the same mouse button is released
1006 if ( event.ButtonDown() || event.ButtonDClick() )
1007 {
1008 if ( !m_winCapture )
1009 {
1010 m_btnCapture = btn;
1011 m_winCapture = consumer->GetInputWindow();
1012 m_winCapture->CaptureMouse();
1013
1014 // generate the command
1015 bool hasAction = true;
1016 wxControlAction action;
1017 switch ( ht )
1018 {
1019 case wxHT_SCROLLBAR_ARROW_LINE_1:
1020 action = wxACTION_SCROLL_LINE_UP;
1021 break;
1022
1023 case wxHT_SCROLLBAR_ARROW_LINE_2:
1024 action = wxACTION_SCROLL_LINE_DOWN;
1025 break;
1026
1027 case wxHT_SCROLLBAR_BAR_1:
1028 action = wxACTION_SCROLL_PAGE_UP;
1029 m_ptStartScrolling = event.GetPosition();
1030 break;
1031
1032 case wxHT_SCROLLBAR_BAR_2:
1033 action = wxACTION_SCROLL_PAGE_DOWN;
1034 m_ptStartScrolling = event.GetPosition();
1035 break;
1036
1037 case wxHT_SCROLLBAR_THUMB:
1038 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
1039 m_ofsMouse = GetMouseCoord(scrollbar, event) -
1040 scrollbar->ScrollbarToPixel();
1041
1042 // fall through: there is no immediate action
1043
1044 default:
1045 hasAction = false;
1046 }
1047
1048 // remove highlighting
1049 Highlight(scrollbar, false);
1050 m_htLast = ht;
1051
1052 // and press the arrow or highlight thumb now instead
1053 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
1054 Highlight(scrollbar, true);
1055 else
1056 Press(scrollbar, true);
1057
1058 // start dragging
1059 if ( hasAction )
1060 {
1061 m_timerScroll = new wxScrollBarTimer(this, action,
1062 scrollbar);
1063 m_timerScroll->StartAutoScroll();
1064 }
1065 //else: no (immediate) action
1066
1067 }
1068 //else: mouse already captured, nothing to do
1069 }
1070 // release mouse if the *same* button went up
1071 else if ( btn == m_btnCapture )
1072 {
1073 if ( m_winCapture )
1074 {
1075 StopScrolling(scrollbar);
1076
1077 // if we were dragging the thumb, send the last event
1078 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
1079 {
1080 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
1081 }
1082
1083 m_htLast = ht;
1084 Highlight(scrollbar, true);
1085 }
1086 else
1087 {
1088 // this is not supposed to happen as the button can't go up
1089 // without going down previously and then we'd have
1090 // m_winCapture by now
1091 wxFAIL_MSG( _T("logic error in mouse capturing code") );
1092 }
1093 }
1094 }
1095
1096 return wxStdInputHandler::HandleMouse(consumer, event);
1097 }
1098
1099 bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1100 const wxMouseEvent& event)
1101 {
1102 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
1103
1104 if ( m_winCapture )
1105 {
1106 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Dragging() )
1107 {
1108 // make the thumb follow the mouse by keeping the same offset
1109 // between the mouse position and the top/left of the thumb
1110 HandleThumbMove(scrollbar, event);
1111
1112 return true;
1113 }
1114
1115 // no other changes are possible while the mouse is captured
1116 return false;
1117 }
1118
1119 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);
1120
1121 if ( event.Dragging() )
1122 {
1123 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition());
1124 if ( ht == m_htLast )
1125 {
1126 // nothing changed
1127 return false;
1128 }
1129
1130 #ifdef DEBUG_MOUSE
1131 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
1132 #endif // DEBUG_MOUSE
1133
1134 Highlight(scrollbar, false);
1135 m_htLast = ht;
1136
1137 if ( !isArrow )
1138 Highlight(scrollbar, true);
1139 //else: already done by wxScrollArrows::HandleMouseMove
1140 }
1141 else if ( event.Leaving() )
1142 {
1143 if ( !isArrow )
1144 Highlight(scrollbar, false);
1145
1146 m_htLast = wxHT_NOWHERE;
1147 }
1148 else // event.Entering()
1149 {
1150 // we don't process this event
1151 return false;
1152 }
1153
1154 // we did something
1155 return true;
1156 }
1157
1158 #endif // wxUSE_SCROLLBAR
1159
1160 #if wxUSE_TIMER
1161
1162 // ----------------------------------------------------------------------------
1163 // wxScrollTimer
1164 // ----------------------------------------------------------------------------
1165
1166 wxScrollTimer::wxScrollTimer()
1167 {
1168 m_skipNext = false;
1169 }
1170
1171 void wxScrollTimer::StartAutoScroll()
1172 {
1173 // start scrolling immediately
1174 if ( !DoNotify() )
1175 {
1176 // ... and end it too
1177 return;
1178 }
1179
1180 // there is an initial delay before the scrollbar starts scrolling -
1181 // implement it by ignoring the first timer expiration and only start
1182 // scrolling from the second one
1183 m_skipNext = true;
1184 Start(200); // FIXME: hardcoded delay
1185 }
1186
1187 void wxScrollTimer::Notify()
1188 {
1189 if ( m_skipNext )
1190 {
1191 // scroll normally now - reduce the delay
1192 Stop();
1193 Start(50); // FIXME: hardcoded delay
1194
1195 m_skipNext = false;
1196 }
1197 else
1198 {
1199 // if DoNotify() returns false, we're already deleted by the timer
1200 // event handler, so don't do anything else here
1201 (void)DoNotify();
1202 }
1203 }
1204
1205 #endif // wxUSE_TIMER