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