fix for scrollbar's thumb update bug in wxUniv
[wxWidgets.git] / src / univ / scrolbar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "univscrolbar.h"
22 #endif
23
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 #if wxUSE_SCROLLBAR
31
32 #ifndef WX_PRECOMP
33 #include "wx/timer.h"
34
35 #include "wx/dcclient.h"
36 #include "wx/scrolbar.h"
37 #include "wx/validate.h"
38 #endif
39
40 #include "wx/univ/scrtimer.h"
41
42 #include "wx/univ/renderer.h"
43 #include "wx/univ/inphand.h"
44 #include "wx/univ/theme.h"
45
46 #define WXDEBUG_SCROLLBAR
47
48 #ifndef __WXDEBUG__
49 #undef WXDEBUG_SCROLLBAR
50 #endif // !__WXDEBUG__
51
52 #if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
53 #include "wx/msw/private.h"
54 #endif
55
56 // ----------------------------------------------------------------------------
57 // wxScrollBarTimer: this class is used to repeatedly scroll the scrollbar
58 // when the mouse is help pressed on the arrow or on the bar. It generates the
59 // given scroll action command periodically.
60 // ----------------------------------------------------------------------------
61
62 class wxScrollBarTimer : public wxScrollTimer
63 {
64 public:
65 wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
66 const wxControlAction& action,
67 wxScrollBar *control);
68
69 protected:
70 virtual bool DoNotify();
71
72 private:
73 wxStdScrollBarInputHandler *m_handler;
74 wxControlAction m_action;
75 wxScrollBar *m_control;
76 };
77
78 // ============================================================================
79 // implementation
80 // ============================================================================
81
82 IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl)
83
84 BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase)
85 EVT_IDLE(wxScrollBar::OnIdle)
86 END_EVENT_TABLE()
87
88 // ----------------------------------------------------------------------------
89 // creation
90 // ----------------------------------------------------------------------------
91
92 #ifdef __VISUALC__
93 // warning C4355: 'this' : used in base member initializer list
94 #pragma warning(disable:4355) // so what? disable it...
95 #endif
96
97 wxScrollBar::wxScrollBar()
98 : m_arrows(this)
99 {
100 Init();
101 }
102
103 wxScrollBar::wxScrollBar(wxWindow *parent,
104 wxWindowID id,
105 const wxPoint& pos,
106 const wxSize& size,
107 long style,
108 const wxValidator& validator,
109 const wxString& name)
110 : m_arrows(this)
111 {
112 Init();
113
114 (void)Create(parent, id, pos, size, style, validator, name);
115 }
116
117 #ifdef __VISUALC__
118 // warning C4355: 'this' : used in base member initializer list
119 #pragma warning(default:4355)
120 #endif
121
122 void wxScrollBar::Init()
123 {
124 m_range =
125 m_thumbSize =
126 m_thumbPos =
127 m_pageSize = 0;
128
129 m_thumbPosOld = -1;
130
131 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
132 {
133 m_elementsState[n] = 0;
134 }
135
136 m_dirty = FALSE;
137 }
138
139 bool wxScrollBar::Create(wxWindow *parent,
140 wxWindowID id,
141 const wxPoint &pos,
142 const wxSize &size,
143 long style,
144 const wxValidator& validator,
145 const wxString &name)
146 {
147 // the scrollbars never have the border
148 style &= ~wxBORDER_MASK;
149
150 if ( !wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name) )
151 return FALSE;
152
153 SetBestSize(size);
154
155 // override the cursor of the target window (if any)
156 SetCursor(wxCURSOR_ARROW);
157
158 CreateInputHandler(wxINP_HANDLER_SCROLLBAR);
159
160 return TRUE;
161 }
162
163 wxScrollBar::~wxScrollBar()
164 {
165 }
166
167 // ----------------------------------------------------------------------------
168 // scrollbar API
169 // ----------------------------------------------------------------------------
170
171 void wxScrollBar::DoSetThumb(int pos)
172 {
173 // don't assert hecks here, we're a private function which is meant to be
174 // called with any args at all
175 if ( pos < 0 )
176 {
177 pos = 0;
178 }
179 else if ( pos > m_range - m_thumbSize )
180 {
181 pos = m_range - m_thumbSize;
182 }
183
184 if ( m_thumbPos == pos )
185 {
186 // nothing changed, avoid refreshes which would provoke flicker
187 return;
188 }
189
190 if ( m_thumbPosOld == -1 )
191 {
192 // remember the old thumb position
193 m_thumbPosOld = m_thumbPos;
194 }
195
196 m_thumbPos = pos;
197
198 // we have to refresh the part of the bar which was under the thumb and the
199 // thumb itself
200 m_elementsState[Element_Thumb] |= wxCONTROL_DIRTY;
201 m_elementsState[m_thumbPos > m_thumbPosOld
202 ? Element_Bar_1 : Element_Bar_2] |= wxCONTROL_DIRTY;
203 m_dirty = TRUE;
204 }
205
206 int wxScrollBar::GetThumbPosition() const
207 {
208 return m_thumbPos;
209 }
210
211 int wxScrollBar::GetThumbSize() const
212 {
213 return m_thumbSize;
214 }
215
216 int wxScrollBar::GetPageSize() const
217 {
218 return m_pageSize;
219 }
220
221 int wxScrollBar::GetRange() const
222 {
223 return m_range;
224 }
225
226 void wxScrollBar::SetThumbPosition(int pos)
227 {
228 wxCHECK_RET( pos >= 0 && pos <= m_range, _T("thumb position out of range") );
229
230 DoSetThumb(pos);
231 }
232
233 void wxScrollBar::SetScrollbar(int position, int thumbSize,
234 int range, int pageSize,
235 bool refresh)
236 {
237 // we only refresh everythign when the range changes, thumb position
238 // changes are handled in OnIdle
239 bool needsRefresh = (range != m_range) ||
240 (thumbSize != m_thumbSize) ||
241 (pageSize != m_pageSize);
242
243 // set all parameters
244 m_range = range;
245 m_thumbSize = thumbSize;
246 SetThumbPosition(position);
247 m_pageSize = pageSize;
248
249 // ignore refresh parameter unless we really need to refresh everything -
250 // there ir a lot of existing code which just calls SetScrollbar() without
251 // specifying the last parameter even though it doesn't need at all to
252 // refresh the window immediately
253 if ( refresh && needsRefresh )
254 {
255 // and update the window
256 Refresh();
257 Update();
258 }
259 }
260
261 // ----------------------------------------------------------------------------
262 // geometry
263 // ----------------------------------------------------------------------------
264
265 wxSize wxScrollBar::DoGetBestClientSize() const
266 {
267 // this dimension is completely arbitrary
268 static const wxCoord SIZE = 140;
269
270 wxSize size = m_renderer->GetScrollbarArrowSize();
271 if ( IsVertical() )
272 {
273 size.y = SIZE;
274 }
275 else // horizontal
276 {
277 size.x = SIZE;
278 }
279
280 return size;
281 }
282
283 wxScrollArrows::Arrow wxScrollBar::HitTest(const wxPoint& pt) const
284 {
285 switch ( m_renderer->HitTestScrollbar(this, pt) )
286 {
287 case wxHT_SCROLLBAR_ARROW_LINE_1:
288 return wxScrollArrows::Arrow_First;
289
290 case wxHT_SCROLLBAR_ARROW_LINE_2:
291 return wxScrollArrows::Arrow_Second;
292
293 default:
294 return wxScrollArrows::Arrow_None;
295 }
296 }
297
298 // ----------------------------------------------------------------------------
299 // drawing
300 // ----------------------------------------------------------------------------
301
302 void wxScrollBar::OnIdle(wxIdleEvent& event)
303 {
304 UpdateThumb();
305 event.Skip();
306 }
307
308 void wxScrollBar::UpdateThumb()
309 {
310 if ( m_dirty )
311 {
312 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
313 {
314 if ( m_elementsState[n] & wxCONTROL_DIRTY )
315 {
316 wxRect rect = GetRenderer()->GetScrollbarRect(this, (Element)n);
317
318 if ( rect.width && rect.height )
319 {
320 // we try to avoid redrawing the entire shaft (which might
321 // be quite long) if possible by only redrawing the area
322 // wich really changed
323 if ( (n == Element_Bar_1 || n == Element_Bar_2) &&
324 (m_thumbPosOld != -1) )
325 {
326 // the less efficient but more reliable (i.e. this will
327 // probably work everywhere) version: refresh the
328 // distance covered by thumb since the last update
329 #if 0
330 wxRect rectOld =
331 GetRenderer()->GetScrollbarRect(this,
332 (Element)n,
333 m_thumbPosOld);
334 if ( IsVertical() )
335 {
336 if ( n == Element_Bar_1 )
337 rect.SetTop(rectOld.GetBottom());
338 else
339 rect.SetBottom(rectOld.GetBottom());
340 }
341 else // horizontal
342 {
343 if ( n == Element_Bar_1 )
344 rect.SetLeft(rectOld.GetRight());
345 else
346 rect.SetRight(rectOld.GetRight());
347 }
348 #else // efficient version: only repaint the area occupied by
349 // the thumb previously - we can't do better than this
350 rect = GetRenderer()->GetScrollbarRect(this,
351 Element_Thumb,
352 m_thumbPosOld);
353 #endif // 0/1
354 }
355
356 #ifdef WXDEBUG_SCROLLBAR
357 static bool s_refreshDebug = FALSE;
358 if ( s_refreshDebug )
359 {
360 wxClientDC dc(this);
361 dc.SetBrush(*wxCYAN_BRUSH);
362 dc.SetPen(*wxTRANSPARENT_PEN);
363 dc.DrawRectangle(rect);
364
365 // under Unix we use "--sync" X option for this
366 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
367 ::GdiFlush();
368 ::Sleep(200);
369 #endif // __WXMSW__
370 }
371 #endif // WXDEBUG_SCROLLBAR
372
373 Refresh(TRUE, &rect);
374 }
375
376 m_elementsState[n] &= ~wxCONTROL_DIRTY;
377 }
378 }
379
380 m_dirty = FALSE;
381 }
382 }
383
384 void wxScrollBar::DoDraw(wxControlRenderer *renderer)
385 {
386 renderer->DrawScrollbar(this, m_thumbPosOld);
387
388 // clear all dirty flags
389 m_dirty = FALSE;
390 m_thumbPosOld = -1;
391 }
392
393 // ----------------------------------------------------------------------------
394 // state flags
395 // ----------------------------------------------------------------------------
396
397 static inline wxScrollBar::Element ElementForArrow(wxScrollArrows::Arrow arrow)
398 {
399 return arrow == wxScrollArrows::Arrow_First
400 ? wxScrollBar::Element_Arrow_Line_1
401 : wxScrollBar::Element_Arrow_Line_2;
402 }
403
404 int wxScrollBar::GetArrowState(wxScrollArrows::Arrow arrow) const
405 {
406 return GetState(ElementForArrow(arrow));
407 }
408
409 void wxScrollBar::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
410 {
411 Element which = ElementForArrow(arrow);
412 int state = GetState(which);
413 if ( set )
414 state |= flag;
415 else
416 state &= ~flag;
417
418 SetState(which, state);
419 }
420
421 int wxScrollBar::GetState(Element which) const
422 {
423 // if the entire scrollbar is disabled, all of its elements are too
424 int flags = m_elementsState[which];
425 if ( !IsEnabled() )
426 flags |= wxCONTROL_DISABLED;
427
428 return flags;
429 }
430
431 void wxScrollBar::SetState(Element which, int flags)
432 {
433 if ( (int)(m_elementsState[which] & ~wxCONTROL_DIRTY) != flags )
434 {
435 m_elementsState[which] = flags | wxCONTROL_DIRTY;
436
437 m_dirty = TRUE;
438 }
439 }
440
441 // ----------------------------------------------------------------------------
442 // input processing
443 // ----------------------------------------------------------------------------
444
445 bool wxScrollBar::OnArrow(wxScrollArrows::Arrow arrow)
446 {
447 int oldThumbPos = GetThumbPosition();
448 PerformAction(arrow == wxScrollArrows::Arrow_First
449 ? wxACTION_SCROLL_LINE_UP
450 : wxACTION_SCROLL_LINE_DOWN);
451
452 // did we scroll till the end?
453 return GetThumbPosition() != oldThumbPos;
454 }
455
456 bool wxScrollBar::PerformAction(const wxControlAction& action,
457 long numArg,
458 const wxString& strArg)
459 {
460 int thumbOld = m_thumbPos;
461
462 bool notify = FALSE; // send an event about the change?
463
464 wxEventType scrollType;
465
466 // test for thumb move first as these events happen in quick succession
467 if ( action == wxACTION_SCROLL_THUMB_MOVE )
468 {
469 DoSetThumb(numArg);
470
471 // VS: we have to force redraw here, otherwise the thumb will lack
472 // behind mouse cursor
473 UpdateThumb();
474
475 scrollType = wxEVT_SCROLLWIN_THUMBTRACK;
476 }
477 else if ( action == wxACTION_SCROLL_LINE_UP )
478 {
479 scrollType = wxEVT_SCROLLWIN_LINEUP;
480 ScrollLines(-1);
481 }
482 else if ( action == wxACTION_SCROLL_LINE_DOWN )
483 {
484 scrollType = wxEVT_SCROLLWIN_LINEDOWN;
485 ScrollLines(1);
486 }
487 else if ( action == wxACTION_SCROLL_PAGE_UP )
488 {
489 scrollType = wxEVT_SCROLLWIN_PAGEUP;
490 ScrollPages(-1);
491 }
492 else if ( action == wxACTION_SCROLL_PAGE_DOWN )
493 {
494 scrollType = wxEVT_SCROLLWIN_PAGEDOWN;
495 ScrollPages(1);
496 }
497 else if ( action == wxACTION_SCROLL_START )
498 {
499 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
500 ScrollToStart();
501 }
502 else if ( action == wxACTION_SCROLL_END )
503 {
504 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
505 ScrollToEnd();
506 }
507 else if ( action == wxACTION_SCROLL_THUMB_DRAG )
508 {
509 // we won't use it but this line suppresses the compiler
510 // warning about "variable may be used without having been
511 // initialized"
512 scrollType = wxEVT_NULL;
513 }
514 else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
515 {
516 // always notify about this
517 notify = TRUE;
518 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
519 }
520 else
521 return wxControl::PerformAction(action, numArg, strArg);
522
523 // has scrollbar position changed?
524 bool changed = m_thumbPos != thumbOld;
525 if ( notify || changed )
526 {
527 wxScrollWinEvent event(scrollType, m_thumbPos,
528 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
529 event.SetEventObject(this);
530 GetParent()->GetEventHandler()->ProcessEvent(event);
531 }
532
533 return TRUE;
534 }
535
536 void wxScrollBar::ScrollToStart()
537 {
538 DoSetThumb(0);
539 }
540
541 void wxScrollBar::ScrollToEnd()
542 {
543 DoSetThumb(m_range - m_thumbSize);
544 }
545
546 bool wxScrollBar::ScrollLines(int nLines)
547 {
548 DoSetThumb(m_thumbPos + nLines);
549 return TRUE;
550 }
551
552 bool wxScrollBar::ScrollPages(int nPages)
553 {
554 DoSetThumb(m_thumbPos + nPages*m_pageSize);
555 return TRUE;
556 }
557
558 // ============================================================================
559 // scroll bar input handler
560 // ============================================================================
561
562 // ----------------------------------------------------------------------------
563 // wxScrollBarTimer
564 // ----------------------------------------------------------------------------
565
566 wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
567 const wxControlAction& action,
568 wxScrollBar *control)
569 {
570 m_handler = handler;
571 m_action = action;
572 m_control = control;
573 }
574
575 bool wxScrollBarTimer::DoNotify()
576 {
577 return m_handler->OnScrollTimer(m_control, m_action);
578 }
579
580 // ----------------------------------------------------------------------------
581 // wxStdScrollBarInputHandler
582 // ----------------------------------------------------------------------------
583
584 wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
585 wxInputHandler *handler)
586 : wxStdInputHandler(handler)
587 {
588 m_renderer = renderer;
589 m_winCapture = NULL;
590 m_htLast = wxHT_NOWHERE;
591 m_timerScroll = NULL;
592 }
593
594 wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
595 {
596 // normally, it's NULL by now but just in case the user somehow managed to
597 // keep the mouse captured until now...
598 delete m_timerScroll;
599 }
600
601 void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
602 int flag,
603 bool doIt)
604 {
605 if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
606 {
607 wxScrollBar::Element
608 elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);
609
610 int flags = control->GetState(elem);
611 if ( doIt )
612 flags |= flag;
613 else
614 flags &= ~flag;
615 control->SetState(elem, flags);
616 }
617 }
618
619 bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
620 const wxControlAction& action)
621 {
622 int oldThumbPos = scrollbar->GetThumbPosition();
623 scrollbar->PerformAction(action);
624 if ( scrollbar->GetThumbPosition() != oldThumbPos )
625 return TRUE;
626
627 // we scrolled till the end
628 m_timerScroll->Stop();
629
630 return FALSE;
631 }
632
633 void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
634 {
635 // return everything to the normal state
636 if ( m_winCapture )
637 {
638 m_winCapture->ReleaseMouse();
639 m_winCapture = NULL;
640 }
641
642 m_btnCapture = -1;
643
644 if ( m_timerScroll )
645 {
646 delete m_timerScroll;
647 m_timerScroll = NULL;
648 }
649
650 // unpress the arrow and highlight the current element
651 Press(control, FALSE);
652 }
653
654 wxCoord
655 wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
656 const wxMouseEvent& event) const
657 {
658 wxPoint pt = event.GetPosition();
659 return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
660 }
661
662 void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
663 const wxMouseEvent& event)
664 {
665 int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
666 thumbPos = m_renderer->PixelToScrollbar(scrollbar, thumbPos);
667 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
668 }
669
670 bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
671 const wxKeyEvent& event,
672 bool pressed)
673 {
674 // we only react to the key presses here
675 if ( pressed )
676 {
677 wxControlAction action;
678 switch ( event.GetKeyCode() )
679 {
680 case WXK_DOWN:
681 case WXK_RIGHT: action = wxACTION_SCROLL_LINE_DOWN; break;
682 case WXK_UP:
683 case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break;
684 case WXK_HOME: action = wxACTION_SCROLL_START; break;
685 case WXK_END: action = wxACTION_SCROLL_END; break;
686 case WXK_PAGEUP:
687 case WXK_PRIOR: action = wxACTION_SCROLL_PAGE_UP; break;
688 case WXK_PAGEDOWN:
689 case WXK_NEXT: action = wxACTION_SCROLL_PAGE_DOWN; break;
690 }
691
692 if ( !!action )
693 {
694 consumer->PerformAction(action);
695
696 return TRUE;
697 }
698 }
699
700 return wxStdInputHandler::HandleKey(consumer, event, pressed);
701 }
702
703 bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
704 const wxMouseEvent& event)
705 {
706 // is this a click event from an acceptable button?
707 int btn = event.GetButton();
708 if ( (btn != -1) && IsAllowedButton(btn) )
709 {
710 // determine which part of the window mouse is in
711 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
712 wxHitTest ht = m_renderer->HitTestScrollbar
713 (
714 scrollbar,
715 event.GetPosition()
716 );
717
718 // when the mouse is pressed on any scrollbar element, we capture it
719 // and hold capture until the same mouse button is released
720 if ( event.ButtonDown() || event.ButtonDClick() )
721 {
722 if ( !m_winCapture )
723 {
724 m_btnCapture = btn;
725 m_winCapture = consumer->GetInputWindow();
726 m_winCapture->CaptureMouse();
727
728 // generate the command
729 bool hasAction = TRUE;
730 wxControlAction action;
731 switch ( ht )
732 {
733 case wxHT_SCROLLBAR_ARROW_LINE_1:
734 action = wxACTION_SCROLL_LINE_UP;
735 break;
736
737 case wxHT_SCROLLBAR_ARROW_LINE_2:
738 action = wxACTION_SCROLL_LINE_DOWN;
739 break;
740
741 case wxHT_SCROLLBAR_BAR_1:
742 action = wxACTION_SCROLL_PAGE_UP;
743 m_ptStartScrolling = event.GetPosition();
744 break;
745
746 case wxHT_SCROLLBAR_BAR_2:
747 action = wxACTION_SCROLL_PAGE_DOWN;
748 m_ptStartScrolling = event.GetPosition();
749 break;
750
751 case wxHT_SCROLLBAR_THUMB:
752 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
753 m_ofsMouse = GetMouseCoord(scrollbar, event) -
754 m_renderer->ScrollbarToPixel(scrollbar);
755
756 // fall through: there is no immediate action
757
758 default:
759 hasAction = FALSE;
760 }
761
762 // remove highlighting
763 Highlight(scrollbar, FALSE);
764 m_htLast = ht;
765
766 // and press the arrow or highlight thumb now instead
767 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
768 Highlight(scrollbar, TRUE);
769 else
770 Press(scrollbar, TRUE);
771
772 // start dragging
773 if ( hasAction )
774 {
775 m_timerScroll = new wxScrollBarTimer(this, action,
776 scrollbar);
777 m_timerScroll->StartAutoScroll();
778 }
779 //else: no (immediate) action
780
781 }
782 //else: mouse already captured, nothing to do
783 }
784 // release mouse if the *same* button went up
785 else if ( btn == m_btnCapture )
786 {
787 if ( m_winCapture )
788 {
789 StopScrolling(scrollbar);
790
791 // if we were dragging the thumb, send the last event
792 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
793 {
794 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
795 }
796
797 m_htLast = ht;
798 Highlight(scrollbar, TRUE);
799 }
800 else
801 {
802 // this is not supposed to happen as the button can't go up
803 // without going down previously and then we'd have
804 // m_winCapture by now
805 wxFAIL_MSG( _T("logic error in mouse capturing code") );
806 }
807 }
808 }
809
810 return wxStdInputHandler::HandleMouse(consumer, event);
811 }
812
813 bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
814 const wxMouseEvent& event)
815 {
816 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
817
818 if ( m_winCapture )
819 {
820 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Moving() )
821 {
822 // make the thumb follow the mouse by keeping the same offset
823 // between the mouse position and the top/left of the thumb
824 HandleThumbMove(scrollbar, event);
825
826 return TRUE;
827 }
828
829 // no other changes are possible while the mouse is captured
830 return FALSE;
831 }
832
833 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);
834
835 if ( event.Moving() )
836 {
837 wxHitTest ht = m_renderer->HitTestScrollbar
838 (
839 scrollbar,
840 event.GetPosition()
841 );
842 if ( ht == m_htLast )
843 {
844 // nothing changed
845 return FALSE;
846 }
847
848 #ifdef DEBUG_MOUSE
849 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
850 #endif // DEBUG_MOUSE
851
852 Highlight(scrollbar, FALSE);
853 m_htLast = ht;
854
855 if ( !isArrow )
856 Highlight(scrollbar, TRUE);
857 //else: already done by wxScrollArrows::HandleMouseMove
858 }
859 else if ( event.Leaving() )
860 {
861 if ( !isArrow )
862 Highlight(scrollbar, FALSE);
863
864 m_htLast = wxHT_NOWHERE;
865 }
866 else // event.Entering()
867 {
868 // we don't process this event
869 return FALSE;
870 }
871
872 // we did something
873 return TRUE;
874 }
875
876 #endif // wxUSE_SCROLLBAR
877
878 // ----------------------------------------------------------------------------
879 // wxScrollTimer
880 // ----------------------------------------------------------------------------
881
882 wxScrollTimer::wxScrollTimer()
883 {
884 m_skipNext = FALSE;
885 }
886
887 void wxScrollTimer::StartAutoScroll()
888 {
889 // start scrolling immediately
890 if ( !DoNotify() )
891 {
892 // ... and end it too
893 return;
894 }
895
896 // there is an initial delay before the scrollbar starts scrolling -
897 // implement it by ignoring the first timer expiration and only start
898 // scrolling from the second one
899 m_skipNext = TRUE;
900 Start(200); // FIXME: hardcoded delay
901 }
902
903 void wxScrollTimer::Notify()
904 {
905 if ( m_skipNext )
906 {
907 // scroll normally now - reduce the delay
908 Stop();
909 Start(50); // FIXME: hardcoded delay
910
911 m_skipNext = FALSE;
912 }
913 else
914 {
915 // if DoNotify() returns false, we're already deleted by the timer
916 // event handler, so don't do anything else here
917 (void)DoNotify();
918 }
919 }
920