4c01175388d7b0531d9deaa233c6a699b05a095c
[WebKit-https.git] / WebCore / platform / ScrollView.cpp
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
38 using std::max;
39
40 namespace WebCore {
41
42 ScrollView::ScrollView()
43     : m_horizontalScrollbarMode(ScrollbarAuto)
44     , m_verticalScrollbarMode(ScrollbarAuto)
45     , m_horizontalScrollbarLock(false)
46     , m_verticalScrollbarLock(false)
47     , m_prohibitsScrolling(false)
48     , m_canBlitOnScroll(true)
49     , m_scrollbarsAvoidingResizer(0)
50     , m_scrollbarsSuppressed(false)
51     , m_inUpdateScrollbars(false)
52     , m_updateScrollbarsPass(0)
53     , m_drawPanScrollIcon(false)
54     , m_useFixedLayout(false)
55     , m_paintsEntireContents(false)
56 {
57     platformInit();
58 }
59
60 ScrollView::~ScrollView()
61 {
62     platformDestroy();
63 }
64
65 void ScrollView::addChild(PassRefPtr<Widget> prpChild) 
66 {
67     Widget* child = prpChild.get();
68     ASSERT(child != this && !child->parent());
69     child->setParent(this);
70     m_children.add(prpChild);
71     if (child->platformWidget())
72         platformAddChild(child);
73 }
74
75 void ScrollView::removeChild(Widget* child)
76 {
77     ASSERT(child->parent() == this);
78     child->setParent(0);
79     m_children.remove(child);
80     if (child->platformWidget())
81         platformRemoveChild(child);
82 }
83
84 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
85 {
86     if (hasBar && avoidScrollbarCreation())
87         return;
88
89     if (hasBar && !m_horizontalScrollbar) {
90         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
91         addChild(m_horizontalScrollbar.get());
92         m_horizontalScrollbar->styleChanged();
93     } else if (!hasBar && m_horizontalScrollbar) {
94         removeChild(m_horizontalScrollbar.get());
95         m_horizontalScrollbar = 0;
96     }
97 }
98
99 void ScrollView::setHasVerticalScrollbar(bool hasBar)
100 {
101     if (hasBar && avoidScrollbarCreation())
102         return;
103
104     if (hasBar && !m_verticalScrollbar) {
105         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
106         addChild(m_verticalScrollbar.get());
107         m_verticalScrollbar->styleChanged();
108     } else if (!hasBar && m_verticalScrollbar) {
109         removeChild(m_verticalScrollbar.get());
110         m_verticalScrollbar = 0;
111     }
112 }
113
114 #if !PLATFORM(GTK)
115 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
116 {
117     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
118 }
119
120 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
121                                    bool horizontalLock, bool verticalLock)
122 {
123     bool needsUpdate = false;
124
125     if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
126         m_horizontalScrollbarMode = horizontalMode;
127         needsUpdate = true;
128     }
129
130     if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
131         m_verticalScrollbarMode = verticalMode;
132         needsUpdate = true;
133     }
134
135     if (horizontalLock)
136         setHorizontalScrollbarLock();
137
138     if (verticalLock)
139         setVerticalScrollbarLock();
140
141     if (!needsUpdate)
142         return;
143
144     if (platformWidget())
145         platformSetScrollbarModes();
146     else
147         updateScrollbars(scrollOffset());
148 }
149 #endif
150
151 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
152 {
153     if (platformWidget()) {
154         platformScrollbarModes(horizontalMode, verticalMode);
155         return;
156     }
157     horizontalMode = m_horizontalScrollbarMode;
158     verticalMode = m_verticalScrollbarMode;
159 }
160
161 void ScrollView::setCanHaveScrollbars(bool canScroll)
162 {
163     ScrollbarMode newHorizontalMode;
164     ScrollbarMode newVerticalMode;
165     
166     scrollbarModes(newHorizontalMode, newVerticalMode);
167     
168     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
169         newVerticalMode = ScrollbarAuto;
170     else if (!canScroll)
171         newVerticalMode = ScrollbarAlwaysOff;
172     
173     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
174         newHorizontalMode = ScrollbarAuto;
175     else if (!canScroll)
176         newHorizontalMode = ScrollbarAlwaysOff;
177     
178     setScrollbarModes(newHorizontalMode, newVerticalMode);
179 }
180
181 void ScrollView::setCanBlitOnScroll(bool b)
182 {
183     if (platformWidget()) {
184         platformSetCanBlitOnScroll(b);
185         return;
186     }
187
188     m_canBlitOnScroll = b;
189 }
190
191 bool ScrollView::canBlitOnScroll() const
192 {
193     if (platformWidget())
194         return platformCanBlitOnScroll();
195
196     return m_canBlitOnScroll;
197 }
198
199 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
200 {
201     m_paintsEntireContents = paintsEntireContents;
202 }
203
204 #if !PLATFORM(GTK)
205 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
206 {
207     if (platformWidget())
208         return platformVisibleContentRect(includeScrollbars);
209     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
210                    IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), 
211                            max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
212 }
213 #endif
214
215 int ScrollView::layoutWidth() const
216 {
217     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
218 }
219
220 int ScrollView::layoutHeight() const
221 {
222     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
223 }
224
225 IntSize ScrollView::fixedLayoutSize() const
226 {
227     return m_fixedLayoutSize;
228 }
229
230 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
231 {
232     if (fixedLayoutSize() == newSize)
233         return;
234     m_fixedLayoutSize = newSize;
235     updateScrollbars(scrollOffset());
236 }
237
238 bool ScrollView::useFixedLayout() const
239 {
240     return m_useFixedLayout;
241 }
242
243 void ScrollView::setUseFixedLayout(bool enable)
244 {
245     if (useFixedLayout() == enable)
246         return;
247     m_useFixedLayout = enable;
248     updateScrollbars(scrollOffset());
249 }
250
251 IntSize ScrollView::contentsSize() const
252 {
253     if (platformWidget())
254         return platformContentsSize();
255     return m_contentsSize;
256 }
257
258 void ScrollView::setContentsSize(const IntSize& newSize)
259 {
260     if (contentsSize() == newSize)
261         return;
262     m_contentsSize = newSize;
263     if (platformWidget())
264         platformSetContentsSize();
265     else
266         updateScrollbars(scrollOffset());
267 }
268
269 IntPoint ScrollView::maximumScrollPosition() const
270 {
271     IntSize maximumOffset = contentsSize() - visibleContentRect().size();
272     maximumOffset.clampNegativeToZero();
273     return IntPoint(maximumOffset.width(), maximumOffset.height());
274 }
275
276 void ScrollView::valueChanged(Scrollbar* scrollbar)
277 {
278     // Figure out if we really moved.
279     IntSize newOffset = m_scrollOffset;
280     if (scrollbar) {
281         if (scrollbar->orientation() == HorizontalScrollbar)
282             newOffset.setWidth(scrollbar->value());
283         else if (scrollbar->orientation() == VerticalScrollbar)
284             newOffset.setHeight(scrollbar->value());
285     }
286
287     IntSize scrollDelta = newOffset - m_scrollOffset;
288     if (scrollDelta == IntSize())
289         return;
290     m_scrollOffset = newOffset;
291
292     if (scrollbarsSuppressed())
293         return;
294
295     repaintFixedElementsAfterScrolling();
296     scrollContents(scrollDelta);
297 }
298
299 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
300 {
301     if (prohibitsScrolling())
302         return;
303
304     if (platformWidget()) {
305         platformSetScrollPosition(scrollPoint);
306         return;
307     }
308
309     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
310     newScrollPosition.clampNegativeToZero();
311
312     if (newScrollPosition == scrollPosition())
313         return;
314
315     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
316 }
317
318 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
319 {
320     if (platformWidget())
321         return platformScroll(direction, granularity);
322
323     if (direction == ScrollUp || direction == ScrollDown) {
324         if (m_verticalScrollbar)
325             return m_verticalScrollbar->scroll(direction, granularity);
326     } else {
327         if (m_horizontalScrollbar)
328             return m_horizontalScrollbar->scroll(direction, granularity);
329     }
330     return false;
331 }
332
333 static const unsigned cMaxUpdateScrollbarsPass = 2;
334
335 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
336 {
337     if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
338         return;
339
340     // If we came in here with the view already needing a layout, then go ahead and do that
341     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
342     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
343     if (!m_scrollbarsSuppressed) {
344         m_inUpdateScrollbars = true;
345         visibleContentsResized();
346         m_inUpdateScrollbars = false;
347     }
348
349     bool hasHorizontalScrollbar = m_horizontalScrollbar;
350     bool hasVerticalScrollbar = m_verticalScrollbar;
351     
352     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
353     bool newHasVerticalScrollbar = hasVerticalScrollbar;
354    
355     ScrollbarMode hScroll = m_horizontalScrollbarMode;
356     ScrollbarMode vScroll = m_verticalScrollbarMode;
357
358     if (hScroll != ScrollbarAuto)
359         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
360     if (vScroll != ScrollbarAuto)
361         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
362
363     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
364         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
365             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
366         if (hasVerticalScrollbar != newHasVerticalScrollbar)
367             setHasVerticalScrollbar(newHasVerticalScrollbar);
368     } else {
369         bool sendContentResizedNotification = false;
370         
371         IntSize docSize = contentsSize();
372         IntSize frameSize = frameRect().size();
373
374         if (hScroll == ScrollbarAuto) {
375             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
376             if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
377                 newHasHorizontalScrollbar = false;
378         }
379         if (vScroll == ScrollbarAuto) {
380             newHasVerticalScrollbar = docSize.height() > visibleHeight();
381             if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
382                 newHasVerticalScrollbar = false;
383         }
384
385         // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
386         // try to both gain/lose a scrollbar in the same pass.
387         if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
388             newHasVerticalScrollbar = false;
389         if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
390             newHasHorizontalScrollbar = false;
391
392         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
393             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
394             sendContentResizedNotification = true;
395         }
396
397         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
398             setHasVerticalScrollbar(newHasVerticalScrollbar);
399             sendContentResizedNotification = true;
400         }
401
402         if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
403             m_updateScrollbarsPass++;
404             contentsResized();
405             visibleContentsResized();
406             IntSize newDocSize = contentsSize();
407             if (newDocSize == docSize) {
408                 // The layout with the new scroll state had no impact on
409                 // the document's overall size, so updateScrollbars didn't get called.
410                 // Recur manually.
411                 updateScrollbars(desiredOffset);
412             }
413             m_updateScrollbarsPass--;
414         }
415     }
416     
417     // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
418     // doing it multiple times).
419     if (m_updateScrollbarsPass)
420         return;
421
422     m_inUpdateScrollbars = true;
423     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
424     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
425     scroll.clampNegativeToZero();
426  
427     if (m_horizontalScrollbar) {
428         int clientWidth = visibleWidth();
429         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
430         int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
431         IntRect oldRect(m_horizontalScrollbar->frameRect());
432         IntRect hBarRect = IntRect(0,
433                                    height() - m_horizontalScrollbar->height(),
434                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
435                                    m_horizontalScrollbar->height());
436         m_horizontalScrollbar->setFrameRect(hBarRect);
437         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
438             m_horizontalScrollbar->invalidate();
439
440         if (m_scrollbarsSuppressed)
441             m_horizontalScrollbar->setSuppressInvalidation(true);
442         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
443         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
444         m_horizontalScrollbar->setValue(scroll.width());
445         if (m_scrollbarsSuppressed)
446             m_horizontalScrollbar->setSuppressInvalidation(false); 
447     } 
448
449     if (m_verticalScrollbar) {
450         int clientHeight = visibleHeight();
451         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
452         int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
453         IntRect oldRect(m_verticalScrollbar->frameRect());
454         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 
455                                    0,
456                                    m_verticalScrollbar->width(),
457                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
458         m_verticalScrollbar->setFrameRect(vBarRect);
459         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
460             m_verticalScrollbar->invalidate();
461
462         if (m_scrollbarsSuppressed)
463             m_verticalScrollbar->setSuppressInvalidation(true);
464         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
465         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
466         m_verticalScrollbar->setValue(scroll.height());
467         if (m_scrollbarsSuppressed)
468             m_verticalScrollbar->setSuppressInvalidation(false);
469     }
470
471     if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
472         frameRectsChanged();
473         updateScrollCorner();
474     }
475
476     // See if our offset has changed in a situation where we might not have scrollbars.
477     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
478     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
479     // does not).
480     IntSize scrollDelta = scroll - m_scrollOffset;
481     if (scrollDelta != IntSize()) {
482        m_scrollOffset = scroll;
483        scrollContents(scrollDelta);
484     }
485
486     m_inUpdateScrollbars = false;
487 }
488
489 const int panIconSizeLength = 16;
490
491 void ScrollView::scrollContents(const IntSize& scrollDelta)
492 {
493     if (!hostWindow())
494         return;
495
496     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
497     // with the clip rect every time to keep it smooth.
498     IntRect clipRect = windowClipRect();
499     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
500     IntRect updateRect = clipRect;
501     updateRect.intersect(scrollViewRect);
502
503     // Invalidate the window (not the backing store).
504     hostWindow()->invalidateWindow(updateRect, false /*immediate*/);
505
506     if (m_drawPanScrollIcon) {
507         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
508         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
509         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
510         panScrollIconDirtyRect.intersect(clipRect);
511         hostWindow()->invalidateContentsAndWindow(panScrollIconDirtyRect, false /*immediate*/);
512     }
513
514     if (canBlitOnScroll()) { // The main frame can just blit the WebView window
515         // FIXME: Find a way to scroll subframes with this faster path
516         if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
517             hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
518     } else { 
519        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
520        // windowed plugins.
521        hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
522     }
523
524     // This call will move children with native widgets (plugins) and invalidate them as well.
525     frameRectsChanged();
526
527     // Now blit the backingstore into the window which should be very fast.
528     hostWindow()->invalidateWindow(IntRect(), true);
529 }
530
531 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
532 {
533     hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
534     return true;
535 }
536
537 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
538 {
539     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
540     return viewPoint + scrollOffset();
541 }
542
543 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
544 {
545     IntPoint viewPoint = contentsPoint - scrollOffset();
546     return convertToContainingWindow(viewPoint);  
547 }
548
549 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
550 {
551     IntRect viewRect = convertFromContainingWindow(windowRect);
552     viewRect.move(scrollOffset());
553     return viewRect;
554 }
555
556 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
557 {
558     IntRect viewRect = contentsRect;
559     viewRect.move(-scrollOffset());
560     return convertToContainingWindow(viewRect);
561 }
562
563 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
564 {
565     if (platformWidget())
566         return platformContentsToScreen(rect);
567     if (!hostWindow())
568         return IntRect();
569     return hostWindow()->windowToScreen(contentsToWindow(rect));
570 }
571
572 IntPoint ScrollView::screenToContents(const IntPoint& point) const
573 {
574     if (platformWidget())
575         return platformScreenToContents(point);
576     if (!hostWindow())
577         return IntPoint();
578     return windowToContents(hostWindow()->screenToWindow(point));
579 }
580
581 bool ScrollView::containsScrollbarsAvoidingResizer() const
582 {
583     return !m_scrollbarsAvoidingResizer;
584 }
585
586 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
587 {
588     int oldCount = m_scrollbarsAvoidingResizer;
589     m_scrollbarsAvoidingResizer += overlapDelta;
590     if (parent())
591         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
592     else if (!scrollbarsSuppressed()) {
593         // If we went from n to 0 or from 0 to n and we're the outermost view,
594         // we need to invalidate the windowResizerRect(), since it will now need to paint
595         // differently.
596         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
597             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
598             invalidateRect(windowResizerRect());
599     }
600 }
601
602 void ScrollView::setParent(ScrollView* parentView)
603 {
604     if (parentView == parent())
605         return;
606
607     if (m_scrollbarsAvoidingResizer && parent())
608         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
609
610     Widget::setParent(parentView);
611
612     if (m_scrollbarsAvoidingResizer && parent())
613         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
614 }
615
616 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
617 {
618     if (suppressed == m_scrollbarsSuppressed)
619         return;
620
621     m_scrollbarsSuppressed = suppressed;
622
623     if (platformWidget())
624         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
625     else if (repaintOnUnsuppress && !suppressed) {
626         if (m_horizontalScrollbar)
627             m_horizontalScrollbar->invalidate();
628         if (m_verticalScrollbar)
629             m_verticalScrollbar->invalidate();
630
631         // Invalidate the scroll corner too on unsuppress.
632         invalidateRect(scrollCornerRect());
633     }
634 }
635
636 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
637 {
638     if (platformWidget())
639         return 0;
640
641     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
642     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
643         return m_horizontalScrollbar.get();
644     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
645         return m_verticalScrollbar.get();
646     return 0;
647 }
648
649 void ScrollView::wheelEvent(PlatformWheelEvent& e)
650 {
651     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
652 #if PLATFORM(WX)
653     if (!canHaveScrollbars()) {
654 #else
655     if (!canHaveScrollbars() || platformWidget()) {
656 #endif
657         return;
658     }
659
660     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
661     IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
662     if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
663         (e.deltaX() > 0 && scrollOffset().width() > 0) ||
664         (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
665         (e.deltaY() > 0 && scrollOffset().height() > 0)) {
666         e.accept();
667         float deltaX = e.deltaX();
668         float deltaY = e.deltaY();
669         if (e.granularity() == ScrollByPageWheelEvent) {
670             ASSERT(deltaX == 0);
671             bool negative = deltaY < 0;
672             deltaY = max(max(static_cast<float>(visibleHeight()) * Scrollbar::minFractionToStepWhenPaging(), static_cast<float>(visibleHeight() - Scrollbar::maxOverlapBetweenPages())), 1.0f);
673             if (negative)
674                 deltaY = -deltaY;
675         }
676         scrollBy(IntSize(-deltaX, -deltaY));
677     }
678 }
679
680 void ScrollView::setFrameRect(const IntRect& newRect)
681 {
682     IntRect oldRect = frameRect();
683     
684     if (newRect == oldRect)
685         return;
686
687     Widget::setFrameRect(newRect);
688
689     if (platformWidget())
690         return;
691     
692     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
693         updateScrollbars(m_scrollOffset);
694         if (!m_useFixedLayout)
695             contentsResized();
696     }
697
698     frameRectsChanged();
699 }
700
701 void ScrollView::frameRectsChanged()
702 {
703     if (platformWidget())
704         return;
705
706     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
707     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
708         (*current)->frameRectsChanged();
709 }
710
711 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
712 {
713     IntRect paintRect = rect;
714     if (!paintsEntireContents())
715         paintRect.intersect(visibleContentRect());
716     if (paintRect.isEmpty())
717         return;
718
719     if (platformWidget()) {
720         platformRepaintContentRectangle(paintRect, now);
721         return;
722     }
723
724     if (hostWindow())
725         hostWindow()->invalidateContentsAndWindow(contentsToWindow(paintRect), now /*immediate*/);
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     static 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()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
944 }
945
946 void ScrollView::removePanScrollIcon()
947 {
948     if (!hostWindow())
949         return;
950     m_drawPanScrollIcon = false; 
951     hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
952 }
953
954 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(EFL)
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 void ScrollView::platformSetScrollbarModes()
989 {
990 }
991
992 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
993 {
994     horizontal = ScrollbarAuto;
995     vertical = ScrollbarAuto;
996 }
997
998 void ScrollView::platformSetCanBlitOnScroll(bool)
999 {
1000 }
1001
1002 bool ScrollView::platformCanBlitOnScroll() const
1003 {
1004     return false;
1005 }
1006
1007 IntRect ScrollView::platformVisibleContentRect(bool) const
1008 {
1009     return IntRect();
1010 }
1011
1012 IntSize ScrollView::platformContentsSize() const
1013 {
1014     return IntSize();
1015 }
1016
1017 void ScrollView::platformSetContentsSize()
1018 {
1019 }
1020
1021 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1022 {
1023     return rect;
1024 }
1025
1026 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1027 {
1028     return point;
1029 }
1030
1031 void ScrollView::platformSetScrollPosition(const IntPoint&)
1032 {
1033 }
1034
1035 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1036 {
1037     return true;
1038 }
1039
1040 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1041 {
1042 }
1043
1044 bool ScrollView::platformIsOffscreen() const
1045 {
1046     return false;
1047 }
1048
1049 #endif
1050
1051 }
1052