]> git.saurik.com Git - wxWidgets.git/blob - src/univ/scrolbar.cpp
wxCocoa: Added preliminary Blit support
[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 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 // 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 }
328
329 void wxScrollBar::UpdateThumb()
330 {
331 if ( m_dirty )
332 {
333 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
334 {
335 if ( m_elementsState[n] & wxCONTROL_DIRTY )
336 {
337 wxRect rect = GetRenderer()->GetScrollbarRect(this, (Element)n);
338
339 if ( rect.width && rect.height )
340 {
341 // we try to avoid redrawing the entire shaft (which might
342 // be quite long) if possible by only redrawing the area
343 // wich really changed
344 if ( (n == Element_Bar_1 || n == Element_Bar_2) &&
345 (m_thumbPosOld != -1) )
346 {
347 // the less efficient but more reliable (i.e. this will
348 // probably work everywhere) version: refresh the
349 // distance covered by thumb since the last update
350 #if 0
351 wxRect rectOld =
352 GetRenderer()->GetScrollbarRect(this,
353 (Element)n,
354 m_thumbPosOld);
355 if ( IsVertical() )
356 {
357 if ( n == Element_Bar_1 )
358 rect.SetTop(rectOld.GetBottom());
359 else
360 rect.SetBottom(rectOld.GetBottom());
361 }
362 else // horizontal
363 {
364 if ( n == Element_Bar_1 )
365 rect.SetLeft(rectOld.GetRight());
366 else
367 rect.SetRight(rectOld.GetRight());
368 }
369 #else // efficient version: only repaint the area occupied by
370 // the thumb previously - we can't do better than this
371 rect = GetRenderer()->GetScrollbarRect(this,
372 Element_Thumb,
373 m_thumbPosOld);
374 #endif // 0/1
375 }
376
377 #ifdef WXDEBUG_SCROLLBAR
378 static bool s_refreshDebug = FALSE;
379 if ( s_refreshDebug )
380 {
381 wxClientDC dc(this);
382 dc.SetBrush(*wxCYAN_BRUSH);
383 dc.SetPen(*wxTRANSPARENT_PEN);
384 dc.DrawRectangle(rect);
385
386 // under Unix we use "--sync" X option for this
387 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
388 ::GdiFlush();
389 ::Sleep(200);
390 #endif // __WXMSW__
391 }
392 #endif // WXDEBUG_SCROLLBAR
393
394 Refresh(FALSE, &rect);
395 }
396
397 m_elementsState[n] &= ~wxCONTROL_DIRTY;
398 }
399 }
400
401 m_dirty = FALSE;
402 }
403 }
404
405 void wxScrollBar::DoDraw(wxControlRenderer *renderer)
406 {
407 renderer->DrawScrollbar(this, m_thumbPosOld);
408
409 // clear all dirty flags
410 m_dirty = FALSE;
411 m_thumbPosOld = -1;
412 }
413
414 // ----------------------------------------------------------------------------
415 // state flags
416 // ----------------------------------------------------------------------------
417
418 static inline wxScrollBar::Element ElementForArrow(wxScrollArrows::Arrow arrow)
419 {
420 return arrow == wxScrollArrows::Arrow_First
421 ? wxScrollBar::Element_Arrow_Line_1
422 : wxScrollBar::Element_Arrow_Line_2;
423 }
424
425 int wxScrollBar::GetArrowState(wxScrollArrows::Arrow arrow) const
426 {
427 return GetState(ElementForArrow(arrow));
428 }
429
430 void wxScrollBar::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
431 {
432 Element which = ElementForArrow(arrow);
433 int state = GetState(which);
434 if ( set )
435 state |= flag;
436 else
437 state &= ~flag;
438
439 SetState(which, state);
440 }
441
442 int wxScrollBar::GetState(Element which) const
443 {
444 // if the entire scrollbar is disabled, all of its elements are too
445 int flags = m_elementsState[which];
446 if ( !IsEnabled() )
447 flags |= wxCONTROL_DISABLED;
448
449 return flags;
450 }
451
452 void wxScrollBar::SetState(Element which, int flags)
453 {
454 if ( (int)(m_elementsState[which] & ~wxCONTROL_DIRTY) != flags )
455 {
456 m_elementsState[which] = flags | wxCONTROL_DIRTY;
457
458 m_dirty = TRUE;
459 }
460 }
461
462 // ----------------------------------------------------------------------------
463 // input processing
464 // ----------------------------------------------------------------------------
465
466 bool wxScrollBar::OnArrow(wxScrollArrows::Arrow arrow)
467 {
468 int oldThumbPos = GetThumbPosition();
469 PerformAction(arrow == wxScrollArrows::Arrow_First
470 ? wxACTION_SCROLL_LINE_UP
471 : wxACTION_SCROLL_LINE_DOWN);
472
473 // did we scroll till the end?
474 return GetThumbPosition() != oldThumbPos;
475 }
476
477 bool wxScrollBar::PerformAction(const wxControlAction& action,
478 long numArg,
479 const wxString& strArg)
480 {
481 int thumbOld = m_thumbPos;
482
483 bool notify = FALSE; // send an event about the change?
484
485 wxEventType scrollType;
486
487 // test for thumb move first as these events happen in quick succession
488 if ( action == wxACTION_SCROLL_THUMB_MOVE )
489 {
490 DoSetThumb(numArg);
491
492 // VS: we have to force redraw here, otherwise the thumb will lack
493 // behind mouse cursor
494 UpdateThumb();
495
496 scrollType = wxEVT_SCROLLWIN_THUMBTRACK;
497 }
498 else if ( action == wxACTION_SCROLL_LINE_UP )
499 {
500 scrollType = wxEVT_SCROLLWIN_LINEUP;
501 ScrollLines(-1);
502 }
503 else if ( action == wxACTION_SCROLL_LINE_DOWN )
504 {
505 scrollType = wxEVT_SCROLLWIN_LINEDOWN;
506 ScrollLines(1);
507 }
508 else if ( action == wxACTION_SCROLL_PAGE_UP )
509 {
510 scrollType = wxEVT_SCROLLWIN_PAGEUP;
511 ScrollPages(-1);
512 }
513 else if ( action == wxACTION_SCROLL_PAGE_DOWN )
514 {
515 scrollType = wxEVT_SCROLLWIN_PAGEDOWN;
516 ScrollPages(1);
517 }
518 else if ( action == wxACTION_SCROLL_START )
519 {
520 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
521 ScrollToStart();
522 }
523 else if ( action == wxACTION_SCROLL_END )
524 {
525 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
526 ScrollToEnd();
527 }
528 else if ( action == wxACTION_SCROLL_THUMB_DRAG )
529 {
530 // we won't use it but this line suppresses the compiler
531 // warning about "variable may be used without having been
532 // initialized"
533 scrollType = wxEVT_NULL;
534 }
535 else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
536 {
537 // always notify about this
538 notify = TRUE;
539 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
540 }
541 else
542 return wxControl::PerformAction(action, numArg, strArg);
543
544 // has scrollbar position changed?
545 bool changed = m_thumbPos != thumbOld;
546 if ( notify || changed )
547 {
548 if ( IsStandalone() )
549 {
550 // we should generate EVT_SCROLL events for the standalone
551 // scrollbars and not the EVT_SCROLLWIN ones
552 //
553 // NB: we assume that scrollbar events are sequentially numbered
554 // but this should be ok as other code relies on this as well
555 scrollType += wxEVT_SCROLL_TOP - wxEVT_SCROLLWIN_TOP;
556 }
557
558 wxScrollWinEvent event(scrollType, m_thumbPos,
559 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
560 event.SetEventObject(this);
561 GetParent()->GetEventHandler()->ProcessEvent(event);
562 }
563
564 return TRUE;
565 }
566
567 void wxScrollBar::ScrollToStart()
568 {
569 DoSetThumb(0);
570 }
571
572 void wxScrollBar::ScrollToEnd()
573 {
574 DoSetThumb(m_range - m_thumbSize);
575 }
576
577 bool wxScrollBar::ScrollLines(int nLines)
578 {
579 DoSetThumb(m_thumbPos + nLines);
580 return TRUE;
581 }
582
583 bool wxScrollBar::ScrollPages(int nPages)
584 {
585 DoSetThumb(m_thumbPos + nPages*m_pageSize);
586 return TRUE;
587 }
588
589 // ============================================================================
590 // scroll bar input handler
591 // ============================================================================
592
593 // ----------------------------------------------------------------------------
594 // wxScrollBarTimer
595 // ----------------------------------------------------------------------------
596
597 wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
598 const wxControlAction& action,
599 wxScrollBar *control)
600 {
601 m_handler = handler;
602 m_action = action;
603 m_control = control;
604 }
605
606 bool wxScrollBarTimer::DoNotify()
607 {
608 return m_handler->OnScrollTimer(m_control, m_action);
609 }
610
611 // ----------------------------------------------------------------------------
612 // wxStdScrollBarInputHandler
613 // ----------------------------------------------------------------------------
614
615 wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
616 wxInputHandler *handler)
617 : wxStdInputHandler(handler)
618 {
619 m_renderer = renderer;
620 m_winCapture = NULL;
621 m_htLast = wxHT_NOWHERE;
622 m_timerScroll = NULL;
623 }
624
625 wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
626 {
627 // normally, it's NULL by now but just in case the user somehow managed to
628 // keep the mouse captured until now...
629 delete m_timerScroll;
630 }
631
632 void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
633 int flag,
634 bool doIt)
635 {
636 if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
637 {
638 wxScrollBar::Element
639 elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);
640
641 int flags = control->GetState(elem);
642 if ( doIt )
643 flags |= flag;
644 else
645 flags &= ~flag;
646 control->SetState(elem, flags);
647 }
648 }
649
650 bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
651 const wxControlAction& action)
652 {
653 int oldThumbPos = scrollbar->GetThumbPosition();
654 scrollbar->PerformAction(action);
655 if ( scrollbar->GetThumbPosition() != oldThumbPos )
656 return TRUE;
657
658 // we scrolled till the end
659 m_timerScroll->Stop();
660
661 return FALSE;
662 }
663
664 void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
665 {
666 // return everything to the normal state
667 if ( m_winCapture )
668 {
669 m_winCapture->ReleaseMouse();
670 m_winCapture = NULL;
671 }
672
673 m_btnCapture = -1;
674
675 if ( m_timerScroll )
676 {
677 delete m_timerScroll;
678 m_timerScroll = NULL;
679 }
680
681 // unpress the arrow and highlight the current element
682 Press(control, FALSE);
683 }
684
685 wxCoord
686 wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
687 const wxMouseEvent& event) const
688 {
689 wxPoint pt = event.GetPosition();
690 return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
691 }
692
693 void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
694 const wxMouseEvent& event)
695 {
696 int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
697 thumbPos = m_renderer->PixelToScrollbar(scrollbar, thumbPos);
698 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
699 }
700
701 bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
702 const wxKeyEvent& event,
703 bool pressed)
704 {
705 // we only react to the key presses here
706 if ( pressed )
707 {
708 wxControlAction action;
709 switch ( event.GetKeyCode() )
710 {
711 case WXK_DOWN:
712 case WXK_RIGHT: action = wxACTION_SCROLL_LINE_DOWN; break;
713 case WXK_UP:
714 case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break;
715 case WXK_HOME: action = wxACTION_SCROLL_START; break;
716 case WXK_END: action = wxACTION_SCROLL_END; break;
717 case WXK_PAGEUP:
718 case WXK_PRIOR: action = wxACTION_SCROLL_PAGE_UP; break;
719 case WXK_PAGEDOWN:
720 case WXK_NEXT: action = wxACTION_SCROLL_PAGE_DOWN; break;
721 }
722
723 if ( !!action )
724 {
725 consumer->PerformAction(action);
726
727 return TRUE;
728 }
729 }
730
731 return wxStdInputHandler::HandleKey(consumer, event, pressed);
732 }
733
734 bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
735 const wxMouseEvent& event)
736 {
737 // is this a click event from an acceptable button?
738 int btn = event.GetButton();
739 if ( (btn != -1) && IsAllowedButton(btn) )
740 {
741 // determine which part of the window mouse is in
742 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
743 wxHitTest ht = m_renderer->HitTestScrollbar
744 (
745 scrollbar,
746 event.GetPosition()
747 );
748
749 // when the mouse is pressed on any scrollbar element, we capture it
750 // and hold capture until the same mouse button is released
751 if ( event.ButtonDown() || event.ButtonDClick() )
752 {
753 if ( !m_winCapture )
754 {
755 m_btnCapture = btn;
756 m_winCapture = consumer->GetInputWindow();
757 m_winCapture->CaptureMouse();
758
759 // generate the command
760 bool hasAction = TRUE;
761 wxControlAction action;
762 switch ( ht )
763 {
764 case wxHT_SCROLLBAR_ARROW_LINE_1:
765 action = wxACTION_SCROLL_LINE_UP;
766 break;
767
768 case wxHT_SCROLLBAR_ARROW_LINE_2:
769 action = wxACTION_SCROLL_LINE_DOWN;
770 break;
771
772 case wxHT_SCROLLBAR_BAR_1:
773 action = wxACTION_SCROLL_PAGE_UP;
774 m_ptStartScrolling = event.GetPosition();
775 break;
776
777 case wxHT_SCROLLBAR_BAR_2:
778 action = wxACTION_SCROLL_PAGE_DOWN;
779 m_ptStartScrolling = event.GetPosition();
780 break;
781
782 case wxHT_SCROLLBAR_THUMB:
783 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
784 m_ofsMouse = GetMouseCoord(scrollbar, event) -
785 m_renderer->ScrollbarToPixel(scrollbar);
786
787 // fall through: there is no immediate action
788
789 default:
790 hasAction = FALSE;
791 }
792
793 // remove highlighting
794 Highlight(scrollbar, FALSE);
795 m_htLast = ht;
796
797 // and press the arrow or highlight thumb now instead
798 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
799 Highlight(scrollbar, TRUE);
800 else
801 Press(scrollbar, TRUE);
802
803 // start dragging
804 if ( hasAction )
805 {
806 m_timerScroll = new wxScrollBarTimer(this, action,
807 scrollbar);
808 m_timerScroll->StartAutoScroll();
809 }
810 //else: no (immediate) action
811
812 }
813 //else: mouse already captured, nothing to do
814 }
815 // release mouse if the *same* button went up
816 else if ( btn == m_btnCapture )
817 {
818 if ( m_winCapture )
819 {
820 StopScrolling(scrollbar);
821
822 // if we were dragging the thumb, send the last event
823 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
824 {
825 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
826 }
827
828 m_htLast = ht;
829 Highlight(scrollbar, TRUE);
830 }
831 else
832 {
833 // this is not supposed to happen as the button can't go up
834 // without going down previously and then we'd have
835 // m_winCapture by now
836 wxFAIL_MSG( _T("logic error in mouse capturing code") );
837 }
838 }
839 }
840
841 return wxStdInputHandler::HandleMouse(consumer, event);
842 }
843
844 bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
845 const wxMouseEvent& event)
846 {
847 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
848
849 if ( m_winCapture )
850 {
851 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Moving() )
852 {
853 // make the thumb follow the mouse by keeping the same offset
854 // between the mouse position and the top/left of the thumb
855 HandleThumbMove(scrollbar, event);
856
857 return TRUE;
858 }
859
860 // no other changes are possible while the mouse is captured
861 return FALSE;
862 }
863
864 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);
865
866 if ( event.Moving() )
867 {
868 wxHitTest ht = m_renderer->HitTestScrollbar
869 (
870 scrollbar,
871 event.GetPosition()
872 );
873 if ( ht == m_htLast )
874 {
875 // nothing changed
876 return FALSE;
877 }
878
879 #ifdef DEBUG_MOUSE
880 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
881 #endif // DEBUG_MOUSE
882
883 Highlight(scrollbar, FALSE);
884 m_htLast = ht;
885
886 if ( !isArrow )
887 Highlight(scrollbar, TRUE);
888 //else: already done by wxScrollArrows::HandleMouseMove
889 }
890 else if ( event.Leaving() )
891 {
892 if ( !isArrow )
893 Highlight(scrollbar, FALSE);
894
895 m_htLast = wxHT_NOWHERE;
896 }
897 else // event.Entering()
898 {
899 // we don't process this event
900 return FALSE;
901 }
902
903 // we did something
904 return TRUE;
905 }
906
907 #endif // wxUSE_SCROLLBAR
908
909 // ----------------------------------------------------------------------------
910 // wxScrollTimer
911 // ----------------------------------------------------------------------------
912
913 wxScrollTimer::wxScrollTimer()
914 {
915 m_skipNext = FALSE;
916 }
917
918 void wxScrollTimer::StartAutoScroll()
919 {
920 // start scrolling immediately
921 if ( !DoNotify() )
922 {
923 // ... and end it too
924 return;
925 }
926
927 // there is an initial delay before the scrollbar starts scrolling -
928 // implement it by ignoring the first timer expiration and only start
929 // scrolling from the second one
930 m_skipNext = TRUE;
931 Start(200); // FIXME: hardcoded delay
932 }
933
934 void wxScrollTimer::Notify()
935 {
936 if ( m_skipNext )
937 {
938 // scroll normally now - reduce the delay
939 Stop();
940 Start(50); // FIXME: hardcoded delay
941
942 m_skipNext = FALSE;
943 }
944 else
945 {
946 // if DoNotify() returns false, we're already deleted by the timer
947 // event handler, so don't do anything else here
948 (void)DoNotify();
949 }
950 }
951