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