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