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