1///////////////////////////////////////////////////////////////////////////// 2// Name: src/generic/scrlwing.cpp 3// Purpose: wxScrolledWindow implementation 4// Author: Julian Smart 5// Modified by: Vadim Zeitlin on 31.08.00: wxScrollHelper allows to implement. 6// Ron Lee on 10.4.02: virtual size / auto scrollbars et al. 7// Created: 01/02/97 8// RCS-ID: $Id: scrlwing.cpp 60600 2009-05-12 10:33:49Z VZ $ 9// Copyright: (c) wxWidgets team 10// Licence: wxWindows licence 11///////////////////////////////////////////////////////////////////////////// 12 13// ============================================================================ 14// declarations 15// ============================================================================ 16 17// ---------------------------------------------------------------------------- 18// headers 19// ---------------------------------------------------------------------------- 20 21// For compilers that support precompilation, includes "wx.h". 22#include "wx/wxprec.h" 23 24#ifdef __BORLANDC__ 25 #pragma hdrstop 26#endif 27 28#include "wx/scrolwin.h" 29 30#ifndef WX_PRECOMP 31 #include "wx/utils.h" 32 #include "wx/panel.h" 33 #include "wx/dcclient.h" 34 #if wxUSE_TIMER 35 #include "wx/timer.h" 36 #endif 37 #include "wx/sizer.h" 38 #include "wx/settings.h" 39#endif 40 41#ifdef __WXMAC__ 42#include "wx/scrolbar.h" 43#endif 44 45#include "wx/recguard.h" 46 47#ifdef __WXMSW__ 48 #include <windows.h> // for DLGC_WANTARROWS 49 #include "wx/msw/winundef.h" 50#endif 51 52#ifdef __WXMOTIF__ 53// For wxRETAINED implementation 54#ifdef __VMS__ //VMS's Xm.h is not (yet) compatible with C++ 55 //This code switches off the compiler warnings 56# pragma message disable nosimpint 57#endif 58#include <Xm/Xm.h> 59#ifdef __VMS__ 60# pragma message enable nosimpint 61#endif 62#endif 63 64/* 65 TODO PROPERTIES 66 style wxHSCROLL | wxVSCROLL 67*/ 68 69// ---------------------------------------------------------------------------- 70// wxScrollHelperEvtHandler: intercept the events from the window and forward 71// them to wxScrollHelper 72// ---------------------------------------------------------------------------- 73 74class WXDLLEXPORT wxScrollHelperEvtHandler : public wxEvtHandler 75{ 76public: 77 wxScrollHelperEvtHandler(wxScrollHelper *scrollHelper) 78 { 79 m_scrollHelper = scrollHelper; 80 } 81 82 virtual bool ProcessEvent(wxEvent& event); 83 84 void ResetDrawnFlag() { m_hasDrawnWindow = false; } 85 86private: 87 wxScrollHelper *m_scrollHelper; 88 89 bool m_hasDrawnWindow; 90 91 DECLARE_NO_COPY_CLASS(wxScrollHelperEvtHandler) 92}; 93 94#if wxUSE_TIMER 95// ---------------------------------------------------------------------------- 96// wxAutoScrollTimer: the timer used to generate a stream of scroll events when 97// a captured mouse is held outside the window 98// ---------------------------------------------------------------------------- 99 100class wxAutoScrollTimer : public wxTimer 101{ 102public: 103 wxAutoScrollTimer(wxWindow *winToScroll, wxScrollHelper *scroll, 104 wxEventType eventTypeToSend, 105 int pos, int orient); 106 107 virtual void Notify(); 108 109private: 110 wxWindow *m_win; 111 wxScrollHelper *m_scrollHelper; 112 wxEventType m_eventType; 113 int m_pos, 114 m_orient; 115 116 DECLARE_NO_COPY_CLASS(wxAutoScrollTimer) 117}; 118 119// ============================================================================ 120// implementation 121// ============================================================================ 122 123// ---------------------------------------------------------------------------- 124// wxAutoScrollTimer 125// ---------------------------------------------------------------------------- 126 127wxAutoScrollTimer::wxAutoScrollTimer(wxWindow *winToScroll, 128 wxScrollHelper *scroll, 129 wxEventType eventTypeToSend, 130 int pos, int orient) 131{ 132 m_win = winToScroll; 133 m_scrollHelper = scroll; 134 m_eventType = eventTypeToSend; 135 m_pos = pos; 136 m_orient = orient; 137} 138 139void wxAutoScrollTimer::Notify() 140{ 141 // only do all this as long as the window is capturing the mouse 142 if ( wxWindow::GetCapture() != m_win ) 143 { 144 Stop(); 145 } 146 else // we still capture the mouse, continue generating events 147 { 148 // first scroll the window if we are allowed to do it 149 wxScrollWinEvent event1(m_eventType, m_pos, m_orient); 150 event1.SetEventObject(m_win); 151 if ( m_scrollHelper->SendAutoScrollEvents(event1) && 152 m_win->GetEventHandler()->ProcessEvent(event1) ) 153 { 154 // and then send a pseudo mouse-move event to refresh the selection 155 wxMouseEvent event2(wxEVT_MOTION); 156 wxGetMousePosition(&event2.m_x, &event2.m_y); 157 158 // the mouse event coordinates should be client, not screen as 159 // returned by wxGetMousePosition 160 wxWindow *parentTop = m_win; 161 while ( parentTop->GetParent() ) 162 parentTop = parentTop->GetParent(); 163 wxPoint ptOrig = parentTop->GetPosition(); 164 event2.m_x -= ptOrig.x; 165 event2.m_y -= ptOrig.y; 166 167 event2.SetEventObject(m_win); 168 169 // FIXME: we don't fill in the other members - ok? 170 171 m_win->GetEventHandler()->ProcessEvent(event2); 172 } 173 else // can't scroll further, stop 174 { 175 Stop(); 176 } 177 } 178} 179#endif 180 181// ---------------------------------------------------------------------------- 182// wxScrollHelperEvtHandler 183// ---------------------------------------------------------------------------- 184 185bool wxScrollHelperEvtHandler::ProcessEvent(wxEvent& event) 186{ 187 wxEventType evType = event.GetEventType(); 188 189 // the explanation of wxEVT_PAINT processing hack: for historic reasons 190 // there are 2 ways to process this event in classes deriving from 191 // wxScrolledWindow. The user code may 192 // 193 // 1. override wxScrolledWindow::OnDraw(dc) 194 // 2. define its own OnPaint() handler 195 // 196 // In addition, in wxUniversal wxWindow defines OnPaint() itself and 197 // always processes the draw event, so we can't just try the window 198 // OnPaint() first and call our HandleOnPaint() if it doesn't process it 199 // (the latter would never be called in wxUniversal). 200 // 201 // So the solution is to have a flag telling us whether the user code drew 202 // anything in the window. We set it to true here but reset it to false in 203 // wxScrolledWindow::OnPaint() handler (which wouldn't be called if the 204 // user code defined OnPaint() in the derived class) 205 m_hasDrawnWindow = true; 206 207 // pass it on to the real handler 208 bool processed = wxEvtHandler::ProcessEvent(event); 209 210 // always process the size events ourselves, even if the user code handles 211 // them as well, as we need to AdjustScrollbars() 212 // 213 // NB: it is important to do it after processing the event in the normal 214 // way as HandleOnSize() may generate a wxEVT_SIZE itself if the 215 // scrollbar[s] (dis)appear and it should be seen by the user code 216 // after this one 217 if ( evType == wxEVT_SIZE ) 218 { 219 m_scrollHelper->HandleOnSize((wxSizeEvent &)event); 220 221 return true; 222 } 223 224 if ( processed ) 225 { 226 // normally, nothing more to do here - except if it was a paint event 227 // which wasn't really processed, then we'll try to call our 228 // OnDraw() below (from HandleOnPaint) 229 if ( m_hasDrawnWindow || event.IsCommandEvent() ) 230 { 231 return true; 232 } 233 } 234 235 // reset the skipped flag to false as it might have been set to true in 236 // ProcessEvent() above 237 event.Skip(false); 238 239 if ( evType == wxEVT_PAINT ) 240 { 241 m_scrollHelper->HandleOnPaint((wxPaintEvent &)event); 242 return true; 243 } 244 245 if ( evType == wxEVT_CHILD_FOCUS ) 246 { 247 m_scrollHelper->HandleOnChildFocus((wxChildFocusEvent &)event); 248 return true; 249 } 250 251 if ( evType == wxEVT_SCROLLWIN_TOP || 252 evType == wxEVT_SCROLLWIN_BOTTOM || 253 evType == wxEVT_SCROLLWIN_LINEUP || 254 evType == wxEVT_SCROLLWIN_LINEDOWN || 255 evType == wxEVT_SCROLLWIN_PAGEUP || 256 evType == wxEVT_SCROLLWIN_PAGEDOWN || 257 evType == wxEVT_SCROLLWIN_THUMBTRACK || 258 evType == wxEVT_SCROLLWIN_THUMBRELEASE ) 259 { 260 m_scrollHelper->HandleOnScroll((wxScrollWinEvent &)event); 261 return !event.GetSkipped(); 262 } 263 264 if ( evType == wxEVT_ENTER_WINDOW ) 265 { 266 m_scrollHelper->HandleOnMouseEnter((wxMouseEvent &)event); 267 } 268 else if ( evType == wxEVT_LEAVE_WINDOW ) 269 { 270 m_scrollHelper->HandleOnMouseLeave((wxMouseEvent &)event); 271 } 272#if wxUSE_MOUSEWHEEL 273 else if ( evType == wxEVT_MOUSEWHEEL ) 274 { 275 m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event); 276 return true; 277 } 278#endif // wxUSE_MOUSEWHEEL 279 else if ( evType == wxEVT_CHAR ) 280 { 281 m_scrollHelper->HandleOnChar((wxKeyEvent &)event); 282 return !event.GetSkipped(); 283 } 284 285 return false; 286} 287 288// ---------------------------------------------------------------------------- 289// wxScrollHelper construction 290// ---------------------------------------------------------------------------- 291 292wxScrollHelper::wxScrollHelper(wxWindow *win) 293{ 294 wxASSERT_MSG( win, _T("associated window can't be NULL in wxScrollHelper") ); 295 296 m_xScrollPixelsPerLine = 297 m_yScrollPixelsPerLine = 298 m_xScrollPosition = 299 m_yScrollPosition = 300 m_xScrollLines = 301 m_yScrollLines = 302 m_xScrollLinesPerPage = 303 m_yScrollLinesPerPage = 0; 304 305 m_xScrollingEnabled = 306 m_yScrollingEnabled = true; 307 308 m_scaleX = 309 m_scaleY = 1.0; 310#if wxUSE_MOUSEWHEEL 311 m_wheelRotation = 0; 312#endif 313 314 m_win = 315 m_targetWindow = (wxWindow *)NULL; 316 317 m_timerAutoScroll = (wxTimer *)NULL; 318 319 m_handler = NULL; 320 321 m_win = win; 322 323 m_win->SetScrollHelper( this ); 324 325 // by default, the associated window is also the target window 326 DoSetTargetWindow(win); 327} 328 329wxScrollHelper::~wxScrollHelper() 330{ 331 StopAutoScrolling(); 332 333 DeleteEvtHandler(); 334} 335 336// ---------------------------------------------------------------------------- 337// setting scrolling parameters 338// ---------------------------------------------------------------------------- 339 340void wxScrollHelper::SetScrollbars(int pixelsPerUnitX, 341 int pixelsPerUnitY, 342 int noUnitsX, 343 int noUnitsY, 344 int xPos, 345 int yPos, 346 bool noRefresh) 347{ 348 int xpos, ypos; 349 350 CalcUnscrolledPosition(xPos, yPos, &xpos, &ypos); 351 bool do_refresh = 352 ( 353 (noUnitsX != 0 && m_xScrollLines == 0) || 354 (noUnitsX < m_xScrollLines && xpos > pixelsPerUnitX * noUnitsX) || 355 356 (noUnitsY != 0 && m_yScrollLines == 0) || 357 (noUnitsY < m_yScrollLines && ypos > pixelsPerUnitY * noUnitsY) || 358 (xPos != m_xScrollPosition) || 359 (yPos != m_yScrollPosition) 360 ); 361 362 m_xScrollPixelsPerLine = pixelsPerUnitX; 363 m_yScrollPixelsPerLine = pixelsPerUnitY; 364 m_xScrollPosition = xPos; 365 m_yScrollPosition = yPos; 366 367 int w = noUnitsX * pixelsPerUnitX; 368 int h = noUnitsY * pixelsPerUnitY; 369 370 // For better backward compatibility we set persisting limits 371 // here not just the size. It makes SetScrollbars 'sticky' 372 // emulating the old non-autoscroll behaviour. 373 // m_targetWindow->SetVirtualSizeHints( w, h ); 374 375 // The above should arguably be deprecated, this however we still need. 376 377 // take care not to set 0 virtual size, 0 means that we don't have any 378 // scrollbars and hence we should use the real size instead of the virtual 379 // one which is indicated by using wxDefaultCoord 380 m_targetWindow->SetVirtualSize( w ? w : wxDefaultCoord, 381 h ? h : wxDefaultCoord); 382 383 if (do_refresh && !noRefresh) 384 m_targetWindow->Refresh(true, GetScrollRect()); 385 386#ifndef __WXUNIVERSAL__ 387 // If the target is not the same as the window with the scrollbars, 388 // then we need to update the scrollbars here, since they won't have 389 // been updated by SetVirtualSize(). 390 if ( m_targetWindow != m_win ) 391#endif // !__WXUNIVERSAL__ 392 { 393 AdjustScrollbars(); 394 } 395#ifndef __WXUNIVERSAL__ 396 else 397 { 398 // otherwise this has been done by AdjustScrollbars, above 399 } 400#endif // !__WXUNIVERSAL__ 401} 402 403// ---------------------------------------------------------------------------- 404// [target] window handling 405// ---------------------------------------------------------------------------- 406 407void wxScrollHelper::DeleteEvtHandler() 408{ 409 // search for m_handler in the handler list 410 if ( m_win && m_handler ) 411 { 412 if ( m_win->RemoveEventHandler(m_handler) ) 413 { 414 delete m_handler; 415 } 416 //else: something is very wrong, so better [maybe] leak memory than 417 // risk a crash because of double deletion 418 419 m_handler = NULL; 420 } 421} 422 423void wxScrollHelper::DoSetTargetWindow(wxWindow *target) 424{ 425 m_targetWindow = target; 426#ifdef __WXMAC__ 427 target->MacSetClipChildren( true ) ; 428#endif 429 430 // install the event handler which will intercept the events we're 431 // interested in (but only do it for our real window, not the target window 432 // which we scroll - we don't need to hijack its events) 433 if ( m_targetWindow == m_win ) 434 { 435 // if we already have a handler, delete it first 436 DeleteEvtHandler(); 437 438 m_handler = new wxScrollHelperEvtHandler(this); 439 m_targetWindow->PushEventHandler(m_handler); 440 } 441} 442 443void wxScrollHelper::SetTargetWindow(wxWindow *target) 444{ 445 wxCHECK_RET( target, wxT("target window must not be NULL") ); 446 447 if ( target == m_targetWindow ) 448 return; 449 450 DoSetTargetWindow(target); 451} 452 453wxWindow *wxScrollHelper::GetTargetWindow() const 454{ 455 return m_targetWindow; 456} 457 458// ---------------------------------------------------------------------------- 459// scrolling implementation itself 460// ---------------------------------------------------------------------------- 461 462void wxScrollHelper::HandleOnScroll(wxScrollWinEvent& event) 463{ 464 int nScrollInc = CalcScrollInc(event); 465 if ( nScrollInc == 0 ) 466 { 467 // can't scroll further 468 event.Skip(); 469 470 return; 471 } 472 473 bool needsRefresh = false; 474 475 int dx = 0, 476 dy = 0; 477 int orient = event.GetOrientation(); 478 if (orient == wxHORIZONTAL) 479 { 480 if ( m_xScrollingEnabled ) 481 { 482 dx = -m_xScrollPixelsPerLine * nScrollInc; 483 } 484 else 485 { 486 needsRefresh = true; 487 } 488 } 489 else 490 { 491 if ( m_yScrollingEnabled ) 492 { 493 dy = -m_yScrollPixelsPerLine * nScrollInc; 494 } 495 else 496 { 497 needsRefresh = true; 498 } 499 } 500 501 if ( !needsRefresh ) 502 { 503 // flush all pending repaints before we change m_{x,y}ScrollPosition, as 504 // otherwise invalidated area could be updated incorrectly later when 505 // ScrollWindow() makes sure they're repainted before scrolling them 506#ifdef __WXMAC__ 507 // wxWindowMac is taking care of making sure the update area is correctly 508 // set up, while not forcing an immediate redraw 509#else 510 m_targetWindow->Update(); 511#endif 512 } 513 514 if (orient == wxHORIZONTAL) 515 { 516 m_xScrollPosition += nScrollInc; 517 m_win->SetScrollPos(wxHORIZONTAL, m_xScrollPosition); 518 } 519 else 520 { 521 m_yScrollPosition += nScrollInc; 522 m_win->SetScrollPos(wxVERTICAL, m_yScrollPosition); 523 } 524 525 if ( needsRefresh ) 526 { 527 m_targetWindow->Refresh(true, GetScrollRect()); 528 } 529 else 530 { 531 m_targetWindow->ScrollWindow(dx, dy, GetScrollRect()); 532 } 533} 534 535int wxScrollHelper::CalcScrollInc(wxScrollWinEvent& event) 536{ 537 int pos = event.GetPosition(); 538 int orient = event.GetOrientation(); 539 540 int nScrollInc = 0; 541 if (event.GetEventType() == wxEVT_SCROLLWIN_TOP) 542 { 543 if (orient == wxHORIZONTAL) 544 nScrollInc = - m_xScrollPosition; 545 else 546 nScrollInc = - m_yScrollPosition; 547 } else 548 if (event.GetEventType() == wxEVT_SCROLLWIN_BOTTOM) 549 { 550 if (orient == wxHORIZONTAL) 551 nScrollInc = m_xScrollLines - m_xScrollPosition; 552 else 553 nScrollInc = m_yScrollLines - m_yScrollPosition; 554 } else 555 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP) 556 { 557 nScrollInc = -1; 558 } else 559 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) 560 { 561 nScrollInc = 1; 562 } else 563 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEUP) 564 { 565 if (orient == wxHORIZONTAL) 566 nScrollInc = -GetScrollPageSize(wxHORIZONTAL); 567 else 568 nScrollInc = -GetScrollPageSize(wxVERTICAL); 569 } else 570 if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN) 571 { 572 if (orient == wxHORIZONTAL) 573 nScrollInc = GetScrollPageSize(wxHORIZONTAL); 574 else 575 nScrollInc = GetScrollPageSize(wxVERTICAL); 576 } else 577 if ((event.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK) || 578 (event.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE)) 579 { 580 if (orient == wxHORIZONTAL) 581 nScrollInc = pos - m_xScrollPosition; 582 else 583 nScrollInc = pos - m_yScrollPosition; 584 } 585 586 if (orient == wxHORIZONTAL) 587 { 588 if (m_xScrollPixelsPerLine > 0) 589 { 590 if ( m_xScrollPosition + nScrollInc < 0 ) 591 { 592 // As -ve as we can go 593 nScrollInc = -m_xScrollPosition; 594 } 595 else // check for the other bound 596 { 597 const int posMax = m_xScrollLines - m_xScrollLinesPerPage; 598 if ( m_xScrollPosition + nScrollInc > posMax ) 599 { 600 // As +ve as we can go 601 nScrollInc = posMax - m_xScrollPosition; 602 } 603 } 604 } 605 else 606 m_targetWindow->Refresh(true, GetScrollRect()); 607 } 608 else 609 { 610 if ( m_yScrollPixelsPerLine > 0 ) 611 { 612 if ( m_yScrollPosition + nScrollInc < 0 ) 613 { 614 // As -ve as we can go 615 nScrollInc = -m_yScrollPosition; 616 } 617 else // check for the other bound 618 { 619 const int posMax = m_yScrollLines - m_yScrollLinesPerPage; 620 if ( m_yScrollPosition + nScrollInc > posMax ) 621 { 622 // As +ve as we can go 623 nScrollInc = posMax - m_yScrollPosition; 624 } 625 } 626 } 627 else 628 { 629 // VZ: why do we do this? (FIXME) 630 m_targetWindow->Refresh(true, GetScrollRect()); 631 } 632 } 633 634 return nScrollInc; 635} 636 637// Adjust the scrollbars - new version. 638void wxScrollHelper::AdjustScrollbars() 639{ 640 static wxRecursionGuardFlag s_flagReentrancy; 641 wxRecursionGuard guard(s_flagReentrancy); 642 if ( guard.IsInside() ) 643 { 644 // don't reenter AdjustScrollbars() while another call to 645 // AdjustScrollbars() is in progress because this may lead to calling 646 // ScrollWindow() twice and this can really happen under MSW if 647 // SetScrollbar() call below adds or removes the scrollbar which 648 // changes the window size and hence results in another 649 // AdjustScrollbars() call 650 return; 651 } 652 653 int w = 0, h = 0; 654 int oldw, oldh; 655 656 int oldXScroll = m_xScrollPosition; 657 int oldYScroll = m_yScrollPosition; 658 659 // VZ: at least under Windows this loop is useless because when scrollbars 660 // [dis]appear we get a WM_SIZE resulting in another call to 661 // AdjustScrollbars() anyhow. As it doesn't seem to do any harm I leave 662 // it here for now but it would be better to ensure that all ports 663 // generate EVT_SIZE when scrollbars [dis]appear, emulating it if 664 // necessary, and remove it later 665 // JACS: Stop potential infinite loop by limiting number of iterations 666 int iterationCount = 0; 667 const int iterationMax = 5; 668 do 669 { 670 iterationCount ++; 671 672 GetTargetSize(&w, 0); 673 674 // scroll lines per page: if 0, no scrolling is needed 675 int linesPerPage; 676 677 if ( m_xScrollPixelsPerLine == 0 ) 678 { 679 // scrolling is disabled 680 m_xScrollLines = 0; 681 m_xScrollPosition = 0; 682 linesPerPage = 0; 683 } 684 else // might need scrolling 685 { 686 // Round up integer division to catch any "leftover" client space. 687 const int wVirt = m_targetWindow->GetVirtualSize().GetWidth(); 688 m_xScrollLines = (wVirt + m_xScrollPixelsPerLine - 1) / m_xScrollPixelsPerLine; 689 690 // Calculate page size i.e. number of scroll units you get on the 691 // current client window. 692 linesPerPage = w / m_xScrollPixelsPerLine; 693 694 // Special case. When client and virtual size are very close but 695 // the client is big enough, kill scrollbar. 696 if ((linesPerPage < m_xScrollLines) && (w >= wVirt)) ++linesPerPage; 697 698 if (linesPerPage >= m_xScrollLines) 699 { 700 // we're big enough to not need scrolling 701 linesPerPage = 702 m_xScrollLines = 703 m_xScrollPosition = 0; 704 } 705 else // we do need a scrollbar 706 { 707 if ( linesPerPage < 1 ) 708 linesPerPage = 1; 709 710 // Correct position if greater than extent of canvas minus 711 // the visible portion of it or if below zero 712 const int posMax = m_xScrollLines - linesPerPage; 713 if ( m_xScrollPosition > posMax ) 714 m_xScrollPosition = posMax; 715 else if ( m_xScrollPosition < 0 ) 716 m_xScrollPosition = 0; 717 } 718 } 719 720 m_win->SetScrollbar(wxHORIZONTAL, m_xScrollPosition, 721 linesPerPage, m_xScrollLines); 722 723 // The amount by which we scroll when paging 724 SetScrollPageSize(wxHORIZONTAL, linesPerPage); 725 726 GetTargetSize(0, &h); 727 728 if ( m_yScrollPixelsPerLine == 0 ) 729 { 730 // scrolling is disabled 731 m_yScrollLines = 0; 732 m_yScrollPosition = 0; 733 linesPerPage = 0; 734 } 735 else // might need scrolling 736 { 737 // Round up integer division to catch any "leftover" client space. 738 const int hVirt = m_targetWindow->GetVirtualSize().GetHeight(); 739 m_yScrollLines = ( hVirt + m_yScrollPixelsPerLine - 1 ) / m_yScrollPixelsPerLine; 740 741 // Calculate page size i.e. number of scroll units you get on the 742 // current client window. 743 linesPerPage = h / m_yScrollPixelsPerLine; 744 745 // Special case. When client and virtual size are very close but 746 // the client is big enough, kill scrollbar. 747 if ((linesPerPage < m_yScrollLines) && (h >= hVirt)) ++linesPerPage; 748 749 if (linesPerPage >= m_yScrollLines) 750 { 751 // we're big enough to not need scrolling 752 linesPerPage = 753 m_yScrollLines = 754 m_yScrollPosition = 0; 755 } 756 else // we do need a scrollbar 757 { 758 if ( linesPerPage < 1 ) 759 linesPerPage = 1; 760 761 // Correct position if greater than extent of canvas minus 762 // the visible portion of it or if below zero 763 const int posMax = m_yScrollLines - linesPerPage; 764 if ( m_yScrollPosition > posMax ) 765 m_yScrollPosition = posMax; 766 else if ( m_yScrollPosition < 0 ) 767 m_yScrollPosition = 0; 768 } 769 } 770 771 m_win->SetScrollbar(wxVERTICAL, m_yScrollPosition, 772 linesPerPage, m_yScrollLines); 773 774 // The amount by which we scroll when paging 775 SetScrollPageSize(wxVERTICAL, linesPerPage); 776 777 778 // If a scrollbar (dis)appeared as a result of this, adjust them again. 779 oldw = w; 780 oldh = h; 781 782 GetTargetSize( &w, &h ); 783 } while ( (w != oldw || h != oldh) && (iterationCount < iterationMax) ); 784 785#ifdef __WXMOTIF__ 786 // Sorry, some Motif-specific code to implement a backing pixmap 787 // for the wxRETAINED style. Implementing a backing store can't 788 // be entirely generic because it relies on the wxWindowDC implementation 789 // to duplicate X drawing calls for the backing pixmap. 790 791 if ( m_targetWindow->GetWindowStyle() & wxRETAINED ) 792 { 793 Display* dpy = XtDisplay((Widget)m_targetWindow->GetMainWidget()); 794 795 int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine; 796 int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine; 797 if (m_targetWindow->GetBackingPixmap() && 798 !((m_targetWindow->GetPixmapWidth() == totalPixelWidth) && 799 (m_targetWindow->GetPixmapHeight() == totalPixelHeight))) 800 { 801 XFreePixmap (dpy, (Pixmap) m_targetWindow->GetBackingPixmap()); 802 m_targetWindow->SetBackingPixmap((WXPixmap) 0); 803 } 804 805 if (!m_targetWindow->GetBackingPixmap() && 806 (m_xScrollLines != 0) && (m_yScrollLines != 0)) 807 { 808 int depth = wxDisplayDepth(); 809 m_targetWindow->SetPixmapWidth(totalPixelWidth); 810 m_targetWindow->SetPixmapHeight(totalPixelHeight); 811 m_targetWindow->SetBackingPixmap((WXPixmap) XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)), 812 m_targetWindow->GetPixmapWidth(), m_targetWindow->GetPixmapHeight(), depth)); 813 } 814 815 } 816#endif // Motif 817 818 if (oldXScroll != m_xScrollPosition) 819 { 820 if (m_xScrollingEnabled) 821 m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll - m_xScrollPosition), 0, 822 GetScrollRect() ); 823 else 824 m_targetWindow->Refresh(true, GetScrollRect()); 825 } 826 827 if (oldYScroll != m_yScrollPosition) 828 { 829 if (m_yScrollingEnabled) 830 m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition), 831 GetScrollRect() ); 832 else 833 m_targetWindow->Refresh(true, GetScrollRect()); 834 } 835} 836 837void wxScrollHelper::DoPrepareDC(wxDC& dc) 838{ 839 wxPoint pt = dc.GetDeviceOrigin(); 840#ifdef __WXGTK__ 841 // It may actually be correct to always query 842 // the m_sign from the DC here, but I leve the 843 // #ifdef GTK for now. 844 if (m_win->GetLayoutDirection() == wxLayout_RightToLeft) 845 dc.SetDeviceOrigin( pt.x + m_xScrollPosition * m_xScrollPixelsPerLine, 846 pt.y - m_yScrollPosition * m_yScrollPixelsPerLine ); 847 else 848#endif 849 dc.SetDeviceOrigin( pt.x - m_xScrollPosition * m_xScrollPixelsPerLine, 850 pt.y - m_yScrollPosition * m_yScrollPixelsPerLine ); 851 dc.SetUserScale( m_scaleX, m_scaleY ); 852} 853 854void wxScrollHelper::SetScrollRate( int xstep, int ystep ) 855{ 856 int old_x = m_xScrollPixelsPerLine * m_xScrollPosition; 857 int old_y = m_yScrollPixelsPerLine * m_yScrollPosition; 858 859 m_xScrollPixelsPerLine = xstep; 860 m_yScrollPixelsPerLine = ystep; 861 862 int new_x = m_xScrollPixelsPerLine * m_xScrollPosition; 863 int new_y = m_yScrollPixelsPerLine * m_yScrollPosition; 864 865 m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition ); 866 m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition ); 867 m_targetWindow->ScrollWindow( old_x - new_x, old_y - new_y ); 868 869 AdjustScrollbars(); 870} 871 872void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const 873{ 874 if ( x_unit ) 875 *x_unit = m_xScrollPixelsPerLine; 876 if ( y_unit ) 877 *y_unit = m_yScrollPixelsPerLine; 878} 879 880 881int wxScrollHelper::GetScrollLines( int orient ) const 882{ 883 if ( orient == wxHORIZONTAL ) 884 return m_xScrollLines; 885 else 886 return m_yScrollLines; 887} 888 889int wxScrollHelper::GetScrollPageSize(int orient) const 890{ 891 if ( orient == wxHORIZONTAL ) 892 return m_xScrollLinesPerPage; 893 else 894 return m_yScrollLinesPerPage; 895} 896 897void wxScrollHelper::SetScrollPageSize(int orient, int pageSize) 898{ 899 if ( orient == wxHORIZONTAL ) 900 m_xScrollLinesPerPage = pageSize; 901 else 902 m_yScrollLinesPerPage = pageSize; 903} 904 905/* 906 * Scroll to given position (scroll position, not pixel position) 907 */ 908void wxScrollHelper::Scroll( int x_pos, int y_pos ) 909{ 910 if (!m_targetWindow) 911 return; 912 913 if (((x_pos == -1) || (x_pos == m_xScrollPosition)) && 914 ((y_pos == -1) || (y_pos == m_yScrollPosition))) return; 915 916 int w = 0, h = 0; 917 GetTargetSize(&w, &h); 918 919 // compute new position: 920 int new_x = m_xScrollPosition; 921 int new_y = m_yScrollPosition; 922 923 if ((x_pos != -1) && (m_xScrollPixelsPerLine)) 924 { 925 new_x = x_pos; 926 927 // Calculate page size i.e. number of scroll units you get on the 928 // current client window 929 int noPagePositions = w/m_xScrollPixelsPerLine; 930 if (noPagePositions < 1) noPagePositions = 1; 931 932 // Correct position if greater than extent of canvas minus 933 // the visible portion of it or if below zero 934 new_x = wxMin( m_xScrollLines-noPagePositions, new_x ); 935 new_x = wxMax( 0, new_x ); 936 } 937 if ((y_pos != -1) && (m_yScrollPixelsPerLine)) 938 { 939 new_y = y_pos; 940 941 // Calculate page size i.e. number of scroll units you get on the 942 // current client window 943 int noPagePositions = h/m_yScrollPixelsPerLine; 944 if (noPagePositions < 1) noPagePositions = 1; 945 946 // Correct position if greater than extent of canvas minus 947 // the visible portion of it or if below zero 948 new_y = wxMin( m_yScrollLines-noPagePositions, new_y ); 949 new_y = wxMax( 0, new_y ); 950 } 951 952 if ( new_x == m_xScrollPosition && new_y == m_yScrollPosition ) 953 return; // nothing to do, the position didn't change 954 955 // flush all pending repaints before we change m_{x,y}ScrollPosition, as 956 // otherwise invalidated area could be updated incorrectly later when 957 // ScrollWindow() makes sure they're repainted before scrolling them 958 m_targetWindow->Update(); 959 960 // update the position and scroll the window now: 961 if (m_xScrollPosition != new_x) 962 { 963 int old_x = m_xScrollPosition; 964 m_xScrollPosition = new_x; 965 m_win->SetScrollPos( wxHORIZONTAL, new_x ); 966 m_targetWindow->ScrollWindow( (old_x-new_x)*m_xScrollPixelsPerLine, 0, 967 GetScrollRect() ); 968 } 969 970 if (m_yScrollPosition != new_y) 971 { 972 int old_y = m_yScrollPosition; 973 m_yScrollPosition = new_y; 974 m_win->SetScrollPos( wxVERTICAL, new_y ); 975 m_targetWindow->ScrollWindow( 0, (old_y-new_y)*m_yScrollPixelsPerLine, 976 GetScrollRect() ); 977 } 978} 979 980void wxScrollHelper::EnableScrolling (bool x_scroll, bool y_scroll) 981{ 982 m_xScrollingEnabled = x_scroll; 983 m_yScrollingEnabled = y_scroll; 984} 985 986// Where the current view starts from 987void wxScrollHelper::GetViewStart (int *x, int *y) const 988{ 989 if ( x ) 990 *x = m_xScrollPosition; 991 if ( y ) 992 *y = m_yScrollPosition; 993} 994 995void wxScrollHelper::DoCalcScrolledPosition(int x, int y, int *xx, int *yy) const 996{ 997 if ( xx ) 998 *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine; 999 if ( yy ) 1000 *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine; 1001} 1002 1003void wxScrollHelper::DoCalcUnscrolledPosition(int x, int y, int *xx, int *yy) const 1004{ 1005 if ( xx ) 1006 *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine; 1007 if ( yy ) 1008 *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine; 1009} 1010 1011// ---------------------------------------------------------------------------- 1012// geometry 1013// ---------------------------------------------------------------------------- 1014 1015bool wxScrollHelper::ScrollLayout() 1016{ 1017 if ( m_win->GetSizer() && m_targetWindow == m_win ) 1018 { 1019 // If we're the scroll target, take into account the 1020 // virtual size and scrolled position of the window. 1021 1022 int x = 0, y = 0, w = 0, h = 0; 1023 CalcScrolledPosition(0,0, &x,&y); 1024 m_win->GetVirtualSize(&w, &h); 1025 m_win->GetSizer()->SetDimension(x, y, w, h); 1026 return true; 1027 } 1028 1029 // fall back to default for LayoutConstraints 1030 return m_win->wxWindow::Layout(); 1031} 1032 1033void wxScrollHelper::ScrollDoSetVirtualSize(int x, int y) 1034{ 1035 m_win->wxWindow::DoSetVirtualSize( x, y ); 1036 AdjustScrollbars(); 1037 1038 if (m_win->GetAutoLayout()) 1039 m_win->Layout(); 1040} 1041 1042// wxWindow's GetBestVirtualSize returns the actual window size, 1043// whereas we want to return the virtual size 1044wxSize wxScrollHelper::ScrollGetBestVirtualSize() const 1045{ 1046 wxSize clientSize(m_win->GetClientSize()); 1047 if ( m_win->GetSizer() ) 1048 clientSize.IncTo(m_win->GetSizer()->CalcMin()); 1049 1050 return clientSize; 1051} 1052 1053// return the window best size from the given best virtual size 1054wxSize 1055wxScrollHelper::ScrollGetWindowSizeForVirtualSize(const wxSize& size) const 1056{ 1057 // Only use the content to set the window size in the direction 1058 // where there's no scrolling; otherwise we're going to get a huge 1059 // window in the direction in which scrolling is enabled 1060 int ppuX, ppuY; 1061 GetScrollPixelsPerUnit(&ppuX, &ppuY); 1062 1063 wxSize minSize = m_win->GetMinSize(); 1064 1065 wxSize best(size); 1066 if (ppuX > 0) 1067 best.x = minSize.x + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); 1068 if (ppuY > 0) 1069 best.y = minSize.y + wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); 1070 1071 return best; 1072} 1073 1074// ---------------------------------------------------------------------------- 1075// event handlers 1076// ---------------------------------------------------------------------------- 1077 1078// Default OnSize resets scrollbars, if any 1079void wxScrollHelper::HandleOnSize(wxSizeEvent& WXUNUSED(event)) 1080{ 1081 if ( m_targetWindow->GetAutoLayout() ) 1082 { 1083 wxSize size = m_targetWindow->GetBestVirtualSize(); 1084 1085 // This will call ::Layout() and ::AdjustScrollbars() 1086 m_win->SetVirtualSize( size ); 1087 } 1088 else 1089 { 1090 AdjustScrollbars(); 1091 } 1092} 1093 1094// This calls OnDraw, having adjusted the origin according to the current 1095// scroll position 1096void wxScrollHelper::HandleOnPaint(wxPaintEvent& WXUNUSED(event)) 1097{ 1098 // don't use m_targetWindow here, this is always called for ourselves 1099 wxPaintDC dc(m_win); 1100 DoPrepareDC(dc); 1101 1102 OnDraw(dc); 1103} 1104 1105// kbd handling: notice that we use OnChar() and not OnKeyDown() for 1106// compatibility here - if we used OnKeyDown(), the programs which process 1107// arrows themselves in their OnChar() would never get the message and like 1108// this they always have the priority 1109void wxScrollHelper::HandleOnChar(wxKeyEvent& event) 1110{ 1111 int stx = 0, sty = 0, // view origin 1112 szx = 0, szy = 0, // view size (total) 1113 clix = 0, cliy = 0; // view size (on screen) 1114 1115 GetViewStart(&stx, &sty); 1116 GetTargetSize(&clix, &cliy); 1117 m_targetWindow->GetVirtualSize(&szx, &szy); 1118 1119 if( m_xScrollPixelsPerLine ) 1120 { 1121 clix /= m_xScrollPixelsPerLine; 1122 szx /= m_xScrollPixelsPerLine; 1123 } 1124 else 1125 { 1126 clix = 0; 1127 szx = -1; 1128 } 1129 if( m_yScrollPixelsPerLine ) 1130 { 1131 cliy /= m_yScrollPixelsPerLine; 1132 szy /= m_yScrollPixelsPerLine; 1133 } 1134 else 1135 { 1136 cliy = 0; 1137 szy = -1; 1138 } 1139 1140 int xScrollOld = m_xScrollPosition, 1141 yScrollOld = m_yScrollPosition; 1142 1143 int dsty; 1144 switch ( event.GetKeyCode() ) 1145 { 1146 case WXK_PAGEUP: 1147 dsty = sty - (5 * cliy / 6); 1148 Scroll(-1, (dsty == -1) ? 0 : dsty); 1149 break; 1150 1151 case WXK_PAGEDOWN: 1152 Scroll(-1, sty + (5 * cliy / 6)); 1153 break; 1154 1155 case WXK_HOME: 1156 Scroll(0, event.ControlDown() ? 0 : -1); 1157 break; 1158 1159 case WXK_END: 1160 Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1); 1161 break; 1162 1163 case WXK_UP: 1164 Scroll(-1, sty - 1); 1165 break; 1166 1167 case WXK_DOWN: 1168 Scroll(-1, sty + 1); 1169 break; 1170 1171 case WXK_LEFT: 1172 Scroll(stx - 1, -1); 1173 break; 1174 1175 case WXK_RIGHT: 1176 Scroll(stx + 1, -1); 1177 break; 1178 1179 default: 1180 // not for us 1181 event.Skip(); 1182 } 1183 1184 if ( m_xScrollPosition != xScrollOld ) 1185 { 1186 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_xScrollPosition, 1187 wxHORIZONTAL); 1188 event.SetEventObject(m_win); 1189 m_win->GetEventHandler()->ProcessEvent(event); 1190 } 1191 1192 if ( m_yScrollPosition != yScrollOld ) 1193 { 1194 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_yScrollPosition, 1195 wxVERTICAL); 1196 event.SetEventObject(m_win); 1197 m_win->GetEventHandler()->ProcessEvent(event); 1198 } 1199} 1200 1201// ---------------------------------------------------------------------------- 1202// autoscroll stuff: these functions deal with sending fake scroll events when 1203// a captured mouse is being held outside the window 1204// ---------------------------------------------------------------------------- 1205 1206bool wxScrollHelper::SendAutoScrollEvents(wxScrollWinEvent& event) const 1207{ 1208 // only send the event if the window is scrollable in this direction 1209 wxWindow *win = (wxWindow *)event.GetEventObject(); 1210 return win->HasScrollbar(event.GetOrientation()); 1211} 1212 1213void wxScrollHelper::StopAutoScrolling() 1214{ 1215#if wxUSE_TIMER 1216 if ( m_timerAutoScroll ) 1217 { 1218 delete m_timerAutoScroll; 1219 m_timerAutoScroll = (wxTimer *)NULL; 1220 } 1221#endif 1222} 1223 1224void wxScrollHelper::HandleOnMouseEnter(wxMouseEvent& event) 1225{ 1226 StopAutoScrolling(); 1227 1228 event.Skip(); 1229} 1230 1231void wxScrollHelper::HandleOnMouseLeave(wxMouseEvent& event) 1232{ 1233 // don't prevent the usual processing of the event from taking place 1234 event.Skip(); 1235 1236 // when a captured mouse leave a scrolled window we start generate 1237 // scrolling events to allow, for example, extending selection beyond the 1238 // visible area in some controls 1239 if ( wxWindow::GetCapture() == m_targetWindow ) 1240 { 1241 // where is the mouse leaving? 1242 int pos, orient; 1243 wxPoint pt = event.GetPosition(); 1244 if ( pt.x < 0 ) 1245 { 1246 orient = wxHORIZONTAL; 1247 pos = 0; 1248 } 1249 else if ( pt.y < 0 ) 1250 { 1251 orient = wxVERTICAL; 1252 pos = 0; 1253 } 1254 else // we're lower or to the right of the window 1255 { 1256 wxSize size = m_targetWindow->GetClientSize(); 1257 if ( pt.x > size.x ) 1258 { 1259 orient = wxHORIZONTAL; 1260 pos = m_xScrollLines; 1261 } 1262 else if ( pt.y > size.y ) 1263 { 1264 orient = wxVERTICAL; 1265 pos = m_yScrollLines; 1266 } 1267 else // this should be impossible 1268 { 1269 // but seems to happen sometimes under wxMSW - maybe it's a bug 1270 // there but for now just ignore it 1271 1272 //wxFAIL_MSG( _T("can't understand where has mouse gone") ); 1273 1274 return; 1275 } 1276 } 1277 1278 // only start the auto scroll timer if the window can be scrolled in 1279 // this direction 1280 if ( !m_targetWindow->HasScrollbar(orient) ) 1281 return; 1282 1283#if wxUSE_TIMER 1284 delete m_timerAutoScroll; 1285 m_timerAutoScroll = new wxAutoScrollTimer 1286 ( 1287 m_targetWindow, this, 1288 pos == 0 ? wxEVT_SCROLLWIN_LINEUP 1289 : wxEVT_SCROLLWIN_LINEDOWN, 1290 pos, 1291 orient 1292 ); 1293 m_timerAutoScroll->Start(50); // FIXME: make configurable 1294#else 1295 wxUnusedVar(pos); 1296#endif 1297 } 1298} 1299 1300#if wxUSE_MOUSEWHEEL 1301 1302void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event) 1303{ 1304 m_wheelRotation += event.GetWheelRotation(); 1305 int lines = m_wheelRotation / event.GetWheelDelta(); 1306 m_wheelRotation -= lines * event.GetWheelDelta(); 1307 1308 if (lines != 0) 1309 { 1310 1311 wxScrollWinEvent newEvent; 1312 1313 newEvent.SetPosition(0); 1314 newEvent.SetOrientation(wxVERTICAL); 1315 newEvent.SetEventObject(m_win); 1316 1317 if (event.IsPageScroll()) 1318 { 1319 if (lines > 0) 1320 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEUP); 1321 else 1322 newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN); 1323 1324 m_win->GetEventHandler()->ProcessEvent(newEvent); 1325 } 1326 else 1327 { 1328 lines *= event.GetLinesPerAction(); 1329 if (lines > 0) 1330 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEUP); 1331 else 1332 newEvent.SetEventType(wxEVT_SCROLLWIN_LINEDOWN); 1333 1334 int times = abs(lines); 1335 for (; times > 0; times--) 1336 m_win->GetEventHandler()->ProcessEvent(newEvent); 1337 } 1338 } 1339} 1340 1341#endif // wxUSE_MOUSEWHEEL 1342 1343void wxScrollHelper::HandleOnChildFocus(wxChildFocusEvent& event) 1344{ 1345 // this event should be processed by all windows in parenthood chain, 1346 // e.g. so that nested wxScrolledWindows work correctly 1347 event.Skip(); 1348 1349 // find the immediate child under which the window receiving focus is: 1350 wxWindow *win = event.GetWindow(); 1351 1352 if ( win == m_targetWindow ) 1353 return; // nothing to do 1354 1355#ifdef __WXMAC__ 1356 if (wxDynamicCast(win, wxScrollBar)) 1357 return; 1358#endif 1359 1360 // Fixing ticket: http://trac.wxwidgets.org/ticket/9563 1361 // When a child inside a wxControlContainer receives a focus, the 1362 // wxControlContainer generates an artificial wxChildFocusEvent for 1363 // itself, telling its parent that 'it' received the focus. The effect is 1364 // that this->HandleOnChildFocus is called twice, first with the 1365 // artificial wxChildFocusEvent and then with the original event. We need 1366 // to ignore the artificial event here or otherwise HandleOnChildFocus 1367 // would first scroll the target window to make the entire 1368 // wxControlContainer visible and immediately afterwards scroll the target 1369 // window again to make the child widget visible. This leads to ugly 1370 // flickering when using nested wxPanels/wxScrolledWindows. 1371 // 1372 // Ignore this event if 'win' is derived from wxControlContainer AND its 1373 // parent is the m_targetWindow AND 'win' is not actually reciving the 1374 // focus (win != FindFocus). TODO: This affects all wxControlContainer 1375 // objects, but wxControlContainer is not part of the wxWidgets RTTI and 1376 // so wxDynamicCast(win, wxControlContainer) does not compile. Find a way 1377 // to determine if 'win' derives from wxControlContainer. Until then, 1378 // testing if 'win' derives from wxPanel will probably get >90% of all 1379 // cases. 1380 1381 wxWindow *actual_focus=wxWindow::FindFocus(); 1382 if (win != actual_focus && 1383 wxDynamicCast(win, wxPanel) != 0 && 1384 win->GetParent() == m_targetWindow) 1385 // if win is a wxPanel and receives the focus, it should not be 1386 // scrolled into view 1387 return; 1388 1389 const wxRect viewRect(m_targetWindow->GetClientRect()); 1390 1391 // For composite controls such as wxComboCtrl we should try to fit the 1392 // entire control inside the visible area of the target window, not just 1393 // the focused child of the control. Otherwise we'd make only the textctrl 1394 // part of a wxComboCtrl visible and the button would still be outside the 1395 // scrolled area. But do so only if the parent fits *entirely* inside the 1396 // scrolled window. In other situations, such as nested wxPanel or 1397 // wxScrolledWindows, the parent might be way to big to fit inside the 1398 // scrolled window. If that is the case, then make only the focused window 1399 // visible 1400 if ( win->GetParent() != m_targetWindow) 1401 { 1402 wxWindow *parent=win->GetParent(); 1403 wxSize parent_size=parent->GetSize(); 1404 if (parent_size.GetWidth() <= viewRect.GetWidth() && 1405 parent_size.GetHeight() <= viewRect.GetHeight()) 1406 // make the immediate parent visible instead of the focused control 1407 win=parent; 1408 } 1409 1410 // make win position relative to the m_targetWindow viewing area instead of 1411 // its parent 1412 const wxRect 1413 winRect(m_targetWindow->ScreenToClient(win->GetScreenPosition()), 1414 win->GetSize()); 1415 1416 // check if it's fully visible 1417 if ( viewRect.Contains(winRect) ) 1418 { 1419 // it is, nothing to do 1420 return; 1421 } 1422 1423 // check if we can make it fully visible: this is only possible if it's not 1424 // larger than our view area 1425 if ( winRect.GetWidth() > viewRect.GetWidth() || 1426 winRect.GetHeight() > viewRect.GetHeight() ) 1427 { 1428 // we can't make it fit so avoid scrolling it at all, this is only 1429 // going to be confusing and not helpful 1430 return; 1431 } 1432 1433 1434 // do make the window fit inside the view area by scrolling to it 1435 int stepx, stepy; 1436 GetScrollPixelsPerUnit(&stepx, &stepy); 1437 1438 int startx, starty; 1439 GetViewStart(&startx, &starty); 1440 1441 // first in vertical direction: 1442 if ( stepy > 0 ) 1443 { 1444 int diff = 0; 1445 1446 if ( winRect.GetTop() < 0 ) 1447 { 1448 diff = winRect.GetTop(); 1449 } 1450 else if ( winRect.GetBottom() > viewRect.GetHeight() ) 1451 { 1452 diff = winRect.GetBottom() - viewRect.GetHeight() + 1; 1453 // round up to next scroll step if we can't get exact position, 1454 // so that the window is fully visible: 1455 diff += stepy - 1; 1456 } 1457 1458 starty = (starty * stepy + diff) / stepy; 1459 } 1460 1461 // then horizontal: 1462 if ( stepx > 0 ) 1463 { 1464 int diff = 0; 1465 1466 if ( winRect.GetLeft() < 0 ) 1467 { 1468 diff = winRect.GetLeft(); 1469 } 1470 else if ( winRect.GetRight() > viewRect.GetWidth() ) 1471 { 1472 diff = winRect.GetRight() - viewRect.GetWidth() + 1; 1473 // round up to next scroll step if we can't get exact position, 1474 // so that the window is fully visible: 1475 diff += stepx - 1; 1476 } 1477 1478 startx = (startx * stepx + diff) / stepx; 1479 } 1480 1481 Scroll(startx, starty); 1482} 1483 1484// ---------------------------------------------------------------------------- 1485// wxScrolledWindow implementation 1486// ---------------------------------------------------------------------------- 1487 1488IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel) 1489 1490BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel) 1491 EVT_PAINT(wxScrolledWindow::OnPaint) 1492END_EVENT_TABLE() 1493 1494bool wxScrolledWindow::Create(wxWindow *parent, 1495 wxWindowID id, 1496 const wxPoint& pos, 1497 const wxSize& size, 1498 long style, 1499 const wxString& name) 1500{ 1501 m_targetWindow = this; 1502#ifdef __WXMAC__ 1503 MacSetClipChildren( true ) ; 1504#endif 1505 1506 bool ok = wxPanel::Create(parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name); 1507 1508 return ok; 1509} 1510 1511wxScrolledWindow::~wxScrolledWindow() 1512{ 1513} 1514 1515void wxScrolledWindow::OnPaint(wxPaintEvent& event) 1516{ 1517 // the user code didn't really draw the window if we got here, so set this 1518 // flag to try to call OnDraw() later 1519 m_handler->ResetDrawnFlag(); 1520 1521 event.Skip(); 1522} 1523 1524#ifdef __WXMSW__ 1525WXLRESULT wxScrolledWindow::MSWWindowProc(WXUINT nMsg, 1526 WXWPARAM wParam, 1527 WXLPARAM lParam) 1528{ 1529 WXLRESULT rc = wxPanel::MSWWindowProc(nMsg, wParam, lParam); 1530 1531#ifndef __WXWINCE__ 1532 // we need to process arrows ourselves for scrolling 1533 if ( nMsg == WM_GETDLGCODE ) 1534 { 1535 rc |= DLGC_WANTARROWS; 1536 } 1537#endif 1538 1539 return rc; 1540} 1541 1542#endif // __WXMSW__ 1543