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