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