]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/univ/scrolbar.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / univ / scrolbar.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/univ/scrolbar.cpp
3// Purpose: wxScrollBar implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 20.08.00
7// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#if wxUSE_SCROLLBAR
26
27#include "wx/scrolbar.h"
28
29#ifndef WX_PRECOMP
30 #include "wx/timer.h"
31 #include "wx/dcclient.h"
32 #include "wx/validate.h"
33 #include "wx/log.h"
34#endif
35
36#include "wx/univ/scrtimer.h"
37
38#include "wx/univ/renderer.h"
39#include "wx/univ/inphand.h"
40#include "wx/univ/theme.h"
41
42#if wxDEBUG_LEVEL >= 2
43 #define WXDEBUG_SCROLLBAR
44#endif
45
46#if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
47#include "wx/msw/private.h"
48#endif
49
50// ----------------------------------------------------------------------------
51// wxScrollBarTimer: this class is used to repeatedly scroll the scrollbar
52// when the mouse is help pressed on the arrow or on the bar. It generates the
53// given scroll action command periodically.
54// ----------------------------------------------------------------------------
55
56class wxScrollBarTimer : public wxScrollTimer
57{
58public:
59 wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
60 const wxControlAction& action,
61 wxScrollBar *control);
62
63protected:
64 virtual bool DoNotify();
65
66private:
67 wxStdScrollBarInputHandler *m_handler;
68 wxControlAction m_action;
69 wxScrollBar *m_control;
70};
71
72// ============================================================================
73// implementation
74// ============================================================================
75
76BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase)
77END_EVENT_TABLE()
78
79// ----------------------------------------------------------------------------
80// creation
81// ----------------------------------------------------------------------------
82
83#ifdef __VISUALC__
84 // warning C4355: 'this' : used in base member initializer list
85 #pragma warning(disable:4355) // so what? disable it...
86#endif
87
88wxScrollBar::wxScrollBar()
89 : m_arrows(this)
90{
91 Init();
92}
93
94wxScrollBar::wxScrollBar(wxWindow *parent,
95 wxWindowID id,
96 const wxPoint& pos,
97 const wxSize& size,
98 long style,
99 const wxValidator& validator,
100 const wxString& name)
101 : m_arrows(this)
102{
103 Init();
104
105 (void)Create(parent, id, pos, size, style, validator, name);
106}
107
108#ifdef __VISUALC__
109 // warning C4355: 'this' : used in base member initializer list
110 #pragma warning(default:4355)
111#endif
112
113void wxScrollBar::Init()
114{
115 m_range =
116 m_thumbSize =
117 m_thumbPos =
118 m_pageSize = 0;
119
120 m_thumbPosOld = -1;
121
122 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
123 {
124 m_elementsState[n] = 0;
125 }
126
127 m_dirty = false;
128}
129
130bool wxScrollBar::Create(wxWindow *parent,
131 wxWindowID id,
132 const wxPoint &pos,
133 const wxSize &size,
134 long style,
135 const wxValidator& validator,
136 const wxString &name)
137{
138 // the scrollbars never have the border
139 style &= ~wxBORDER_MASK;
140
141 if ( !wxControl::Create(parent, id, pos, size, style, validator, name) )
142 return false;
143
144 SetInitialSize(size);
145
146 // override the cursor of the target window (if any)
147 SetCursor(wxCURSOR_ARROW);
148
149 CreateInputHandler(wxINP_HANDLER_SCROLLBAR);
150
151 return true;
152}
153
154wxScrollBar::~wxScrollBar()
155{
156}
157
158// ----------------------------------------------------------------------------
159// misc accessors
160// ----------------------------------------------------------------------------
161
162bool wxScrollBar::IsStandalone() const
163{
164 wxWindow *parent = GetParent();
165 if ( !parent )
166 {
167 return true;
168 }
169
170 return (parent->GetScrollbar(wxHORIZONTAL) != this) &&
171 (parent->GetScrollbar(wxVERTICAL) != this);
172}
173
174bool wxScrollBar::AcceptsFocus() const
175{
176 // the window scrollbars never accept focus
177 return wxScrollBarBase::AcceptsFocus() && IsStandalone();
178}
179
180// ----------------------------------------------------------------------------
181// scrollbar API
182// ----------------------------------------------------------------------------
183
184void wxScrollBar::DoSetThumb(int pos)
185{
186 // don't assert hecks here, we're a private function which is meant to be
187 // called with any args at all
188 if ( pos < 0 )
189 {
190 pos = 0;
191 }
192 else if ( pos > m_range - m_thumbSize )
193 {
194 pos = m_range - m_thumbSize;
195 }
196
197 if ( m_thumbPos == pos )
198 {
199 // nothing changed, avoid refreshes which would provoke flicker
200 return;
201 }
202
203 if ( m_thumbPosOld == -1 )
204 {
205 // remember the old thumb position
206 m_thumbPosOld = m_thumbPos;
207 }
208
209 m_thumbPos = pos;
210
211 // we have to refresh the part of the bar which was under the thumb and the
212 // thumb itself
213 m_elementsState[Element_Thumb] |= wxCONTROL_DIRTY;
214 m_elementsState[m_thumbPos > m_thumbPosOld
215 ? Element_Bar_1 : Element_Bar_2] |= wxCONTROL_DIRTY;
216 m_dirty = true;
217}
218
219int wxScrollBar::GetThumbPosition() const
220{
221 return m_thumbPos;
222}
223
224int wxScrollBar::GetThumbSize() const
225{
226 return m_thumbSize;
227}
228
229int wxScrollBar::GetPageSize() const
230{
231 return m_pageSize;
232}
233
234int wxScrollBar::GetRange() const
235{
236 return m_range;
237}
238
239void wxScrollBar::SetThumbPosition(int pos)
240{
241 wxCHECK_RET( pos >= 0 && pos <= m_range, wxT("thumb position out of range") );
242
243 DoSetThumb(pos);
244}
245
246void wxScrollBar::SetScrollbar(int position, int thumbSize,
247 int range, int pageSize,
248 bool refresh)
249{
250 // we only refresh everything when the range changes, thumb position
251 // changes are handled in OnIdle
252 bool needsRefresh = (range != m_range) ||
253 (thumbSize != m_thumbSize) ||
254 (pageSize != m_pageSize);
255
256 // set all parameters
257 m_range = range;
258 m_thumbSize = thumbSize;
259 SetThumbPosition(position);
260 m_pageSize = pageSize;
261
262 // ignore refresh parameter unless we really need to refresh everything -
263 // there ir a lot of existing code which just calls SetScrollbar() without
264 // specifying the last parameter even though it doesn't need at all to
265 // refresh the window immediately
266 if ( refresh && needsRefresh )
267 {
268 // and update the window
269 Refresh();
270 Update();
271 }
272}
273
274// ----------------------------------------------------------------------------
275// geometry
276// ----------------------------------------------------------------------------
277
278wxSize wxScrollBar::DoGetBestClientSize() const
279{
280 // this dimension is completely arbitrary
281 static const wxCoord SIZE = 140;
282
283 wxSize size = m_renderer->GetScrollbarArrowSize();
284 if ( IsVertical() )
285 {
286 size.y = SIZE;
287 }
288 else // horizontal
289 {
290 size.x = SIZE;
291 }
292
293 return size;
294}
295
296wxScrollArrows::Arrow wxScrollBar::HitTestArrow(const wxPoint& pt) const
297{
298 switch ( HitTestBar(pt) )
299 {
300 case wxHT_SCROLLBAR_ARROW_LINE_1:
301 return wxScrollArrows::Arrow_First;
302
303 case wxHT_SCROLLBAR_ARROW_LINE_2:
304 return wxScrollArrows::Arrow_Second;
305
306 default:
307 return wxScrollArrows::Arrow_None;
308 }
309}
310
311wxHitTest wxScrollBar::HitTestBar(const wxPoint& pt) const
312{
313 // we only need to work with either x or y coord depending on the
314 // orientation, choose one (but still check the other one to verify if the
315 // mouse is in the window at all)
316 const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize();
317
318 wxCoord coord, sizeArrow, sizeTotal;
319 wxSize size = GetSize();
320 if ( GetWindowStyle() & wxVERTICAL )
321 {
322 if ( pt.x < 0 || pt.x > size.x )
323 return wxHT_NOWHERE;
324
325 coord = pt.y;
326 sizeArrow = sizeArrowSB.y;
327 sizeTotal = size.y;
328 }
329 else // horizontal
330 {
331 if ( pt.y < 0 || pt.y > size.y )
332 return wxHT_NOWHERE;
333
334 coord = pt.x;
335 sizeArrow = sizeArrowSB.x;
336 sizeTotal = size.x;
337 }
338
339 // test for the arrows first as it's faster
340 if ( coord < 0 || coord > sizeTotal )
341 {
342 return wxHT_NOWHERE;
343 }
344 else if ( coord < sizeArrow )
345 {
346 return wxHT_SCROLLBAR_ARROW_LINE_1;
347 }
348 else if ( coord > sizeTotal - sizeArrow )
349 {
350 return wxHT_SCROLLBAR_ARROW_LINE_2;
351 }
352 else
353 {
354 // calculate the thumb position in pixels
355 sizeTotal -= 2*sizeArrow;
356 wxCoord thumbStart, thumbEnd;
357 int range = GetRange();
358 if ( !range )
359 {
360 // clicking the scrollbar without range has no effect
361 return wxHT_NOWHERE;
362 }
363 else
364 {
365 GetScrollBarThumbSize(sizeTotal,
366 GetThumbPosition(),
367 GetThumbSize(),
368 range,
369 &thumbStart,
370 &thumbEnd);
371 }
372
373 // now compare with the thumb position
374 coord -= sizeArrow;
375 if ( coord < thumbStart )
376 return wxHT_SCROLLBAR_BAR_1;
377 else if ( coord > thumbEnd )
378 return wxHT_SCROLLBAR_BAR_2;
379 else
380 return wxHT_SCROLLBAR_THUMB;
381 }
382}
383
384/* static */
385void wxScrollBar::GetScrollBarThumbSize(wxCoord length,
386 int thumbPos,
387 int thumbSize,
388 int range,
389 wxCoord *thumbStart,
390 wxCoord *thumbEnd)
391{
392 // the thumb can't be made less than this number of pixels
393 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
394
395 *thumbStart = (length*thumbPos) / range;
396 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
397
398 if ( *thumbEnd - *thumbStart < thumbMinWidth )
399 {
400 // adjust the end if possible
401 if ( *thumbStart <= length - thumbMinWidth )
402 {
403 // yes, just make it wider
404 *thumbEnd = *thumbStart + thumbMinWidth;
405 }
406 else // it is at the bottom of the scrollbar
407 {
408 // so move it a bit up
409 *thumbStart = length - thumbMinWidth;
410 *thumbEnd = length;
411 }
412 }
413}
414
415wxRect wxScrollBar::GetScrollbarRect(wxScrollBar::Element elem,
416 int thumbPos) const
417{
418 if ( thumbPos == -1 )
419 {
420 thumbPos = GetThumbPosition();
421 }
422
423 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
424
425 wxSize sizeTotal = GetClientSize();
426 wxCoord *start, *width;
427 wxCoord length, arrow;
428 wxRect rect;
429 if ( IsVertical() )
430 {
431 rect.x = 0;
432 rect.width = sizeTotal.x;
433 length = sizeTotal.y;
434 start = &rect.y;
435 width = &rect.height;
436 arrow = sizeArrow.y;
437 }
438 else // horizontal
439 {
440 rect.y = 0;
441 rect.height = sizeTotal.y;
442 length = sizeTotal.x;
443 start = &rect.x;
444 width = &rect.width;
445 arrow = sizeArrow.x;
446 }
447
448 switch ( elem )
449 {
450 case wxScrollBar::Element_Arrow_Line_1:
451 *start = 0;
452 *width = arrow;
453 break;
454
455 case wxScrollBar::Element_Arrow_Line_2:
456 *start = length - arrow;
457 *width = arrow;
458 break;
459
460 case wxScrollBar::Element_Arrow_Page_1:
461 case wxScrollBar::Element_Arrow_Page_2:
462 // we don't have them at all
463 break;
464
465 case wxScrollBar::Element_Thumb:
466 case wxScrollBar::Element_Bar_1:
467 case wxScrollBar::Element_Bar_2:
468 // we need to calculate the thumb position - do it
469 {
470 length -= 2*arrow;
471 wxCoord thumbStart, thumbEnd;
472 int range = GetRange();
473 if ( !range )
474 {
475 thumbStart =
476 thumbEnd = 0;
477 }
478 else
479 {
480 GetScrollBarThumbSize(length,
481 thumbPos,
482 GetThumbSize(),
483 range,
484 &thumbStart,
485 &thumbEnd);
486 }
487
488 if ( elem == wxScrollBar::Element_Thumb )
489 {
490 *start = thumbStart;
491 *width = thumbEnd - thumbStart;
492 }
493 else if ( elem == wxScrollBar::Element_Bar_1 )
494 {
495 *start = 0;
496 *width = thumbStart;
497 }
498 else // elem == wxScrollBar::Element_Bar_2
499 {
500 *start = thumbEnd;
501 *width = length - thumbEnd;
502 }
503
504 // everything is relative to the start of the shaft so far
505 *start += arrow;
506 }
507 break;
508
509 case wxScrollBar::Element_Max:
510 default:
511 wxFAIL_MSG( wxT("unknown scrollbar element") );
512 }
513
514 return rect;
515}
516
517wxCoord wxScrollBar::GetScrollbarSize() const
518{
519 const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize();
520
521 wxCoord sizeArrow, sizeTotal;
522 if ( GetWindowStyle() & wxVERTICAL )
523 {
524 sizeArrow = sizeArrowSB.y;
525 sizeTotal = GetSize().y;
526 }
527 else // horizontal
528 {
529 sizeArrow = sizeArrowSB.x;
530 sizeTotal = GetSize().x;
531 }
532
533 return sizeTotal - 2*sizeArrow;
534}
535
536
537wxCoord wxScrollBar::ScrollbarToPixel(int thumbPos)
538{
539 int range = GetRange();
540 if ( !range )
541 {
542 // the only valid position anyhow
543 return 0;
544 }
545
546 if ( thumbPos == -1 )
547 {
548 // by default use the current thumb position
549 thumbPos = GetThumbPosition();
550 }
551
552 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
553 return (thumbPos * GetScrollbarSize()) / range
554 + (IsVertical() ? sizeArrow.y : sizeArrow.x);
555}
556
557int wxScrollBar::PixelToScrollbar(wxCoord coord)
558{
559 const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize();
560 return ((coord - (IsVertical() ? sizeArrow.y : sizeArrow.x)) *
561 GetRange() ) / GetScrollbarSize();
562}
563
564// ----------------------------------------------------------------------------
565// drawing
566// ----------------------------------------------------------------------------
567
568void wxScrollBar::OnInternalIdle()
569{
570 UpdateThumb();
571 wxControl::OnInternalIdle();
572}
573
574void wxScrollBar::UpdateThumb()
575{
576 if ( m_dirty )
577 {
578 for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
579 {
580 if ( m_elementsState[n] & wxCONTROL_DIRTY )
581 {
582 wxRect rect = GetScrollbarRect((Element)n);
583
584 if ( rect.width && rect.height )
585 {
586 // we try to avoid redrawing the entire shaft (which might
587 // be quite long) if possible by only redrawing the area
588 // wich really changed
589 if ( (n == Element_Bar_1 || n == Element_Bar_2) &&
590 (m_thumbPosOld != -1) )
591 {
592 // the less efficient but more reliable (i.e. this will
593 // probably work everywhere) version: refresh the
594 // distance covered by thumb since the last update
595#if 0
596 wxRect rectOld =
597 GetRenderer()->GetScrollbarRect(this,
598 (Element)n,
599 m_thumbPosOld);
600 if ( IsVertical() )
601 {
602 if ( n == Element_Bar_1 )
603 rect.SetTop(rectOld.GetBottom());
604 else
605 rect.SetBottom(rectOld.GetBottom());
606 }
607 else // horizontal
608 {
609 if ( n == Element_Bar_1 )
610 rect.SetLeft(rectOld.GetRight());
611 else
612 rect.SetRight(rectOld.GetRight());
613 }
614#else // efficient version: only repaint the area occupied by
615 // the thumb previously - we can't do better than this
616 rect = GetScrollbarRect(Element_Thumb, m_thumbPosOld);
617#endif // 0/1
618 }
619
620#ifdef WXDEBUG_SCROLLBAR
621 static bool s_refreshDebug = false;
622 if ( s_refreshDebug )
623 {
624 wxClientDC dc(this);
625 dc.SetBrush(*wxCYAN_BRUSH);
626 dc.SetPen(*wxTRANSPARENT_PEN);
627 dc.DrawRectangle(rect);
628
629 // under Unix we use "--sync" X option for this
630 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
631 ::GdiFlush();
632 ::Sleep(200);
633 #endif // __WXMSW__
634 }
635#endif // WXDEBUG_SCROLLBAR
636
637 Refresh(false, &rect);
638 }
639
640 m_elementsState[n] &= ~wxCONTROL_DIRTY;
641 }
642 }
643
644 m_dirty = false;
645 }
646}
647
648void wxScrollBar::DoDraw(wxControlRenderer *renderer)
649{
650 renderer->DrawScrollbar(this, m_thumbPosOld);
651
652 // clear all dirty flags
653 m_dirty = false;
654 m_thumbPosOld = -1;
655}
656
657// ----------------------------------------------------------------------------
658// state flags
659// ----------------------------------------------------------------------------
660
661static inline wxScrollBar::Element ElementForArrow(wxScrollArrows::Arrow arrow)
662{
663 return arrow == wxScrollArrows::Arrow_First
664 ? wxScrollBar::Element_Arrow_Line_1
665 : wxScrollBar::Element_Arrow_Line_2;
666}
667
668int wxScrollBar::GetArrowState(wxScrollArrows::Arrow arrow) const
669{
670 return GetState(ElementForArrow(arrow));
671}
672
673void wxScrollBar::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
674{
675 Element which = ElementForArrow(arrow);
676 int state = GetState(which);
677 if ( set )
678 state |= flag;
679 else
680 state &= ~flag;
681
682 SetState(which, state);
683}
684
685int wxScrollBar::GetState(Element which) const
686{
687 // if the entire scrollbar is disabled, all of its elements are too
688 int flags = m_elementsState[which];
689 if ( !IsEnabled() )
690 flags |= wxCONTROL_DISABLED;
691
692 return flags;
693}
694
695void wxScrollBar::SetState(Element which, int flags)
696{
697 if ( (int)(m_elementsState[which] & ~wxCONTROL_DIRTY) != flags )
698 {
699 m_elementsState[which] = flags | wxCONTROL_DIRTY;
700
701 m_dirty = true;
702 }
703}
704
705// ----------------------------------------------------------------------------
706// input processing
707// ----------------------------------------------------------------------------
708
709bool wxScrollBar::OnArrow(wxScrollArrows::Arrow arrow)
710{
711 int oldThumbPos = GetThumbPosition();
712 PerformAction(arrow == wxScrollArrows::Arrow_First
713 ? wxACTION_SCROLL_LINE_UP
714 : wxACTION_SCROLL_LINE_DOWN);
715
716 // did we scroll till the end?
717 return GetThumbPosition() != oldThumbPos;
718}
719
720bool wxScrollBar::PerformAction(const wxControlAction& action,
721 long numArg,
722 const wxString& strArg)
723{
724 int thumbOld = m_thumbPos;
725
726 bool notify = false; // send an event about the change?
727
728 wxEventType scrollType;
729
730 // test for thumb move first as these events happen in quick succession
731 if ( action == wxACTION_SCROLL_THUMB_MOVE )
732 {
733 DoSetThumb(numArg);
734
735 // VS: we have to force redraw here, otherwise the thumb will lack
736 // behind mouse cursor
737 UpdateThumb();
738
739 scrollType = wxEVT_SCROLLWIN_THUMBTRACK;
740 }
741 else if ( action == wxACTION_SCROLL_LINE_UP )
742 {
743 scrollType = wxEVT_SCROLLWIN_LINEUP;
744 ScrollLines(-1);
745 }
746 else if ( action == wxACTION_SCROLL_LINE_DOWN )
747 {
748 scrollType = wxEVT_SCROLLWIN_LINEDOWN;
749 ScrollLines(1);
750 }
751 else if ( action == wxACTION_SCROLL_PAGE_UP )
752 {
753 scrollType = wxEVT_SCROLLWIN_PAGEUP;
754 ScrollPages(-1);
755 }
756 else if ( action == wxACTION_SCROLL_PAGE_DOWN )
757 {
758 scrollType = wxEVT_SCROLLWIN_PAGEDOWN;
759 ScrollPages(1);
760 }
761 else if ( action == wxACTION_SCROLL_START )
762 {
763 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
764 ScrollToStart();
765 }
766 else if ( action == wxACTION_SCROLL_END )
767 {
768 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
769 ScrollToEnd();
770 }
771 else if ( action == wxACTION_SCROLL_THUMB_DRAG )
772 {
773 // we won't use it but this line suppresses the compiler
774 // warning about "variable may be used without having been
775 // initialized"
776 scrollType = wxEVT_NULL;
777 }
778 else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
779 {
780 // always notify about this
781 notify = true;
782 scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
783 }
784 else
785 return wxControl::PerformAction(action, numArg, strArg);
786
787 // has scrollbar position changed?
788 bool changed = m_thumbPos != thumbOld;
789 if ( notify || changed )
790 {
791 if ( IsStandalone() )
792 {
793 // we should generate EVT_SCROLL events for the standalone
794 // scrollbars and not the EVT_SCROLLWIN ones
795 //
796 // NB: we assume that scrollbar events are sequentially numbered
797 // but this should be ok as other code relies on this as well
798 scrollType += wxEVT_SCROLL_TOP - wxEVT_SCROLLWIN_TOP;
799 wxScrollEvent event(scrollType, this->GetId(), m_thumbPos,
800 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
801 event.SetEventObject(this);
802 GetEventHandler()->ProcessEvent(event);
803 }
804 else // part of the window
805 {
806 wxScrollWinEvent event(scrollType, m_thumbPos,
807 IsVertical() ? wxVERTICAL : wxHORIZONTAL);
808 event.SetEventObject(this);
809 GetParent()->GetEventHandler()->ProcessEvent(event);
810 }
811 }
812
813 return true;
814}
815
816void wxScrollBar::ScrollToStart()
817{
818 DoSetThumb(0);
819}
820
821void wxScrollBar::ScrollToEnd()
822{
823 DoSetThumb(m_range - m_thumbSize);
824}
825
826bool wxScrollBar::ScrollLines(int nLines)
827{
828 DoSetThumb(m_thumbPos + nLines);
829 return true;
830}
831
832bool wxScrollBar::ScrollPages(int nPages)
833{
834 DoSetThumb(m_thumbPos + nPages*m_pageSize);
835 return true;
836}
837
838/* static */
839wxInputHandler *wxScrollBar::GetStdInputHandler(wxInputHandler *handlerDef)
840{
841 static wxStdScrollBarInputHandler
842 s_handler(wxTheme::Get()->GetRenderer(), handlerDef);
843
844 return &s_handler;
845}
846
847// ============================================================================
848// scroll bar input handler
849// ============================================================================
850
851// ----------------------------------------------------------------------------
852// wxScrollBarTimer
853// ----------------------------------------------------------------------------
854
855wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
856 const wxControlAction& action,
857 wxScrollBar *control)
858{
859 m_handler = handler;
860 m_action = action;
861 m_control = control;
862}
863
864bool wxScrollBarTimer::DoNotify()
865{
866 return m_handler->OnScrollTimer(m_control, m_action);
867}
868
869// ----------------------------------------------------------------------------
870// wxStdScrollBarInputHandler
871// ----------------------------------------------------------------------------
872
873wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
874 wxInputHandler *handler)
875 : wxStdInputHandler(handler)
876{
877 m_renderer = renderer;
878 m_winCapture = NULL;
879 m_htLast = wxHT_NOWHERE;
880 m_timerScroll = NULL;
881}
882
883wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
884{
885 // normally, it's NULL by now but just in case the user somehow managed to
886 // keep the mouse captured until now...
887 delete m_timerScroll;
888}
889
890void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
891 int flag,
892 bool doIt)
893{
894 if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
895 {
896 wxScrollBar::Element
897 elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);
898
899 int flags = control->GetState(elem);
900 if ( doIt )
901 flags |= flag;
902 else
903 flags &= ~flag;
904 control->SetState(elem, flags);
905 }
906}
907
908bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
909 const wxControlAction& action)
910{
911 int oldThumbPos = scrollbar->GetThumbPosition();
912 scrollbar->PerformAction(action);
913 if ( scrollbar->GetThumbPosition() != oldThumbPos )
914 return true;
915
916 // we scrolled till the end
917 m_timerScroll->Stop();
918
919 return false;
920}
921
922void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
923{
924 // return everything to the normal state
925 if ( m_winCapture )
926 {
927 m_winCapture->ReleaseMouse();
928 m_winCapture = NULL;
929 }
930
931 m_btnCapture = -1;
932
933 wxDELETE(m_timerScroll);
934
935 // unpress the arrow and highlight the current element
936 Press(control, false);
937}
938
939wxCoord
940wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
941 const wxMouseEvent& event) const
942{
943 wxPoint pt = event.GetPosition();
944 return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
945}
946
947void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
948 const wxMouseEvent& event)
949{
950 int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
951 thumbPos = scrollbar->PixelToScrollbar(thumbPos);
952 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
953}
954
955bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
956 const wxKeyEvent& event,
957 bool pressed)
958{
959 // we only react to the key presses here
960 if ( pressed )
961 {
962 wxControlAction action;
963 switch ( event.GetKeyCode() )
964 {
965 case WXK_DOWN:
966 case WXK_RIGHT: action = wxACTION_SCROLL_LINE_DOWN; break;
967 case WXK_UP:
968 case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break;
969 case WXK_HOME: action = wxACTION_SCROLL_START; break;
970 case WXK_END: action = wxACTION_SCROLL_END; break;
971 case WXK_PAGEUP: action = wxACTION_SCROLL_PAGE_UP; break;
972 case WXK_PAGEDOWN: action = wxACTION_SCROLL_PAGE_DOWN; break;
973 }
974
975 if ( !action.IsEmpty() )
976 {
977 consumer->PerformAction(action);
978
979 return true;
980 }
981 }
982
983 return wxStdInputHandler::HandleKey(consumer, event, pressed);
984}
985
986bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
987 const wxMouseEvent& event)
988{
989 // is this a click event from an acceptable button?
990 int btn = event.GetButton();
991 if ( btn == wxMOUSE_BTN_LEFT )
992 {
993 // determine which part of the window mouse is in
994 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
995 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition());
996
997 // when the mouse is pressed on any scrollbar element, we capture it
998 // and hold capture until the same mouse button is released
999 if ( event.ButtonDown() || event.ButtonDClick() )
1000 {
1001 if ( !m_winCapture )
1002 {
1003 m_btnCapture = btn;
1004 m_winCapture = consumer->GetInputWindow();
1005 m_winCapture->CaptureMouse();
1006
1007 // generate the command
1008 bool hasAction = true;
1009 wxControlAction action;
1010 switch ( ht )
1011 {
1012 case wxHT_SCROLLBAR_ARROW_LINE_1:
1013 action = wxACTION_SCROLL_LINE_UP;
1014 break;
1015
1016 case wxHT_SCROLLBAR_ARROW_LINE_2:
1017 action = wxACTION_SCROLL_LINE_DOWN;
1018 break;
1019
1020 case wxHT_SCROLLBAR_BAR_1:
1021 action = wxACTION_SCROLL_PAGE_UP;
1022 m_ptStartScrolling = event.GetPosition();
1023 break;
1024
1025 case wxHT_SCROLLBAR_BAR_2:
1026 action = wxACTION_SCROLL_PAGE_DOWN;
1027 m_ptStartScrolling = event.GetPosition();
1028 break;
1029
1030 case wxHT_SCROLLBAR_THUMB:
1031 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
1032 m_ofsMouse = GetMouseCoord(scrollbar, event) -
1033 scrollbar->ScrollbarToPixel();
1034
1035 // fall through: there is no immediate action
1036
1037 default:
1038 hasAction = false;
1039 }
1040
1041 // remove highlighting
1042 Highlight(scrollbar, false);
1043 m_htLast = ht;
1044
1045 // and press the arrow or highlight thumb now instead
1046 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
1047 Highlight(scrollbar, true);
1048 else
1049 Press(scrollbar, true);
1050
1051 // start dragging
1052 if ( hasAction )
1053 {
1054 m_timerScroll = new wxScrollBarTimer(this, action,
1055 scrollbar);
1056 m_timerScroll->StartAutoScroll();
1057 }
1058 //else: no (immediate) action
1059
1060 }
1061 //else: mouse already captured, nothing to do
1062 }
1063 // release mouse if the *same* button went up
1064 else if ( btn == m_btnCapture )
1065 {
1066 if ( m_winCapture )
1067 {
1068 StopScrolling(scrollbar);
1069
1070 // if we were dragging the thumb, send the last event
1071 if ( m_htLast == wxHT_SCROLLBAR_THUMB )
1072 {
1073 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
1074 }
1075
1076 m_htLast = ht;
1077 Highlight(scrollbar, true);
1078 }
1079 else
1080 {
1081 // this is not supposed to happen as the button can't go up
1082 // without going down previously and then we'd have
1083 // m_winCapture by now
1084 wxFAIL_MSG( wxT("logic error in mouse capturing code") );
1085 }
1086 }
1087 }
1088
1089 return wxStdInputHandler::HandleMouse(consumer, event);
1090}
1091
1092bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1093 const wxMouseEvent& event)
1094{
1095 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
1096
1097 if ( m_winCapture )
1098 {
1099 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Dragging() )
1100 {
1101 // make the thumb follow the mouse by keeping the same offset
1102 // between the mouse position and the top/left of the thumb
1103 HandleThumbMove(scrollbar, event);
1104
1105 return true;
1106 }
1107
1108 // no other changes are possible while the mouse is captured
1109 return false;
1110 }
1111
1112 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);
1113
1114 if ( event.Dragging() )
1115 {
1116 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition());
1117 if ( ht == m_htLast )
1118 {
1119 // nothing changed
1120 return false;
1121 }
1122
1123#ifdef DEBUG_MOUSE
1124 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
1125#endif // DEBUG_MOUSE
1126
1127 Highlight(scrollbar, false);
1128 m_htLast = ht;
1129
1130 if ( !isArrow )
1131 Highlight(scrollbar, true);
1132 //else: already done by wxScrollArrows::HandleMouseMove
1133 }
1134 else if ( event.Leaving() )
1135 {
1136 if ( !isArrow )
1137 Highlight(scrollbar, false);
1138
1139 m_htLast = wxHT_NOWHERE;
1140 }
1141 else // event.Entering()
1142 {
1143 // we don't process this event
1144 return false;
1145 }
1146
1147 // we did something
1148 return true;
1149}
1150
1151#endif // wxUSE_SCROLLBAR
1152
1153#if wxUSE_TIMER
1154
1155// ----------------------------------------------------------------------------
1156// wxScrollTimer
1157// ----------------------------------------------------------------------------
1158
1159wxScrollTimer::wxScrollTimer()
1160{
1161 m_skipNext = false;
1162}
1163
1164void wxScrollTimer::StartAutoScroll()
1165{
1166 // start scrolling immediately
1167 if ( !DoNotify() )
1168 {
1169 // ... and end it too
1170 return;
1171 }
1172
1173 // there is an initial delay before the scrollbar starts scrolling -
1174 // implement it by ignoring the first timer expiration and only start
1175 // scrolling from the second one
1176 m_skipNext = true;
1177 Start(200); // FIXME: hardcoded delay
1178}
1179
1180void wxScrollTimer::Notify()
1181{
1182 if ( m_skipNext )
1183 {
1184 // scroll normally now - reduce the delay
1185 Stop();
1186 Start(50); // FIXME: hardcoded delay
1187
1188 m_skipNext = false;
1189 }
1190 else
1191 {
1192 // if DoNotify() returns false, we're already deleted by the timer
1193 // event handler, so don't do anything else here
1194 (void)DoNotify();
1195 }
1196}
1197
1198#endif // wxUSE_TIMER