2009-02-16 Anantanarayanan Iyengar <ananta@chromium.org>
[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 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_drawPanScrollIcon(false)
50     , m_useFixedLayout(false)
51 {
52     platformInit();
53 }
54
55 ScrollView::~ScrollView()
56 {
57     platformDestroy();
58 }
59
60 void ScrollView::addChild(Widget* child) 
61 {
62     ASSERT(child != this && !child->parent());
63     child->setParent(this);
64     m_children.add(child);
65     if (child->platformWidget())
66         platformAddChild(child);
67 }
68
69 void ScrollView::removeChild(Widget* child)
70 {
71     ASSERT(child->parent() == this);
72     child->setParent(0);
73     m_children.remove(child);
74     if (child->platformWidget())
75         platformRemoveChild(child);
76 }
77
78 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
79 {
80     if (hasBar && !m_horizontalScrollbar && !platformHasHorizontalAdjustment()) {
81         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
82         addChild(m_horizontalScrollbar.get());
83     } else if (!hasBar && m_horizontalScrollbar) {
84         removeChild(m_horizontalScrollbar.get());
85         m_horizontalScrollbar = 0;
86     }
87 }
88
89 void ScrollView::setHasVerticalScrollbar(bool hasBar)
90 {
91     if (hasBar && !m_verticalScrollbar && !platformHasVerticalAdjustment()) {
92         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
93         addChild(m_verticalScrollbar.get());
94     } else if (!hasBar && m_verticalScrollbar) {
95         removeChild(m_verticalScrollbar.get());
96         m_verticalScrollbar = 0;
97     }
98 }
99
100 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
101 {
102     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
103 }
104
105 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
106 {
107     if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode())
108         return;
109     m_horizontalScrollbarMode = horizontalMode;
110     m_verticalScrollbarMode = verticalMode;
111     if (platformWidget())
112         platformSetScrollbarModes();
113     else
114         updateScrollbars(scrollOffset());
115 }
116
117 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
118 {
119     if (platformWidget()) {
120         platformScrollbarModes(horizontalMode, verticalMode);
121         return;
122     }
123     horizontalMode = m_horizontalScrollbarMode;
124     verticalMode = m_verticalScrollbarMode;
125 }
126
127 void ScrollView::setCanHaveScrollbars(bool canScroll)
128 {
129     ScrollbarMode newHorizontalMode;
130     ScrollbarMode newVerticalMode;
131     
132     scrollbarModes(newHorizontalMode, newVerticalMode);
133     
134     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
135         newVerticalMode = ScrollbarAuto;
136     else if (!canScroll)
137         newVerticalMode = ScrollbarAlwaysOff;
138     
139     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
140         newHorizontalMode = ScrollbarAuto;
141     else if (!canScroll)
142         newHorizontalMode = ScrollbarAlwaysOff;
143     
144     setScrollbarModes(newHorizontalMode, newVerticalMode);
145 }
146
147 void ScrollView::setCanBlitOnScroll(bool b)
148 {
149     if (platformWidget()) {
150         platformSetCanBlitOnScroll(b);
151         return;
152     }
153
154     m_canBlitOnScroll = b;
155 }
156
157 bool ScrollView::canBlitOnScroll() const
158 {
159     if (platformWidget())
160         return platformCanBlitOnScroll();
161
162     return m_canBlitOnScroll;
163 }
164
165 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
166 {
167     if (platformWidget())
168         return platformVisibleContentRect(includeScrollbars);
169     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
170                    IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), 
171                            max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
172 }
173
174 int ScrollView::layoutWidth() const
175 {
176     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
177 }
178
179 int ScrollView::layoutHeight() const
180 {
181     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
182 }
183
184 IntSize ScrollView::fixedLayoutSize() const
185 {
186     return m_fixedLayoutSize;
187 }
188
189 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
190 {
191     if (fixedLayoutSize() == newSize)
192         return;
193     m_fixedLayoutSize = newSize;
194     updateScrollbars(scrollOffset());
195 }
196
197 bool ScrollView::useFixedLayout() const
198 {
199     return m_useFixedLayout;
200 }
201
202 void ScrollView::setUseFixedLayout(bool enable)
203 {
204     if (useFixedLayout() == enable)
205         return;
206     m_useFixedLayout = enable;
207     updateScrollbars(scrollOffset());
208 }
209
210 IntSize ScrollView::contentsSize() const
211 {
212     if (platformWidget())
213         return platformContentsSize();
214     return m_contentsSize;
215 }
216
217 void ScrollView::setContentsSize(const IntSize& newSize)
218 {
219     if (contentsSize() == newSize)
220         return;
221     m_contentsSize = newSize;
222     if (platformWidget())
223         platformSetContentsSize();
224     else
225         updateScrollbars(scrollOffset());
226 }
227
228 IntPoint ScrollView::maximumScrollPosition() const
229 {
230     IntSize maximumOffset = contentsSize() - visibleContentRect().size();
231     maximumOffset.clampNegativeToZero();
232     return IntPoint(maximumOffset.width(), maximumOffset.height());
233 }
234
235 void ScrollView::valueChanged(Scrollbar* scrollbar)
236 {
237     // Figure out if we really moved.
238     IntSize newOffset = m_scrollOffset;
239     if (scrollbar) {
240         if (scrollbar == m_horizontalScrollbar)
241             newOffset.setWidth(scrollbar->value());
242         else if (scrollbar == m_verticalScrollbar)
243             newOffset.setHeight(scrollbar->value());
244     }
245
246     IntSize scrollDelta = newOffset - m_scrollOffset;
247     if (scrollDelta == IntSize())
248         return;
249     m_scrollOffset = newOffset;
250
251     if (scrollbarsSuppressed())
252         return;
253
254     scrollContents(scrollDelta);
255 }
256
257 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
258 {
259     // FIXME: This method is not transform-aware.  It should just be moved to FrameView so that an accurate
260     // position for the child view can be determined.
261     IntRect rect = r;
262     ScrollView* view = this;
263     while (view) {
264         if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling.
265             return;
266         view->setScrollPosition(rect.location());
267         rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
268         if (view->parent())
269             rect.intersect(view->frameRect());
270         view = view->parent();
271     }
272     
273     // We may be embedded inside some containing platform scroll view that we don't manage.  This is the case
274     // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView
275     // that contains both it and message headers.  Let the HostWindow know about this scroll so that it can pass the message
276     // on up the view chain.
277     // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to
278     // expose the headers.
279     hostWindow()->scrollRectIntoView(rect, this);
280 }
281
282 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
283 {
284     if (prohibitsScrolling())
285         return;
286
287     if (platformWidget()) {
288         platformSetScrollPosition(scrollPoint);
289         return;
290     }
291
292     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
293     newScrollPosition.clampNegativeToZero();
294
295     if (newScrollPosition == scrollPosition())
296         return;
297
298     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
299 }
300
301 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
302 {
303     if (platformWidget())
304         return platformScroll(direction, granularity);
305
306     if (direction == ScrollUp || direction == ScrollDown) {
307         if (m_verticalScrollbar)
308             return m_verticalScrollbar->scroll(direction, granularity);
309     } else {
310         if (m_horizontalScrollbar)
311             return m_horizontalScrollbar->scroll(direction, granularity);
312     }
313     return false;
314 }
315
316 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
317 {
318     // Don't allow re-entrancy into this function.
319     if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
320         return;
321
322     m_inUpdateScrollbars = true;
323
324     bool hasVerticalScrollbar = m_verticalScrollbar;
325     bool hasHorizontalScrollbar = m_horizontalScrollbar;
326     bool oldHasVertical = hasVerticalScrollbar;
327     bool oldHasHorizontal = hasHorizontalScrollbar;
328     ScrollbarMode hScroll = m_horizontalScrollbarMode;
329     ScrollbarMode vScroll = m_verticalScrollbarMode;
330
331     const int scrollbarThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness();
332
333     for (int pass = 0; pass < 2; pass++) {
334         bool scrollsVertically;
335         bool scrollsHorizontally;
336
337         if (!m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) {
338             // Do a layout if pending before checking if scrollbars are needed.
339             if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar != oldHasHorizontal)
340                 visibleContentsResized();
341
342             scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() > height());
343             if (scrollsVertically)
344                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() + scrollbarThickness > width());
345             else {
346                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() > width());
347                 if (scrollsHorizontally)
348                     scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() + scrollbarThickness > height());
349             }
350         } else {
351             scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScrollbar : (hScroll == ScrollbarAlwaysOn);
352             scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollbar : (vScroll == ScrollbarAlwaysOn);
353         }
354         
355         if (hasVerticalScrollbar != scrollsVertically) {
356             setHasVerticalScrollbar(scrollsVertically);
357             hasVerticalScrollbar = scrollsVertically;
358         }
359
360         if (hasHorizontalScrollbar != scrollsHorizontally) {
361             setHasHorizontalScrollbar(scrollsHorizontally);
362             hasHorizontalScrollbar = scrollsHorizontally;
363         }
364     }
365     
366     // Set up the range (and page step/line step).
367     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
368     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
369     scroll.clampNegativeToZero();
370  
371     if (!platformHandleHorizontalAdjustment(scroll) && m_horizontalScrollbar) {
372         int clientWidth = visibleWidth();
373         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
374         int pageStep = (clientWidth - cAmountToKeepWhenPaging);
375         if (pageStep < 0)
376             pageStep = clientWidth;
377         IntRect oldRect(m_horizontalScrollbar->frameRect());
378         IntRect hBarRect = IntRect(0,
379                                    height() - m_horizontalScrollbar->height(),
380                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
381                                    m_horizontalScrollbar->height());
382         m_horizontalScrollbar->setFrameRect(hBarRect);
383         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
384             m_horizontalScrollbar->invalidate();
385
386         if (m_scrollbarsSuppressed)
387             m_horizontalScrollbar->setSuppressInvalidation(true);
388         m_horizontalScrollbar->setSteps(cScrollbarPixelsPerLineStep, pageStep);
389         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
390         m_horizontalScrollbar->setValue(scroll.width());
391         if (m_scrollbarsSuppressed)
392             m_horizontalScrollbar->setSuppressInvalidation(false); 
393     } 
394
395     if (!platformHandleVerticalAdjustment(scroll) && m_verticalScrollbar) {
396         int clientHeight = visibleHeight();
397         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
398         int pageStep = (clientHeight - cAmountToKeepWhenPaging);
399         if (pageStep < 0)
400             pageStep = clientHeight;
401         IntRect oldRect(m_verticalScrollbar->frameRect());
402         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 
403                                    0,
404                                    m_verticalScrollbar->width(),
405                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
406         m_verticalScrollbar->setFrameRect(vBarRect);
407         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
408             m_verticalScrollbar->invalidate();
409
410         if (m_scrollbarsSuppressed)
411             m_verticalScrollbar->setSuppressInvalidation(true);
412         m_verticalScrollbar->setSteps(cScrollbarPixelsPerLineStep, pageStep);
413         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
414         m_verticalScrollbar->setValue(scroll.height());
415         if (m_scrollbarsSuppressed)
416             m_verticalScrollbar->setSuppressInvalidation(false);
417     }
418
419     if (oldHasVertical != (m_verticalScrollbar != 0) || oldHasHorizontal != (m_horizontalScrollbar != 0))
420         frameRectsChanged();
421
422     // See if our offset has changed in a situation where we might not have scrollbars.
423     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
424     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
425     // does not).
426     IntSize scrollDelta = scroll - m_scrollOffset;
427     if (scrollDelta != IntSize()) {
428        m_scrollOffset = scroll;
429        scrollContents(scrollDelta);
430     }
431
432     m_inUpdateScrollbars = false;
433 }
434
435 const int panIconSizeLength = 20;
436
437 void ScrollView::scrollContents(const IntSize& scrollDelta)
438 {
439     if (!hostWindow())
440         return;
441
442     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
443     // with the clip rect every time to keep it smooth.
444     IntRect clipRect = windowClipRect();
445     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
446     IntRect updateRect = clipRect;
447     updateRect.intersect(scrollViewRect);
448
449     // Invalidate the window (not the backing store).
450     hostWindow()->repaint(updateRect, false);
451
452     if (m_drawPanScrollIcon) {
453         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
454         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
455         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
456         panScrollIconDirtyRect.intersect(clipRect);
457         hostWindow()->repaint(panScrollIconDirtyRect, true);
458     }
459
460     if (canBlitOnScroll() && !rootPreventsBlitting()) { // The main frame can just blit the WebView window
461        // FIXME: Find a way to blit subframes without blitting overlapping content
462        hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect);
463     } else { 
464        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
465        // plugins.
466        hostWindow()->repaint(updateRect, true, false, true); // Invalidate the backing store and repaint it synchronously
467     }
468
469     // This call will move children with native widgets (plugins) and invalidate them as well.
470     frameRectsChanged();
471
472     // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should
473     // be very fast).
474     hostWindow()->paint();
475 }
476
477 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
478 {
479     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
480     return viewPoint + scrollOffset();
481 }
482
483 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
484 {
485     IntPoint viewPoint = contentsPoint - scrollOffset();
486     return convertToContainingWindow(viewPoint);  
487 }
488
489 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
490 {
491     IntRect viewRect = convertFromContainingWindow(windowRect);
492     viewRect.move(scrollOffset());
493     return viewRect;
494 }
495
496 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
497 {
498     IntRect viewRect = contentsRect;
499     viewRect.move(-scrollOffset());
500     return convertToContainingWindow(viewRect);
501 }
502
503 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
504 {
505     if (platformWidget())
506         return platformContentsToScreen(rect);
507     return hostWindow()->windowToScreen(contentsToWindow(rect));
508 }
509
510 IntPoint ScrollView::screenToContents(const IntPoint& point) const
511 {
512     if (platformWidget())
513         return platformScreenToContents(point);
514     return windowToContents(hostWindow()->screenToWindow(point));
515 }
516
517 bool ScrollView::containsScrollbarsAvoidingResizer() const
518 {
519     return !m_scrollbarsAvoidingResizer;
520 }
521
522 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
523 {
524     int oldCount = m_scrollbarsAvoidingResizer;
525     m_scrollbarsAvoidingResizer += overlapDelta;
526     if (parent())
527         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
528     else if (!scrollbarsSuppressed()) {
529         // If we went from n to 0 or from 0 to n and we're the outermost view,
530         // we need to invalidate the windowResizerRect(), since it will now need to paint
531         // differently.
532         if (oldCount > 0 && m_scrollbarsAvoidingResizer == 0 ||
533             oldCount == 0 && m_scrollbarsAvoidingResizer > 0)
534             invalidateRect(windowResizerRect());
535     }
536 }
537
538 void ScrollView::setParent(ScrollView* parentView)
539 {
540     if (parentView == parent())
541         return;
542
543     if (m_scrollbarsAvoidingResizer && parent())
544         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
545
546     Widget::setParent(parentView);
547     
548     if (m_scrollbarsAvoidingResizer && parent())
549         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
550 }
551
552 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
553 {
554     if (suppressed == m_scrollbarsSuppressed)
555         return;
556
557     m_scrollbarsSuppressed = suppressed;
558
559     if (platformWidget())
560         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
561     else if (repaintOnUnsuppress && !suppressed) {
562         if (m_horizontalScrollbar)
563             m_horizontalScrollbar->invalidate();
564         if (m_verticalScrollbar)
565             m_verticalScrollbar->invalidate();
566
567         // Invalidate the scroll corner too on unsuppress.
568         IntRect hCorner;
569         if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
570             hCorner = IntRect(m_horizontalScrollbar->width(),
571                               height() - m_horizontalScrollbar->height(),
572                               width() - m_horizontalScrollbar->width(),
573                               m_horizontalScrollbar->height());
574             invalidateRect(hCorner);
575         }
576
577         if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
578             IntRect vCorner(width() - m_verticalScrollbar->width(),
579                             m_verticalScrollbar->height(),
580                             m_verticalScrollbar->width(),
581                             height() - m_verticalScrollbar->height());
582             if (vCorner != hCorner)
583                 invalidateRect(vCorner);
584         }
585     }
586 }
587
588 Scrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mouseEvent)
589 {
590     if (platformWidget())
591         return 0;
592
593     IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos());
594     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
595         return m_horizontalScrollbar.get();
596     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
597         return m_verticalScrollbar.get();
598     return 0;
599 }
600
601 void ScrollView::wheelEvent(PlatformWheelEvent& e)
602 {
603     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
604     if (!canHaveScrollbars() || platformWidget())
605         return;
606
607     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
608     IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
609     if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
610         (e.deltaX() > 0 && scrollOffset().width() > 0) ||
611         (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
612         (e.deltaY() > 0 && scrollOffset().height() > 0)) {
613         e.accept();
614         float deltaX = e.deltaX();
615         float deltaY = e.deltaY();
616         if (e.granularity() == ScrollByLineWheelEvent) {
617             deltaX *= cMouseWheelPixelsPerLineStep;
618             deltaY *= cMouseWheelPixelsPerLineStep;
619         } else if (e.granularity() == ScrollByPageWheelEvent) {
620             ASSERT(deltaX == 0);
621             bool negative = deltaY < 0;
622             deltaY = max(0, visibleHeight() - cAmountToKeepWhenPaging);
623             if (negative)
624                 deltaY = -deltaY;
625         }
626         scrollBy(IntSize(-deltaX, -deltaY));
627     }
628 }
629
630 void ScrollView::setFrameRect(const IntRect& newRect)
631 {
632     IntRect oldRect = frameRect();
633     
634     if (newRect == oldRect)
635         return;
636
637     Widget::setFrameRect(newRect);
638
639     if (platformWidget())
640         return;
641     
642     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
643         updateScrollbars(m_scrollOffset);
644         contentsResized();
645     }
646
647     frameRectsChanged();
648 }
649
650 void ScrollView::frameRectsChanged()
651 {
652     if (platformWidget())
653         return;
654
655     HashSet<Widget*>::const_iterator end = m_children.end();
656     for (HashSet<Widget*>::const_iterator current = m_children.begin(); current != end; ++current)
657         (*current)->frameRectsChanged();
658 }
659
660 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
661 {
662     if (rect.isEmpty())
663         return;
664
665     if (platformWidget()) {
666         platformRepaintContentRectangle(rect, now);
667         return;
668     }
669
670     hostWindow()->repaint(contentsToWindow(rect), true, now);
671 }
672
673 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
674 {
675     if (platformWidget()) {
676         Widget::paint(context, rect);
677         return;
678     }
679
680     if (context->paintingDisabled() && !context->updatingControlTints())
681         return;
682
683     IntRect documentDirtyRect = rect;
684     documentDirtyRect.intersect(frameRect());
685
686     context->save();
687
688     context->translate(x(), y());
689     documentDirtyRect.move(-x(), -y());
690
691     context->translate(-scrollX(), -scrollY());
692     documentDirtyRect.move(scrollX(), scrollY());
693
694     context->clip(visibleContentRect());
695
696     paintContents(context, documentDirtyRect);
697
698     context->restore();
699
700     // Now paint the scrollbars.
701     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
702         context->save();
703         IntRect scrollViewDirtyRect = rect;
704         scrollViewDirtyRect.intersect(frameRect());
705         context->translate(x(), y());
706         scrollViewDirtyRect.move(-x(), -y());
707         if (m_horizontalScrollbar)
708             m_horizontalScrollbar->paint(context, scrollViewDirtyRect);
709         if (m_verticalScrollbar)
710             m_verticalScrollbar->paint(context, scrollViewDirtyRect);
711
712         IntRect hCorner;
713         if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
714             hCorner = IntRect(m_horizontalScrollbar->width(),
715                               height() - m_horizontalScrollbar->height(),
716                               width() - m_horizontalScrollbar->width(),
717                               m_horizontalScrollbar->height());
718             if (hCorner.intersects(scrollViewDirtyRect))
719                 ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, hCorner);
720         }
721
722         if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
723             IntRect vCorner(width() - m_verticalScrollbar->width(),
724                             m_verticalScrollbar->height(),
725                             m_verticalScrollbar->width(),
726                             height() - m_verticalScrollbar->height());
727             if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect))
728                 ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, vCorner);
729         }
730
731         context->restore();
732     }
733
734     // Paint the panScroll Icon
735     if (m_drawPanScrollIcon) {
736         DEFINE_STATIC_LOCAL(RefPtr<Image>, panScrollIcon, (Image::loadPlatformResource("panIcon")));
737         context->drawImage(panScrollIcon.get(), m_panScrollIconPoint);
738     }
739 }
740
741 bool ScrollView::scrollbarCornerPresent() const
742 {
743     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
744            (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
745 }
746
747 void ScrollView::setParentVisible(bool visible)
748 {
749     if (isParentVisible() == visible)
750         return;
751     
752     Widget::setParentVisible(visible);
753
754     if (!isSelfVisible())
755         return;
756         
757     HashSet<Widget*>::iterator end = m_children.end();
758     for (HashSet<Widget*>::iterator it = m_children.begin(); it != end; ++it)
759         (*it)->setParentVisible(visible);
760 }
761
762 void ScrollView::show()
763 {
764     if (!isSelfVisible()) {
765         setSelfVisible(true);
766         if (isParentVisible()) {
767             HashSet<Widget*>::iterator end = m_children.end();
768             for (HashSet<Widget*>::iterator it = m_children.begin(); it != end; ++it)
769                 (*it)->setParentVisible(true);
770         }
771     }
772
773     Widget::show();
774 }
775
776 void ScrollView::hide()
777 {
778     if (isSelfVisible()) {
779         if (isParentVisible()) {
780             HashSet<Widget*>::iterator end = m_children.end();
781             for (HashSet<Widget*>::iterator it = m_children.begin(); it != end; ++it)
782                 (*it)->setParentVisible(false);
783         }
784         setSelfVisible(false);
785     }
786
787     Widget::hide();
788 }
789
790 bool ScrollView::isOffscreen() const
791 {
792     if (platformWidget())
793         return platformIsOffscreen();
794     
795     if (!isVisible())
796         return true;
797     
798     // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
799     // currently, we can add the method when the other platforms decide to implement this concept.
800     return false;
801 }
802
803
804 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
805 {
806     m_drawPanScrollIcon = true;    
807     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
808     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength,panIconSizeLength)), true, true);    
809 }
810
811 void ScrollView::removePanScrollIcon()
812 {
813     m_drawPanScrollIcon = false; 
814     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true, true);
815 }
816
817 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT)
818 void ScrollView::platformInit()
819 {
820 }
821
822 void ScrollView::platformDestroy()
823 {
824 }
825 #endif
826
827 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
828 void ScrollView::platformAddChild(Widget*)
829 {
830 }
831
832 void ScrollView::platformRemoveChild(Widget*)
833 {
834 }
835 #endif
836
837 #if !PLATFORM(MAC)
838 void ScrollView::platformSetScrollbarsSuppressed(bool repaintOnUnsuppress)
839 {
840 }
841 #endif
842
843 #if !PLATFORM(MAC) && !PLATFORM(WX)
844 void ScrollView::platformSetScrollbarModes()
845 {
846 }
847
848 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
849 {
850 }
851
852 void ScrollView::platformSetCanBlitOnScroll(bool)
853 {
854 }
855
856 bool ScrollView::platformCanBlitOnScroll() const
857 {
858     return false;
859 }
860
861 IntRect ScrollView::platformVisibleContentRect(bool) const
862 {
863     return IntRect();
864 }
865
866 IntSize ScrollView::platformContentsSize() const
867 {
868     return IntSize();
869 }
870
871 void ScrollView::platformSetContentsSize()
872 {
873 }
874
875 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
876 {
877     return rect;
878 }
879
880 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
881 {
882     return point;
883 }
884
885 void ScrollView::platformSetScrollPosition(const IntPoint&)
886 {
887 }
888
889 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
890 {
891     return true;
892 }
893
894 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool now)
895 {
896 }
897
898 bool ScrollView::platformIsOffscreen() const
899 {
900     return false;
901 }
902 #endif
903
904 #if !PLATFORM(GTK)
905 bool ScrollView::platformHandleHorizontalAdjustment(const IntSize&)
906 {
907     return false;
908 }
909
910 bool ScrollView::platformHandleVerticalAdjustment(const IntSize&)
911 {
912     return false;
913 }
914
915 bool ScrollView::platformHasHorizontalAdjustment() const
916 {
917     return false;
918 }
919
920 bool ScrollView::platformHasVerticalAdjustment() const
921 {
922     return false;
923 }
924
925 #endif
926
927 }
928