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