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