1 /* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "ScrollView.h" 28 29 #include "GraphicsContext.h" 30 #include "HostWindow.h" 31 #include "PlatformMouseEvent.h" 32 #include "PlatformWheelEvent.h" 33 #include "Scrollbar.h" 34 #include "ScrollbarTheme.h" 35 #include <wtf/StdLibExtras.h> 36 37 using std::max; 38 39 namespace WebCore { 40 41 ScrollView::ScrollView() 42 : m_horizontalScrollbarMode(ScrollbarAuto) 43 , m_verticalScrollbarMode(ScrollbarAuto) 44 , m_prohibitsScrolling(false) 45 , m_canBlitOnScroll(true) 46 , m_scrollbarsAvoidingResizer(0) 47 , m_scrollbarsSuppressed(false) 48 , m_inUpdateScrollbars(false) 49 , m_updateScrollbarsPass(0) 50 , m_drawPanScrollIcon(false) 51 , m_useFixedLayout(false) 52 , m_paintsEntireContents(false) 53 { 54 platformInit(); 55 } 56 57 ScrollView::~ScrollView() 58 { 59 platformDestroy(); 60 } 61 62 void ScrollView::addChild(PassRefPtr<Widget> prpChild) 63 { 64 Widget* child = prpChild.get(); 65 ASSERT(child != this && !child->parent()); 66 child->setParent(this); 67 m_children.add(prpChild); 68 if (child->platformWidget()) 69 platformAddChild(child); 70 } 71 72 void ScrollView::removeChild(Widget* child) 73 { 74 ASSERT(child->parent() == this); 75 child->setParent(0); 76 m_children.remove(child); 77 if (child->platformWidget()) 78 platformRemoveChild(child); 79 } 80 81 void ScrollView::setHasHorizontalScrollbar(bool hasBar) 82 { 83 if (hasBar && !m_horizontalScrollbar) { 84 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar); 85 addChild(m_horizontalScrollbar.get()); 86 m_horizontalScrollbar->styleChanged(); 87 } else if (!hasBar && m_horizontalScrollbar) { 88 removeChild(m_horizontalScrollbar.get()); 89 m_horizontalScrollbar = 0; 90 } 91 } 92 93 void ScrollView::setHasVerticalScrollbar(bool hasBar) 94 { 95 if (hasBar && !m_verticalScrollbar) { 96 m_verticalScrollbar = createScrollbar(VerticalScrollbar); 97 addChild(m_verticalScrollbar.get()); 98 m_verticalScrollbar->styleChanged(); 99 } else if (!hasBar && m_verticalScrollbar) { 100 removeChild(m_verticalScrollbar.get()); 101 m_verticalScrollbar = 0; 102 } 103 } 104 105 #if !PLATFORM(GTK) 106 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) 107 { 108 return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); 109 } 110 111 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode) 112 { 113 if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode()) 114 return; 115 m_horizontalScrollbarMode = horizontalMode; 116 m_verticalScrollbarMode = verticalMode; 117 if (platformWidget()) 118 platformSetScrollbarModes(); 119 else 120 updateScrollbars(scrollOffset()); 121 } 122 #endif 123 124 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const 125 { 126 if (platformWidget()) { 127 platformScrollbarModes(horizontalMode, verticalMode); 128 return; 129 } 130 horizontalMode = m_horizontalScrollbarMode; 131 verticalMode = m_verticalScrollbarMode; 132 } 133 134 void ScrollView::setCanHaveScrollbars(bool canScroll) 135 { 136 ScrollbarMode newHorizontalMode; 137 ScrollbarMode newVerticalMode; 138 139 scrollbarModes(newHorizontalMode, newVerticalMode); 140 141 if (canScroll && newVerticalMode == ScrollbarAlwaysOff) 142 newVerticalMode = ScrollbarAuto; 143 else if (!canScroll) 144 newVerticalMode = ScrollbarAlwaysOff; 145 146 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff) 147 newHorizontalMode = ScrollbarAuto; 148 else if (!canScroll) 149 newHorizontalMode = ScrollbarAlwaysOff; 150 151 setScrollbarModes(newHorizontalMode, newVerticalMode); 152 } 153 154 void ScrollView::setCanBlitOnScroll(bool b) 155 { 156 if (platformWidget()) { 157 platformSetCanBlitOnScroll(b); 158 return; 159 } 160 161 m_canBlitOnScroll = b; 162 } 163 164 bool ScrollView::canBlitOnScroll() const 165 { 166 if (platformWidget()) 167 return platformCanBlitOnScroll(); 168 169 return m_canBlitOnScroll; 170 } 171 172 void ScrollView::setPaintsEntireContents(bool paintsEntireContents) 173 { 174 m_paintsEntireContents = paintsEntireContents; 175 } 176 177 #if !PLATFORM(GTK) 178 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const 179 { 180 if (platformWidget()) 181 return platformVisibleContentRect(includeScrollbars); 182 return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), 183 IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), 184 max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0)))); 185 } 186 #endif 187 188 int ScrollView::layoutWidth() const 189 { 190 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width(); 191 } 192 193 int ScrollView::layoutHeight() const 194 { 195 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height(); 196 } 197 198 IntSize ScrollView::fixedLayoutSize() const 199 { 200 return m_fixedLayoutSize; 201 } 202 203 void ScrollView::setFixedLayoutSize(const IntSize& newSize) 204 { 205 if (fixedLayoutSize() == newSize) 206 return; 207 m_fixedLayoutSize = newSize; 208 updateScrollbars(scrollOffset()); 209 } 210 211 bool ScrollView::useFixedLayout() const 212 { 213 return m_useFixedLayout; 214 } 215 216 void ScrollView::setUseFixedLayout(bool enable) 217 { 218 if (useFixedLayout() == enable) 219 return; 220 m_useFixedLayout = enable; 221 updateScrollbars(scrollOffset()); 222 } 223 224 IntSize ScrollView::contentsSize() const 225 { 226 if (platformWidget()) 227 return platformContentsSize(); 228 return m_contentsSize; 229 } 230 231 void ScrollView::setContentsSize(const IntSize& newSize) 232 { 233 if (contentsSize() == newSize) 234 return; 235 m_contentsSize = newSize; 236 if (platformWidget()) 237 platformSetContentsSize(); 238 else 239 updateScrollbars(scrollOffset()); 240 } 241 242 IntPoint ScrollView::maximumScrollPosition() const 243 { 244 IntSize maximumOffset = contentsSize() - visibleContentRect().size(); 245 maximumOffset.clampNegativeToZero(); 246 return IntPoint(maximumOffset.width(), maximumOffset.height()); 247 } 248 249 void ScrollView::valueChanged(Scrollbar* scrollbar) 250 { 251 // Figure out if we really moved. 252 IntSize newOffset = m_scrollOffset; 253 if (scrollbar) { 254 if (scrollbar->orientation() == HorizontalScrollbar) 255 newOffset.setWidth(scrollbar->value()); 256 else if (scrollbar->orientation() == VerticalScrollbar) 257 newOffset.setHeight(scrollbar->value()); 258 } 259 260 IntSize scrollDelta = newOffset - m_scrollOffset; 261 if (scrollDelta == IntSize()) 262 return; 263 m_scrollOffset = newOffset; 264 265 if (scrollbarsSuppressed()) 266 return; 267 268 scrollContents(scrollDelta); 269 } 270 271 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r) 272 { 273 #if PLATFORM(ANDROID) 274 if (platformProhibitsScrolling()) 275 return; 276 #endif 277 // FIXME: This method is not transform-aware. It should just be moved to FrameView so that an accurate 278 // position for the child view can be determined. 279 IntRect rect = r; 280 ScrollView* view = this; 281 while (view) { 282 if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling. 283 return; 284 view->setScrollPosition(rect.location()); 285 rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height()); 286 if (view->parent()) 287 rect.intersect(view->frameRect()); 288 view = view->parent(); 289 } 290 291 // We may be embedded inside some containing platform scroll view that we don't manage. This is the case 292 // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView 293 // that contains both it and message headers. Let the HostWindow know about this scroll so that it can pass the message 294 // on up the view chain. 295 // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to 296 // expose the headers. 297 if (hostWindow()) 298 hostWindow()->scrollRectIntoView(rect, this); 299 } 300 301 void ScrollView::setScrollPosition(const IntPoint& scrollPoint) 302 { 303 if (prohibitsScrolling()) 304 return; 305 306 if (platformWidget()) { 307 platformSetScrollPosition(scrollPoint); 308 return; 309 } 310 311 IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition()); 312 newScrollPosition.clampNegativeToZero(); 313 314 if (newScrollPosition == scrollPosition()) 315 return; 316 317 updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y())); 318 } 319 320 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity) 321 { 322 if (platformWidget()) 323 return platformScroll(direction, granularity); 324 325 if (direction == ScrollUp || direction == ScrollDown) { 326 if (m_verticalScrollbar) 327 return m_verticalScrollbar->scroll(direction, granularity); 328 } else { 329 if (m_horizontalScrollbar) 330 return m_horizontalScrollbar->scroll(direction, granularity); 331 } 332 return false; 333 } 334 335 static const unsigned cMaxUpdateScrollbarsPass = 2; 336 337 void ScrollView::updateScrollbars(const IntSize& desiredOffset) 338 { 339 if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget()) 340 return; 341 342 // If we came in here with the view already needing a layout, then go ahead and do that 343 // first. (This will be the common case, e.g., when the page changes due to window resizing for example). 344 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total. 345 if (!m_scrollbarsSuppressed) { 346 m_inUpdateScrollbars = true; 347 visibleContentsResized(); 348 m_inUpdateScrollbars = false; 349 } 350 351 bool hasHorizontalScrollbar = m_horizontalScrollbar; 352 bool hasVerticalScrollbar = m_verticalScrollbar; 353 354 bool newHasHorizontalScrollbar = hasHorizontalScrollbar; 355 bool newHasVerticalScrollbar = hasVerticalScrollbar; 356 357 ScrollbarMode hScroll = m_horizontalScrollbarMode; 358 ScrollbarMode vScroll = m_verticalScrollbarMode; 359 360 if (hScroll != ScrollbarAuto) 361 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn); 362 if (vScroll != ScrollbarAuto) 363 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn); 364 365 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) { 366 if (hasHorizontalScrollbar != newHasHorizontalScrollbar) 367 setHasHorizontalScrollbar(newHasHorizontalScrollbar); 368 if (hasVerticalScrollbar != newHasVerticalScrollbar) 369 setHasVerticalScrollbar(newHasVerticalScrollbar); 370 } else { 371 bool sendContentResizedNotification = false; 372 373 IntSize docSize = contentsSize(); 374 IntSize frameSize = frameRect().size(); 375 376 if (hScroll == ScrollbarAuto) { 377 newHasHorizontalScrollbar = docSize.width() > visibleWidth(); 378 if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height()) 379 newHasHorizontalScrollbar = false; 380 } 381 if (vScroll == ScrollbarAuto) { 382 newHasVerticalScrollbar = docSize.height() > visibleHeight(); 383 if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height()) 384 newHasVerticalScrollbar = false; 385 } 386 387 // If we ever turn one scrollbar off, always turn the other one off too. Never ever 388 // try to both gain/lose a scrollbar in the same pass. 389 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn) 390 newHasVerticalScrollbar = false; 391 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn) 392 newHasHorizontalScrollbar = false; 393 394 if (hasHorizontalScrollbar != newHasHorizontalScrollbar) { 395 setHasHorizontalScrollbar(newHasHorizontalScrollbar); 396 sendContentResizedNotification = true; 397 } 398 399 if (hasVerticalScrollbar != newHasVerticalScrollbar) { 400 setHasVerticalScrollbar(newHasVerticalScrollbar); 401 sendContentResizedNotification = true; 402 } 403 404 if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) { 405 m_updateScrollbarsPass++; 406 contentsResized(); 407 visibleContentsResized(); 408 IntSize newDocSize = contentsSize(); 409 if (newDocSize == docSize) { 410 // The layout with the new scroll state had no impact on 411 // the document's overall size, so updateScrollbars didn't get called. 412 // Recur manually. 413 updateScrollbars(desiredOffset); 414 } 415 m_updateScrollbarsPass--; 416 } 417 } 418 419 // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid 420 // doing it multiple times). 421 if (m_updateScrollbarsPass) 422 return; 423 424 m_inUpdateScrollbars = true; 425 IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight()); 426 IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition); 427 scroll.clampNegativeToZero(); 428 429 if (m_horizontalScrollbar) { 430 int clientWidth = visibleWidth(); 431 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth); 432 int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1); 433 IntRect oldRect(m_horizontalScrollbar->frameRect()); 434 IntRect hBarRect = IntRect(0, 435 height() - m_horizontalScrollbar->height(), 436 width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0), 437 m_horizontalScrollbar->height()); 438 m_horizontalScrollbar->setFrameRect(hBarRect); 439 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect()) 440 m_horizontalScrollbar->invalidate(); 441 442 if (m_scrollbarsSuppressed) 443 m_horizontalScrollbar->setSuppressInvalidation(true); 444 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); 445 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth()); 446 m_horizontalScrollbar->setValue(scroll.width()); 447 if (m_scrollbarsSuppressed) 448 m_horizontalScrollbar->setSuppressInvalidation(false); 449 } 450 451 if (m_verticalScrollbar) { 452 int clientHeight = visibleHeight(); 453 m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight); 454 int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1); 455 if (pageStep < 0) 456 pageStep = clientHeight; 457 IntRect oldRect(m_verticalScrollbar->frameRect()); 458 IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 459 0, 460 m_verticalScrollbar->width(), 461 height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0)); 462 m_verticalScrollbar->setFrameRect(vBarRect); 463 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect()) 464 m_verticalScrollbar->invalidate(); 465 466 if (m_scrollbarsSuppressed) 467 m_verticalScrollbar->setSuppressInvalidation(true); 468 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); 469 m_verticalScrollbar->setProportion(clientHeight, contentsHeight()); 470 m_verticalScrollbar->setValue(scroll.height()); 471 if (m_scrollbarsSuppressed) 472 m_verticalScrollbar->setSuppressInvalidation(false); 473 } 474 475 if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) { 476 frameRectsChanged(); 477 updateScrollCorner(); 478 } 479 480 // See if our offset has changed in a situation where we might not have scrollbars. 481 // This can happen when editing a body with overflow:hidden and scrolling to reveal selection. 482 // It can also happen when maximizing a window that has scrollbars (but the new maximized result 483 // does not). 484 IntSize scrollDelta = scroll - m_scrollOffset; 485 if (scrollDelta != IntSize()) { 486 m_scrollOffset = scroll; 487 scrollContents(scrollDelta); 488 } 489 490 m_inUpdateScrollbars = false; 491 } 492 493 const int panIconSizeLength = 16; 494 495 void ScrollView::scrollContents(const IntSize& scrollDelta) 496 { 497 if (!hostWindow()) 498 return; 499 500 // Since scrolling is double buffered, we will be blitting the scroll view's intersection 501 // with the clip rect every time to keep it smooth. 502 IntRect clipRect = windowClipRect(); 503 IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight())); 504 IntRect updateRect = clipRect; 505 updateRect.intersect(scrollViewRect); 506 507 // Invalidate the window (not the backing store). 508 hostWindow()->repaint(updateRect, false); 509 510 if (m_drawPanScrollIcon) { 511 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary 512 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2)); 513 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength)); 514 panScrollIconDirtyRect.intersect(clipRect); 515 hostWindow()->repaint(panScrollIconDirtyRect, true); 516 } 517 518 if (canBlitOnScroll()) { // The main frame can just blit the WebView window 519 // FIXME: Find a way to blit subframes without blitting overlapping content 520 hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect); 521 } else { 522 // We need to go ahead and repaint the entire backing store. Do it now before moving the 523 // windowed plugins. 524 hostWindow()->repaint(updateRect, true, false, true); // Invalidate the backing store and repaint it synchronously 525 } 526 527 // This call will move children with native widgets (plugins) and invalidate them as well. 528 frameRectsChanged(); 529 530 // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should 531 // be very fast). 532 hostWindow()->paint(); 533 } 534 535 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const 536 { 537 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 538 return viewPoint + scrollOffset(); 539 } 540 541 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const 542 { 543 IntPoint viewPoint = contentsPoint - scrollOffset(); 544 return convertToContainingWindow(viewPoint); 545 } 546 547 IntRect ScrollView::windowToContents(const IntRect& windowRect) const 548 { 549 IntRect viewRect = convertFromContainingWindow(windowRect); 550 viewRect.move(scrollOffset()); 551 return viewRect; 552 } 553 554 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const 555 { 556 IntRect viewRect = contentsRect; 557 viewRect.move(-scrollOffset()); 558 return convertToContainingWindow(viewRect); 559 } 560 561 IntRect ScrollView::contentsToScreen(const IntRect& rect) const 562 { 563 if (platformWidget()) 564 return platformContentsToScreen(rect); 565 if (!hostWindow()) 566 return IntRect(); 567 return hostWindow()->windowToScreen(contentsToWindow(rect)); 568 } 569 570 IntPoint ScrollView::screenToContents(const IntPoint& point) const 571 { 572 if (platformWidget()) 573 return platformScreenToContents(point); 574 if (!hostWindow()) 575 return IntPoint(); 576 return windowToContents(hostWindow()->screenToWindow(point)); 577 } 578 579 bool ScrollView::containsScrollbarsAvoidingResizer() const 580 { 581 return !m_scrollbarsAvoidingResizer; 582 } 583 584 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta) 585 { 586 int oldCount = m_scrollbarsAvoidingResizer; 587 m_scrollbarsAvoidingResizer += overlapDelta; 588 if (parent()) 589 parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta); 590 else if (!scrollbarsSuppressed()) { 591 // If we went from n to 0 or from 0 to n and we're the outermost view, 592 // we need to invalidate the windowResizerRect(), since it will now need to paint 593 // differently. 594 if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) || 595 (oldCount == 0 && m_scrollbarsAvoidingResizer > 0)) 596 invalidateRect(windowResizerRect()); 597 } 598 } 599 600 void ScrollView::setParent(ScrollView* parentView) 601 { 602 if (parentView == parent()) 603 return; 604 605 if (m_scrollbarsAvoidingResizer && parent()) 606 parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer); 607 608 Widget::setParent(parentView); 609 610 if (m_scrollbarsAvoidingResizer && parent()) 611 parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer); 612 } 613 614 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress) 615 { 616 if (suppressed == m_scrollbarsSuppressed) 617 return; 618 619 m_scrollbarsSuppressed = suppressed; 620 621 if (platformWidget()) 622 platformSetScrollbarsSuppressed(repaintOnUnsuppress); 623 else if (repaintOnUnsuppress && !suppressed) { 624 if (m_horizontalScrollbar) 625 m_horizontalScrollbar->invalidate(); 626 if (m_verticalScrollbar) 627 m_verticalScrollbar->invalidate(); 628 629 // Invalidate the scroll corner too on unsuppress. 630 invalidateRect(scrollCornerRect()); 631 } 632 } 633 634 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint) 635 { 636 if (platformWidget()) 637 return 0; 638 639 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 640 if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint)) 641 return m_horizontalScrollbar.get(); 642 if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint)) 643 return m_verticalScrollbar.get(); 644 return 0; 645 } 646 647 void ScrollView::wheelEvent(PlatformWheelEvent& e) 648 { 649 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled. 650 #if PLATFORM(WX) 651 if (!canHaveScrollbars()) { 652 #else 653 if (!canHaveScrollbars() || platformWidget()) { 654 #endif 655 return; 656 } 657 658 // Determine how much we want to scroll. If we can move at all, we will accept the event. 659 IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition(); 660 if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) || 661 (e.deltaX() > 0 && scrollOffset().width() > 0) || 662 (e.deltaY() < 0 && maxScrollDelta.height() > 0) || 663 (e.deltaY() > 0 && scrollOffset().height() > 0)) { 664 e.accept(); 665 float deltaX = e.deltaX(); 666 float deltaY = e.deltaY(); 667 if (e.granularity() == ScrollByPageWheelEvent) { 668 ASSERT(deltaX == 0); 669 bool negative = deltaY < 0; 670 deltaY = max(max<int>(visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), visibleHeight() - Scrollbar::maxOverlapBetweenPages()), 1); 671 if (negative) 672 deltaY = -deltaY; 673 } 674 scrollBy(IntSize(-deltaX, -deltaY)); 675 } 676 } 677 678 void ScrollView::setFrameRect(const IntRect& newRect) 679 { 680 IntRect oldRect = frameRect(); 681 682 if (newRect == oldRect) 683 return; 684 685 Widget::setFrameRect(newRect); 686 687 if (platformWidget()) 688 return; 689 690 if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) { 691 updateScrollbars(m_scrollOffset); 692 contentsResized(); 693 } 694 695 frameRectsChanged(); 696 } 697 698 void ScrollView::frameRectsChanged() 699 { 700 if (platformWidget()) 701 return; 702 703 HashSet<RefPtr<Widget> >::const_iterator end = m_children.end(); 704 for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current) 705 (*current)->frameRectsChanged(); 706 } 707 708 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now) 709 { 710 IntRect paintRect = rect; 711 if (!paintsEntireContents()) 712 paintRect.intersect(visibleContentRect()); 713 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS 714 if (rect != paintRect) 715 platformOffscreenContentRectangle(visibleContentRect(), rect); 716 #endif 717 if (paintRect.isEmpty()) 718 return; 719 if (platformWidget()) { 720 platformRepaintContentRectangle(paintRect, now); 721 return; 722 } 723 724 if (hostWindow()) 725 hostWindow()->repaint(contentsToWindow(paintRect), true, now); 726 } 727 728 IntRect ScrollView::scrollCornerRect() const 729 { 730 IntRect cornerRect; 731 732 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) { 733 cornerRect.unite(IntRect(m_horizontalScrollbar->width(), 734 height() - m_horizontalScrollbar->height(), 735 width() - m_horizontalScrollbar->width(), 736 m_horizontalScrollbar->height())); 737 } 738 739 if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) { 740 cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(), 741 m_verticalScrollbar->height(), 742 m_verticalScrollbar->width(), 743 height() - m_verticalScrollbar->height())); 744 } 745 746 return cornerRect; 747 } 748 749 void ScrollView::updateScrollCorner() 750 { 751 } 752 753 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect) 754 { 755 ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect); 756 } 757 758 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect) 759 { 760 if (m_horizontalScrollbar) 761 m_horizontalScrollbar->paint(context, rect); 762 if (m_verticalScrollbar) 763 m_verticalScrollbar->paint(context, rect); 764 765 paintScrollCorner(context, scrollCornerRect()); 766 } 767 768 void ScrollView::paintPanScrollIcon(GraphicsContext* context) 769 { 770 DEFINE_STATIC_LOCAL(Image*, panScrollIcon, (Image::loadPlatformResource("panIcon").releaseRef())); 771 context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint); 772 } 773 774 void ScrollView::paint(GraphicsContext* context, const IntRect& rect) 775 { 776 if (platformWidget()) { 777 Widget::paint(context, rect); 778 return; 779 } 780 781 if (context->paintingDisabled() && !context->updatingControlTints()) 782 return; 783 784 IntRect documentDirtyRect = rect; 785 documentDirtyRect.intersect(frameRect()); 786 787 context->save(); 788 789 context->translate(x(), y()); 790 documentDirtyRect.move(-x(), -y()); 791 792 context->translate(-scrollX(), -scrollY()); 793 documentDirtyRect.move(scrollX(), scrollY()); 794 795 context->clip(visibleContentRect()); 796 797 paintContents(context, documentDirtyRect); 798 799 context->restore(); 800 801 // Now paint the scrollbars. 802 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) { 803 context->save(); 804 IntRect scrollViewDirtyRect = rect; 805 scrollViewDirtyRect.intersect(frameRect()); 806 context->translate(x(), y()); 807 scrollViewDirtyRect.move(-x(), -y()); 808 809 paintScrollbars(context, scrollViewDirtyRect); 810 811 context->restore(); 812 } 813 814 // Paint the panScroll Icon 815 if (m_drawPanScrollIcon) 816 paintPanScrollIcon(context); 817 } 818 819 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint) 820 { 821 if (!scrollbarCornerPresent()) 822 return false; 823 824 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 825 826 if (m_horizontalScrollbar) { 827 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y(); 828 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height(); 829 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width(); 830 831 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin; 832 } 833 834 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x(); 835 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width(); 836 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height(); 837 838 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin; 839 } 840 841 bool ScrollView::scrollbarCornerPresent() const 842 { 843 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) || 844 (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0); 845 } 846 847 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const 848 { 849 // Scrollbars won't be transformed within us 850 IntRect newRect = localRect; 851 newRect.move(scrollbar->x(), scrollbar->y()); 852 return newRect; 853 } 854 855 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const 856 { 857 IntRect newRect = parentRect; 858 // Scrollbars won't be transformed within us 859 newRect.move(-scrollbar->x(), -scrollbar->y()); 860 return newRect; 861 } 862 863 // FIXME: test these on windows 864 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const 865 { 866 // Scrollbars won't be transformed within us 867 IntPoint newPoint = localPoint; 868 newPoint.move(scrollbar->x(), scrollbar->y()); 869 return newPoint; 870 } 871 872 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const 873 { 874 IntPoint newPoint = parentPoint; 875 // Scrollbars won't be transformed within us 876 newPoint.move(-scrollbar->x(), -scrollbar->y()); 877 return newPoint; 878 } 879 880 void ScrollView::setParentVisible(bool visible) 881 { 882 if (isParentVisible() == visible) 883 return; 884 885 Widget::setParentVisible(visible); 886 887 if (!isSelfVisible()) 888 return; 889 890 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 891 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 892 (*it)->setParentVisible(visible); 893 } 894 895 void ScrollView::show() 896 { 897 if (!isSelfVisible()) { 898 setSelfVisible(true); 899 if (isParentVisible()) { 900 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 901 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 902 (*it)->setParentVisible(true); 903 } 904 } 905 906 Widget::show(); 907 } 908 909 void ScrollView::hide() 910 { 911 if (isSelfVisible()) { 912 if (isParentVisible()) { 913 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 914 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 915 (*it)->setParentVisible(false); 916 } 917 setSelfVisible(false); 918 } 919 920 Widget::hide(); 921 } 922 923 bool ScrollView::isOffscreen() const 924 { 925 if (platformWidget()) 926 return platformIsOffscreen(); 927 928 if (!isVisible()) 929 return true; 930 931 // FIXME: Add a HostWindow::isOffscreen method here. Since only Mac implements this method 932 // currently, we can add the method when the other platforms decide to implement this concept. 933 return false; 934 } 935 936 937 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition) 938 { 939 if (!hostWindow()) 940 return; 941 m_drawPanScrollIcon = true; 942 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ; 943 hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength,panIconSizeLength)), true, true); 944 } 945 946 void ScrollView::removePanScrollIcon() 947 { 948 if (!hostWindow()) 949 return; 950 m_drawPanScrollIcon = false; 951 hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true, true); 952 } 953 954 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) 955 956 void ScrollView::platformInit() 957 { 958 } 959 960 void ScrollView::platformDestroy() 961 { 962 } 963 964 #endif 965 966 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC) 967 968 void ScrollView::platformAddChild(Widget*) 969 { 970 } 971 972 void ScrollView::platformRemoveChild(Widget*) 973 { 974 } 975 976 #endif 977 978 #if !PLATFORM(MAC) 979 980 void ScrollView::platformSetScrollbarsSuppressed(bool) 981 { 982 } 983 984 #endif 985 986 #if !PLATFORM(MAC) && !PLATFORM(WX) 987 988 #if !PLATFORM(ANDROID) 989 void ScrollView::platformSetScrollbarModes() 990 { 991 } 992 993 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const 994 { 995 horizontal = ScrollbarAuto; 996 vertical = ScrollbarAuto; 997 } 998 #endif 999 1000 void ScrollView::platformSetCanBlitOnScroll(bool) 1001 { 1002 } 1003 1004 bool ScrollView::platformCanBlitOnScroll() const 1005 { 1006 return false; 1007 } 1008 1009 #if !PLATFORM(ANDROID) 1010 IntRect ScrollView::platformVisibleContentRect(bool) const 1011 { 1012 return IntRect(); 1013 } 1014 #endif 1015 1016 #if !PLATFORM(ANDROID) 1017 IntSize ScrollView::platformContentsSize() const 1018 { 1019 return IntSize(); 1020 } 1021 #endif 1022 1023 void ScrollView::platformSetContentsSize() 1024 { 1025 } 1026 1027 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const 1028 { 1029 return rect; 1030 } 1031 1032 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const 1033 { 1034 return point; 1035 } 1036 1037 #if !PLATFORM(ANDROID) 1038 void ScrollView::platformSetScrollPosition(const IntPoint&) 1039 { 1040 } 1041 #endif 1042 1043 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity) 1044 { 1045 return true; 1046 } 1047 1048 #if !PLATFORM(ANDROID) 1049 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/) 1050 { 1051 } 1052 1053 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS 1054 void ScrollView::platformOffscreenContentRectangle(const IntRect& ) 1055 { 1056 } 1057 #endif 1058 #endif 1059 1060 bool ScrollView::platformIsOffscreen() const 1061 { 1062 return false; 1063 } 1064 1065 #endif 1066 1067 } 1068 1069