1///////////////////////////////////////////////////////////////////////////// 2// Name: src/univ/scrolbar.cpp 3// Purpose: wxScrollBar implementation 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 20.08.00 7// RCS-ID: $Id: scrolbar.cpp 42821 2006-10-31 09:26:55Z VS $ 8// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com) 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20#include "wx/wxprec.h" 21 22#ifdef __BORLANDC__ 23 #pragma hdrstop 24#endif 25 26#if wxUSE_SCROLLBAR 27 28#include "wx/scrolbar.h" 29 30#ifndef WX_PRECOMP 31 #include "wx/timer.h" 32 #include "wx/dcclient.h" 33 #include "wx/validate.h" 34 #include "wx/log.h" 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 49#if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__) 50#include "wx/msw/private.h" 51#endif 52 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) 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 132 m_dirty = false; 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 146 if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) 147 return false; 148 149 SetInitialSize(size); 150 151 // override the cursor of the target window (if any) 152 SetCursor(wxCURSOR_ARROW); 153 154 CreateInputHandler(wxINP_HANDLER_SCROLLBAR); 155 156 return true; 157} 158 159wxScrollBar::~wxScrollBar() 160{ 161} 162 163// ---------------------------------------------------------------------------- 164// misc accessors 165// ---------------------------------------------------------------------------- 166 167bool wxScrollBar::IsStandalone() const 168{ 169 wxWindow *parent = GetParent(); 170 if ( !parent ) 171 { 172 return true; 173 } 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(); 183} 184 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; 221 m_dirty = true; 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{ 255 // we only refresh everything when the range changes, thumb position 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 301wxScrollArrows::Arrow wxScrollBar::HitTestArrow(const wxPoint& pt) const 302{ 303 switch ( HitTestBar(pt) ) 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 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 569// ---------------------------------------------------------------------------- 570// drawing 571// ---------------------------------------------------------------------------- 572 573void wxScrollBar::OnInternalIdle() 574{ 575 UpdateThumb(); 576 wxControl::OnInternalIdle(); 577} 578 579void wxScrollBar::UpdateThumb() 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 { 587 wxRect rect = GetScrollbarRect((Element)n); 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 621 rect = GetScrollbarRect(Element_Thumb, m_thumbPosOld); 622#endif // 0/1 623 } 624 625#ifdef WXDEBUG_SCROLLBAR 626 static bool s_refreshDebug = false; 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 635 #if defined(__WXMSW__) && !defined(__WXMICROWIN__) 636 ::GdiFlush(); 637 ::Sleep(200); 638 #endif // __WXMSW__ 639 } 640#endif // WXDEBUG_SCROLLBAR 641 642 Refresh(false, &rect); 643 } 644 645 m_elementsState[n] &= ~wxCONTROL_DIRTY; 646 } 647 } 648 649 m_dirty = false; 650 } 651} 652 653void wxScrollBar::DoDraw(wxControlRenderer *renderer) 654{ 655 renderer->DrawScrollbar(this, m_thumbPosOld); 656 657 // clear all dirty flags 658 m_dirty = false; 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 706 m_dirty = true; 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 731 bool notify = false; // send an event about the change? 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 740 // VS: we have to force redraw here, otherwise the thumb will lack 741 // behind mouse cursor 742 UpdateThumb(); 743 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 786 notify = true; 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 { 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; 804 wxScrollEvent event(scrollType, this->GetId(), m_thumbPos, 805 IsVertical() ? wxVERTICAL : wxHORIZONTAL); 806 event.SetEventObject(this); 807 GetEventHandler()->ProcessEvent(event); 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); 815 } 816 } 817 818 return true; 819} 820 821void wxScrollBar::ScrollToStart() 822{ 823 DoSetThumb(0); 824} 825 826void wxScrollBar::ScrollToEnd() 827{ 828 DoSetThumb(m_range - m_thumbSize); 829} 830 831bool wxScrollBar::ScrollLines(int nLines) 832{ 833 DoSetThumb(m_thumbPos + nLines); 834 return true; 835} 836 837bool wxScrollBar::ScrollPages(int nPages) 838{ 839 DoSetThumb(m_thumbPos + nPages*m_pageSize); 840 return true; 841} 842 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 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 ) 919 return true; 920 921 // we scrolled till the end 922 m_timerScroll->Stop(); 923 924 return false; 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 945 Press(control, false); 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; 960 thumbPos = scrollbar->PixelToScrollbar(thumbPos); 961 scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos); 962} 963 964bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer, 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; 980 case WXK_PAGEUP: action = wxACTION_SCROLL_PAGE_UP; break; 981 case WXK_PAGEDOWN: action = wxACTION_SCROLL_PAGE_DOWN; break; 982 } 983 984 if ( !action.IsEmpty() ) 985 { 986 consumer->PerformAction(action); 987 988 return true; 989 } 990 } 991 992 return wxStdInputHandler::HandleKey(consumer, event, pressed); 993} 994 995bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer, 996 const wxMouseEvent& event) 997{ 998 // is this a click event from an acceptable button? 999 int btn = event.GetButton(); 1000 if ( btn == wxMOUSE_BTN_LEFT ) 1001 { 1002 // determine which part of the window mouse is in 1003 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar); 1004 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition()); 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; 1013 m_winCapture = consumer->GetInputWindow(); 1014 m_winCapture->CaptureMouse(); 1015 1016 // generate the command 1017 bool hasAction = true; 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: 1040 consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG); 1041 m_ofsMouse = GetMouseCoord(scrollbar, event) - 1042 scrollbar->ScrollbarToPixel(); 1043 1044 // fall through: there is no immediate action 1045 1046 default: 1047 hasAction = false; 1048 } 1049 1050 // remove highlighting 1051 Highlight(scrollbar, false); 1052 m_htLast = ht; 1053 1054 // and press the arrow or highlight thumb now instead 1055 if ( m_htLast == wxHT_SCROLLBAR_THUMB ) 1056 Highlight(scrollbar, true); 1057 else 1058 Press(scrollbar, true); 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; 1086 Highlight(scrollbar, true); 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 1098 return wxStdInputHandler::HandleMouse(consumer, event); 1099} 1100 1101bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer, 1102 const wxMouseEvent& event) 1103{ 1104 wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar); 1105 1106 if ( m_winCapture ) 1107 { 1108 if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Dragging() ) 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 1114 return true; 1115 } 1116 1117 // no other changes are possible while the mouse is captured 1118 return false; 1119 } 1120 1121 bool isArrow = scrollbar->GetArrows().HandleMouseMove(event); 1122 1123 if ( event.Dragging() ) 1124 { 1125 wxHitTest ht = scrollbar->HitTestBar(event.GetPosition()); 1126 if ( ht == m_htLast ) 1127 { 1128 // nothing changed 1129 return false; 1130 } 1131 1132#ifdef DEBUG_MOUSE 1133 wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht); 1134#endif // DEBUG_MOUSE 1135 1136 Highlight(scrollbar, false); 1137 m_htLast = ht; 1138 1139 if ( !isArrow ) 1140 Highlight(scrollbar, true); 1141 //else: already done by wxScrollArrows::HandleMouseMove 1142 } 1143 else if ( event.Leaving() ) 1144 { 1145 if ( !isArrow ) 1146 Highlight(scrollbar, false); 1147 1148 m_htLast = wxHT_NOWHERE; 1149 } 1150 else // event.Entering() 1151 { 1152 // we don't process this event 1153 return false; 1154 } 1155 1156 // we did something 1157 return true; 1158} 1159 1160#endif // wxUSE_SCROLLBAR 1161 1162#if wxUSE_TIMER 1163 1164// ---------------------------------------------------------------------------- 1165// wxScrollTimer 1166// ---------------------------------------------------------------------------- 1167 1168wxScrollTimer::wxScrollTimer() 1169{ 1170 m_skipNext = false; 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 1185 m_skipNext = true; 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 1197 m_skipNext = false; 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} 1206 1207#endif // wxUSE_TIMER 1208