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