corrected event type for standalone scrollbars (patch 1240385)
[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 everything 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 wxScrollEvent event(scrollType, this->GetId(), m_thumbPos,
558 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
559 event.SetEventObject(this);
560 GetParent()->GetEventHandler()->ProcessEvent(event);
561 }
562 else // part of the window
563 {
564 wxScrollWinEvent event(scrollType, m_thumbPos,
565 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
566 event.SetEventObject(this);
567 GetParent()->GetEventHandler()->ProcessEvent(event);
568 }
569 }
570
571 return true;
572 }
573
574 void wxScrollBar::ScrollToStart()
575 {
576 DoSetThumb(0);
577 }
578
579 void wxScrollBar::ScrollToEnd()
580 {
581 DoSetThumb(m_range - m_thumbSize);
582 }
583
584 bool wxScrollBar::ScrollLines(int nLines)
585 {
586 DoSetThumb(m_thumbPos + nLines);
587 return true;
588 }
589
590 bool wxScrollBar::ScrollPages(int nPages)
591 {
592 DoSetThumb(m_thumbPos + nPages*m_pageSize);
593 return true;
594 }
595
596 // ============================================================================
597 // scroll bar input handler
598 // ============================================================================
599
600 // ----------------------------------------------------------------------------
601 // wxScrollBarTimer
602 // ----------------------------------------------------------------------------
603
604 wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
605 const wxControlAction& action,
606 wxScrollBar *control)
607 {
608 m_handler = handler;
609 m_action = action;
610 m_control = control;
611 }
612
613 bool wxScrollBarTimer::DoNotify()
614 {
615 return m_handler->OnScrollTimer(m_control, m_action);
616 }
617
618 // ----------------------------------------------------------------------------
619 // wxStdScrollBarInputHandler
620 // ----------------------------------------------------------------------------
621
622 wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
623 wxInputHandler *handler)
624 : wxStdInputHandler(handler)
625 {
626 m_renderer = renderer;
627 m_winCapture = NULL;
628 m_htLast = wxHT_NOWHERE;
629 m_timerScroll = NULL;
630 }
631
632 wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
633 {
634 // normally, it's NULL by now but just in case the user somehow managed to
635 // keep the mouse captured until now...
636 delete m_timerScroll;
637 }
638
639 void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
640 int flag,
641 bool doIt)
642 {
643 if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
644 {
645 wxScrollBar::Element
646 elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);
647
648 int flags = control->GetState(elem);
649 if ( doIt )
650 flags |= flag;
651 else
652 flags &= ~flag;
653 control->SetState(elem, flags);
654 }
655 }
656
657 bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
658 const wxControlAction& action)
659 {
660 int oldThumbPos = scrollbar->GetThumbPosition();
661 scrollbar->PerformAction(action);
662 if ( scrollbar->GetThumbPosition() != oldThumbPos )
663 return true;
664
665 // we scrolled till the end
666 m_timerScroll->Stop();
667
668 return false;
669 }
670
671 void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
672 {
673 // return everything to the normal state
674 if ( m_winCapture )
675 {
676 m_winCapture->ReleaseMouse();
677 m_winCapture = NULL;
678 }
679
680 m_btnCapture = -1;
681
682 if ( m_timerScroll )
683 {
684 delete m_timerScroll;
685 m_timerScroll = NULL;
686 }
687
688 // unpress the arrow and highlight the current element
689 Press(control, false);
690 }
691
692 wxCoord
693 wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
694 const wxMouseEvent& event) const
695 {
696 wxPoint pt = event.GetPosition();
697 return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
698 }
699
700 void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
701 const wxMouseEvent& event)
702 {
703 int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
704 thumbPos = m_renderer->PixelToScrollbar(scrollbar, thumbPos);
705 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
706 }
707
708 bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
709 const wxKeyEvent& event,
710 bool pressed)
711 {
712 // we only react to the key presses here
713 if ( pressed )
714 {
715 wxControlAction action;
716 switch ( event.GetKeyCode() )
717 {
718 case WXK_DOWN:
719 case WXK_RIGHT: action = wxACTION_SCROLL_LINE_DOWN; break;
720 case WXK_UP:
721 case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break;
722 case WXK_HOME: action = wxACTION_SCROLL_START; break;
723 case WXK_END: action = wxACTION_SCROLL_END; break;
724 case WXK_PAGEUP:
725 case WXK_PRIOR: action = wxACTION_SCROLL_PAGE_UP; break;
726 case WXK_PAGEDOWN:
727 case WXK_NEXT: action = wxACTION_SCROLL_PAGE_DOWN; break;
728 }
729
730 if ( !action.IsEmpty() )
731 {
732 consumer->PerformAction(action);
733
734 return true;
735 }
736 }
737
738 return wxStdInputHandler::HandleKey(consumer, event, pressed);
739 }
740
741 bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
742 const wxMouseEvent& event)
743 {
744 // is this a click event from an acceptable button?
745 int btn = event.GetButton();
746 if ( (btn != -1) && IsAllowedButton(btn) )
747 {
748 // determine which part of the window mouse is in
749 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
750 wxHitTest ht = m_renderer->HitTestScrollbar
751 (
752 scrollbar,
753 event.GetPosition()
754 );
755
756 // when the mouse is pressed on any scrollbar element, we capture it
757 // and hold capture until the same mouse button is released
758 if ( event.ButtonDown() || event.ButtonDClick() )
759 {
760 if ( !m_winCapture )
761 {
762 m_btnCapture = btn;
763 m_winCapture = consumer->GetInputWindow();
764 m_winCapture->CaptureMouse();
765
766 // generate the command
767 bool hasAction = true;
768 wxControlAction action;
769 switch ( ht )
770 {
771 case wxHT_SCROLLBAR_ARROW_LINE_1:
772 action = wxACTION_SCROLL_LINE_UP;
773 break;
774
775 case wxHT_SCROLLBAR_ARROW_LINE_2:
776 action = wxACTION_SCROLL_LINE_DOWN;
777 break;
778
779 case wxHT_SCROLLBAR_BAR_1:
780 action = wxACTION_SCROLL_PAGE_UP;
781 m_ptStartScrolling = event.GetPosition();
782 break;
783
784 case wxHT_SCROLLBAR_BAR_2:
785 action = wxACTION_SCROLL_PAGE_DOWN;
786 m_ptStartScrolling = event.GetPosition();
787 break;
788
789 case wxHT_SCROLLBAR_THUMB:
790 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
791 m_ofsMouse = GetMouseCoord(scrollbar, event) -
792 m_renderer->ScrollbarToPixel(scrollbar);
793
794 // fall through: there is no immediate action
795
796 default:
797 hasAction = false;
798 }
799
800 // remove highlighting
801 Highlight(scrollbar, false);
802 m_htLast = ht;
803
804 // and press the arrow or highlight thumb now instead
805 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
806 Highlight(scrollbar, true);
807 else
808 Press(scrollbar, true);
809
810 // start dragging
811 if ( hasAction )
812 {
813 m_timerScroll = new wxScrollBarTimer(this, action,
814 scrollbar);
815 m_timerScroll->StartAutoScroll();
816 }
817 //else: no (immediate) action
818
819 }
820 //else: mouse already captured, nothing to do
821 }
822 // release mouse if the *same* button went up
823 else if ( btn == m_btnCapture )
824 {
825 if ( m_winCapture )
826 {
827 StopScrolling(scrollbar);
828
829 // if we were dragging the thumb, send the last event
830 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
831 {
832 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
833 }
834
835 m_htLast = ht;
836 Highlight(scrollbar, true);
837 }
838 else
839 {
840 // this is not supposed to happen as the button can't go up
841 // without going down previously and then we'd have
842 // m_winCapture by now
843 wxFAIL_MSG( _T("logic error in mouse capturing code") );
844 }
845 }
846 }
847
848 return wxStdInputHandler::HandleMouse(consumer, event);
849 }
850
851 bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
852 const wxMouseEvent& event)
853 {
854 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
855
856 if ( m_winCapture )
857 {
858 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Dragging() )
859 {
860 // make the thumb follow the mouse by keeping the same offset
861 // between the mouse position and the top/left of the thumb
862 HandleThumbMove(scrollbar, event);
863
864 return true;
865 }
866
867 // no other changes are possible while the mouse is captured
868 return false;
869 }
870
871 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);
872
873 if ( event.Dragging() )
874 {
875 wxHitTest ht = m_renderer->HitTestScrollbar
876 (
877 scrollbar,
878 event.GetPosition()
879 );
880 if ( ht == m_htLast )
881 {
882 // nothing changed
883 return false;
884 }
885
886 #ifdef DEBUG_MOUSE
887 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
888 #endif // DEBUG_MOUSE
889
890 Highlight(scrollbar, false);
891 m_htLast = ht;
892
893 if ( !isArrow )
894 Highlight(scrollbar, true);
895 //else: already done by wxScrollArrows::HandleMouseMove
896 }
897 else if ( event.Leaving() )
898 {
899 if ( !isArrow )
900 Highlight(scrollbar, false);
901
902 m_htLast = wxHT_NOWHERE;
903 }
904 else // event.Entering()
905 {
906 // we don't process this event
907 return false;
908 }
909
910 // we did something
911 return true;
912 }
913
914 #endif // wxUSE_SCROLLBAR
915
916 // ----------------------------------------------------------------------------
917 // wxScrollTimer
918 // ----------------------------------------------------------------------------
919
920 wxScrollTimer::wxScrollTimer()
921 {
922 m_skipNext = false;
923 }
924
925 void wxScrollTimer::StartAutoScroll()
926 {
927 // start scrolling immediately
928 if ( !DoNotify() )
929 {
930 // ... and end it too
931 return;
932 }
933
934 // there is an initial delay before the scrollbar starts scrolling -
935 // implement it by ignoring the first timer expiration and only start
936 // scrolling from the second one
937 m_skipNext = true;
938 Start(200); // FIXME: hardcoded delay
939 }
940
941 void wxScrollTimer::Notify()
942 {
943 if ( m_skipNext )
944 {
945 // scroll normally now - reduce the delay
946 Stop();
947 Start(50); // FIXME: hardcoded delay
948
949 m_skipNext = false;
950 }
951 else
952 {
953 // if DoNotify() returns false, we're already deleted by the timer
954 // event handler, so don't do anything else here
955 (void)DoNotify();
956 }
957 }
958