2008-05-07 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebCore / platform / qt / ScrollViewQt.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
4  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
5  * Copyright (C) 2006 George Staikos <staikos@kde.org>
6  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "ScrollView.h"
34 #include "FrameView.h"
35 #include "FloatRect.h"
36 #include "FocusController.h"
37 #include "IntPoint.h"
38 #include "PlatformMouseEvent.h"
39 #include "PlatformWheelEvent.h"
40 #include "NotImplemented.h"
41 #include "Frame.h"
42 #include "Page.h"
43 #include "GraphicsContext.h"
44 #include "PlatformScrollBar.h"
45
46 #include <QDebug>
47 #include <QWidget>
48 #include <QPainter>
49 #include <QApplication>
50 #include <QPalette>
51
52 #ifdef Q_WS_MAC
53 #include <Carbon/Carbon.h>
54 #endif
55
56 #include "qwebframe.h"
57 #include "qwebpage.h"
58
59 // #define DEBUG_SCROLLVIEW
60
61 namespace WebCore {
62
63 class ScrollView::ScrollViewPrivate : public ScrollbarClient
64 {
65 public:
66     ScrollViewPrivate(ScrollView* view)
67       : m_view(view)
68       , m_hasStaticBackground(false)
69       , m_nativeWidgets(0)
70       , m_scrollbarsSuppressed(false)
71       , m_inUpdateScrollbars(false)
72       , m_scrollbarsAvoidingResizer(0)
73       , m_vScrollbarMode(ScrollbarAuto)
74       , m_hScrollbarMode(ScrollbarAuto)
75     {
76     }
77
78     ~ScrollViewPrivate()
79     {
80         setHasHorizontalScrollbar(false);
81         setHasVerticalScrollbar(false);
82     }
83
84     void setHasHorizontalScrollbar(bool hasBar);
85     void setHasVerticalScrollbar(bool hasBar);
86
87     virtual void valueChanged(Scrollbar*);
88     virtual IntRect windowClipRect() const;
89     virtual bool isActive() const;
90
91     void scrollBackingStore(const IntSize& scrollDelta);
92
93     ScrollView* m_view;
94     IntSize m_scrollOffset;
95     IntSize m_contentsSize;
96     bool m_hasStaticBackground;
97     int  m_nativeWidgets;
98     bool m_scrollbarsSuppressed;
99     bool m_inUpdateScrollbars;
100     int m_scrollbarsAvoidingResizer;
101     ScrollbarMode m_vScrollbarMode;
102     ScrollbarMode m_hScrollbarMode;
103     RefPtr<PlatformScrollbar> m_vBar;
104     RefPtr<PlatformScrollbar> m_hBar;
105     HashSet<Widget*> m_children;
106 };
107
108 void ScrollView::ScrollViewPrivate::setHasHorizontalScrollbar(bool hasBar)
109 {
110     if (Scrollbar::hasPlatformScrollbars()) {
111         if (hasBar && !m_hBar) {
112             m_hBar = new PlatformScrollbar(this, HorizontalScrollbar, RegularScrollbar);
113             m_view->addChild(m_hBar.get());
114         } else if (!hasBar && m_hBar) {
115             m_view->removeChild(m_hBar.get());;
116             m_hBar = 0;
117         }
118     }
119 }
120
121 void ScrollView::ScrollViewPrivate::setHasVerticalScrollbar(bool hasBar)
122 {
123     if (Scrollbar::hasPlatformScrollbars()) {
124         if (hasBar && !m_vBar) {
125             m_vBar = new PlatformScrollbar(this, VerticalScrollbar, RegularScrollbar);
126             m_view->addChild(m_vBar.get());
127         } else if (!hasBar && m_vBar) {
128             m_view->removeChild(m_vBar.get());
129             m_vBar = 0;
130         }
131     }
132 }
133
134 void ScrollView::ScrollViewPrivate::valueChanged(Scrollbar* bar)
135 {
136     // Figure out if we really moved.
137     IntSize newOffset = m_scrollOffset;
138     if (bar) {
139         if (bar == m_hBar)
140             newOffset.setWidth(bar->value());
141         else if (bar == m_vBar)
142             newOffset.setHeight(bar->value());
143     }
144     IntSize scrollDelta = newOffset - m_scrollOffset;
145     if (scrollDelta == IntSize())
146         return;
147     m_scrollOffset = newOffset;
148
149     if (m_scrollbarsSuppressed)
150         return;
151
152     scrollBackingStore(scrollDelta);
153     static_cast<FrameView*>(m_view)->frame()->sendScrollEvent();
154 }
155
156 void ScrollView::ScrollViewPrivate::scrollBackingStore(const IntSize& scrollDelta)
157 {
158     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
159     // with the clip rect every time to keep it smooth.
160     IntRect clipRect = m_view->windowClipRect();
161     IntRect scrollViewRect = m_view->convertToContainingWindow(IntRect(0, 0, m_view->visibleWidth(), m_view->visibleHeight()));
162
163     IntRect updateRect = clipRect;
164     updateRect.intersect(scrollViewRect);
165
166     if (!m_hasStaticBackground && !m_view->topLevel()->hasNativeWidgets()) {
167        m_view->scrollBackingStore(-scrollDelta.width(), -scrollDelta.height(),
168                                   scrollViewRect, clipRect);
169     } else  {
170        // We need to go ahead and repaint the entire backing store.
171        m_view->addToDirtyRegion(updateRect);
172        m_view->updateBackingStore();
173     }
174
175     m_view->geometryChanged();
176 }
177
178 IntRect ScrollView::ScrollViewPrivate::windowClipRect() const
179 {
180     return static_cast<const FrameView*>(m_view)->windowClipRect(false);
181 }
182
183 bool ScrollView::ScrollViewPrivate::isActive() const
184 {
185     Page* page = static_cast<const FrameView*>(m_view)->frame()->page();
186     return page && page->focusController()->isActive();
187 }
188
189 ScrollView::ScrollView()
190     : m_data(new ScrollViewPrivate(this))
191 {
192 }
193
194 ScrollView::~ScrollView()
195 {
196     delete m_data;
197 }
198
199 PlatformScrollbar *ScrollView::horizontalScrollBar() const
200 {
201     return m_data->m_hBar.get();
202 }
203
204 PlatformScrollbar *ScrollView::verticalScrollBar() const
205 {
206     return m_data->m_vBar.get();
207 }
208
209 void ScrollView::updateContents(const IntRect& rect, bool now)
210 {
211     if (rect.isEmpty())
212         return;
213
214     IntPoint windowPoint = contentsToWindow(rect.location());
215     IntRect containingWindowRect = rect;
216     containingWindowRect.setLocation(windowPoint);
217
218     // Cache the dirty spot.
219     addToDirtyRegion(containingWindowRect);
220
221     if (now)
222         updateBackingStore();
223 }
224
225 void ScrollView::update()
226 {
227     QWidget* native = nativeWidget();
228     if (native) {
229         native->update();
230         return;
231     }
232     updateContents(IntRect(0, 0, width(), height()));
233 }
234
235 int ScrollView::visibleWidth() const
236 {
237     return width() - (m_data->m_vBar ? m_data->m_vBar->width() : 0);
238 }
239
240 int ScrollView::visibleHeight() const
241 {
242     return height() - (m_data->m_hBar ? m_data->m_hBar->height() : 0);
243 }
244
245 FloatRect ScrollView::visibleContentRect() const
246 {
247     return FloatRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
248 }
249
250 FloatRect ScrollView::visibleContentRectConsideringExternalScrollers() const
251 {
252     // external scrollers not supported for now
253     return visibleContentRect();
254 }
255
256 void ScrollView::setContentsPos(int newX, int newY)
257 {
258     int dx = newX - contentsX();
259     int dy = newY - contentsY();
260     scrollBy(dx, dy);
261 }
262
263 void ScrollView::resizeContents(int w, int h)
264 {
265     IntSize newContentsSize(w, h);
266     if (m_data->m_contentsSize != newContentsSize) {
267         m_data->m_contentsSize = newContentsSize;
268         updateScrollbars(m_data->m_scrollOffset);
269     }
270 }
271
272 void ScrollView::setFrameGeometry(const IntRect& newGeometry)
273 {
274     IntRect oldGeometry = frameGeometry();
275     Widget::setFrameGeometry(newGeometry);
276
277     if (newGeometry == oldGeometry)
278         return;
279
280     if (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height()) {
281         updateScrollbars(m_data->m_scrollOffset);
282         static_cast<FrameView*>(this)->setNeedsLayout();
283     }
284
285     geometryChanged();
286 }
287
288 HashSet<Widget*>* ScrollView::children()
289 {
290     return &(m_data->m_children);
291 }
292
293 void ScrollView::geometryChanged() const
294 {
295     HashSet<Widget*>::const_iterator end = m_data->m_children.end();
296     for (HashSet<Widget*>::const_iterator current = m_data->m_children.begin(); current != end; ++current)
297         (*current)->geometryChanged();
298
299     const_cast<ScrollView *>(this)->invalidateScrollbars();
300 }
301
302
303 int ScrollView::contentsX() const
304 {
305     return scrollOffset().width();
306 }
307
308 int ScrollView::contentsY() const
309 {
310     return scrollOffset().height();
311 }
312
313 int ScrollView::contentsWidth() const
314 {
315     return m_data->m_contentsSize.width();
316 }
317
318 int ScrollView::contentsHeight() const
319 {
320     return m_data->m_contentsSize.height();
321 }
322
323 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
324 {
325     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
326     return viewPoint + scrollOffset();
327 }
328
329 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
330 {
331     IntPoint viewPoint = contentsPoint - scrollOffset();
332     return convertToContainingWindow(viewPoint);
333 }
334
335 IntPoint ScrollView::convertChildToSelf(const Widget* child, const IntPoint& point) const
336 {
337     IntPoint newPoint = point;
338     if (child != m_data->m_hBar && child != m_data->m_vBar)
339         newPoint = point - scrollOffset();
340     return Widget::convertChildToSelf(child, newPoint);
341 }
342
343 IntPoint ScrollView::convertSelfToChild(const Widget* child, const IntPoint& point) const
344 {
345     IntPoint newPoint = point;
346     if (child != m_data->m_hBar && child != m_data->m_vBar)
347         newPoint = point + scrollOffset();
348     return Widget::convertSelfToChild(child, newPoint);
349 }
350
351 IntSize ScrollView::scrollOffset() const
352 {
353     return m_data->m_scrollOffset;
354 }
355
356 IntSize ScrollView::maximumScroll() const
357 {
358     IntSize delta = (m_data->m_contentsSize - IntSize(visibleWidth(), visibleHeight())) - scrollOffset();
359     delta.clampNegativeToZero();
360     return delta;
361 }
362
363 void ScrollView::scrollBy(int dx, int dy)
364 {
365     IntSize scrollOffset = m_data->m_scrollOffset;
366     IntSize newScrollOffset = scrollOffset + IntSize(dx, dy).shrunkTo(maximumScroll());
367     newScrollOffset.clampNegativeToZero();
368
369     if (newScrollOffset == scrollOffset)
370         return;
371
372     updateScrollbars(newScrollOffset);
373 }
374
375 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
376 {
377     IntPoint p(max(0, r.x()), max(0, r.y()));
378     ScrollView* view = this;
379     while (view) {
380         view->setContentsPos(p.x(), p.y());
381         p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
382         view = static_cast<ScrollView*>(parent());
383     }
384 }
385
386 WebCore::ScrollbarMode ScrollView::hScrollbarMode() const
387 {
388     return m_data->m_hScrollbarMode;
389 }
390
391 WebCore::ScrollbarMode ScrollView::vScrollbarMode() const
392 {
393     return m_data->m_vScrollbarMode;
394 }
395
396 void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnSuppress)
397 {
398     m_data->m_scrollbarsSuppressed = suppressed;
399     if (repaintOnSuppress && !suppressed)
400         invalidateScrollbars();
401 }
402
403 void ScrollView::invalidateScrollbars()
404 {
405     if (m_data->m_hBar)
406         m_data->m_hBar->invalidate();
407     if (m_data->m_vBar)
408         m_data->m_vBar->invalidate();
409
410
411     // Invalidate the scroll corner too
412     IntRect hCorner;
413     if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) {
414         hCorner = IntRect(m_data->m_hBar->width(),
415                          height() - m_data->m_hBar->height(),
416                          width() - m_data->m_hBar->width(),
417                          m_data->m_hBar->height());
418        addToDirtyRegion(convertToContainingWindow(hCorner));
419     }
420
421     if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) {
422         IntRect vCorner(width() - m_data->m_vBar->width(),
423                        m_data->m_vBar->height(),
424                        m_data->m_vBar->width(),
425                        height() - m_data->m_vBar->height());
426         if (vCorner != hCorner)
427             addToDirtyRegion(convertToContainingWindow(vCorner));
428     }
429 }
430
431 void ScrollView::setHScrollbarMode(ScrollbarMode newMode)
432 {
433     if (m_data->m_hScrollbarMode != newMode) {
434         m_data->m_hScrollbarMode = newMode;
435         updateScrollbars(m_data->m_scrollOffset);
436     }
437 }
438
439 void ScrollView::setVScrollbarMode(ScrollbarMode newMode)
440 {
441     if (m_data->m_vScrollbarMode != newMode) {
442         m_data->m_vScrollbarMode = newMode;
443         updateScrollbars(m_data->m_scrollOffset);
444     }
445 }
446
447 void ScrollView::setScrollbarsMode(ScrollbarMode newMode)
448 {
449     if (m_data->m_hScrollbarMode != newMode ||
450         m_data->m_vScrollbarMode != newMode) {
451         m_data->m_hScrollbarMode = m_data->m_vScrollbarMode = newMode;
452         updateScrollbars(m_data->m_scrollOffset);
453     }
454 }
455
456 void ScrollView::setStaticBackground(bool flag)
457 {
458     m_data->m_hasStaticBackground = flag;
459 }
460
461 bool ScrollView::inWindow() const
462 {
463     return true;
464 }
465
466 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
467 {
468     // Don't allow re-entrancy into this function.
469     if (m_data->m_inUpdateScrollbars)
470         return;
471
472     // FIXME: This code is here so we don't have to fork FrameView.h/.cpp.
473     // In the end, FrameView should just merge with ScrollView.
474     if (static_cast<const FrameView*>(this)->frame()->prohibitsScrolling())
475         return;
476     
477     m_data->m_inUpdateScrollbars = true;
478
479     bool hasVerticalScrollbar = m_data->m_vBar;
480     bool hasHorizontalScrollbar = m_data->m_hBar;
481     bool oldHasVertical = hasVerticalScrollbar;
482     bool oldHasHorizontal = hasHorizontalScrollbar;
483     ScrollbarMode hScroll = m_data->m_hScrollbarMode;
484     ScrollbarMode vScroll = m_data->m_vScrollbarMode;
485     
486     const int cVerticalWidth = PlatformScrollbar::verticalScrollbarWidth();
487     const int cHorizontalHeight = PlatformScrollbar::horizontalScrollbarHeight();
488
489     for (int pass = 0; pass < 2; pass++) {
490         bool scrollsVertically;
491         bool scrollsHorizontally;
492
493         if (!m_data->m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) {
494             // Do a layout if pending before checking if scrollbars are needed.
495             if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar != oldHasHorizontal)
496                 static_cast<FrameView*>(this)->layout();
497              
498             scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() > height());
499             if (scrollsVertically)
500                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() + cVerticalWidth > width());
501             else {
502                 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() > width());
503                 if (scrollsHorizontally)
504                     scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() + cHorizontalHeight > height());
505             }
506         }
507         else {
508             scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScrollbar : (hScroll == ScrollbarAlwaysOn);
509             scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollbar : (vScroll == ScrollbarAlwaysOn);
510         }
511         
512         if (hasVerticalScrollbar != scrollsVertically) {
513             m_data->setHasVerticalScrollbar(scrollsVertically);
514             hasVerticalScrollbar = scrollsVertically;
515         }
516
517         if (hasHorizontalScrollbar != scrollsHorizontally) {
518             m_data->setHasHorizontalScrollbar(scrollsHorizontally);
519             hasHorizontalScrollbar = scrollsHorizontally;
520         }
521     }
522     
523     // Set up the range (and page step/line step).
524     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
525     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
526     scroll.clampNegativeToZero();
527
528     QPoint scrollbarOffset;
529 #ifdef Q_WS_MAC
530     // On Mac, offset the scrollbars so they don't cover the grow box. Check if the window 
531     // has a grow box, and then check if the bottom-right corner of the scroll view 
532     // intersercts it. The calculations are done in global coordinates.
533     QWidget* contentWidget = containingWindow();
534     if (contentWidget) {
535         QWidget* windowWidget = contentWidget->window();
536         if (windowWidget) {
537             HIViewRef growBox = 0;
538             HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(contentWidget->winId()))), kHIViewWindowGrowBoxID, &growBox);
539             const QPoint contentBr = contentWidget->mapToGlobal(QPoint(0,0)) + contentWidget->size();
540             const QPoint windowBr = windowWidget->mapToGlobal(QPoint(0,0)) + windowWidget->size();
541             const QPoint contentOffset = (windowBr - contentBr);
542             const int growBoxSize = 15;
543             const bool enableOffset = (growBox != 0 && contentOffset.x() >= 0 && contentOffset. y() >= 0);
544             scrollbarOffset = enableOffset ? QPoint(growBoxSize - qMin(contentOffset.x(), growBoxSize),
545                                                     growBoxSize - qMin(contentOffset.y(), growBoxSize))
546                                            : QPoint(0,0);
547         }
548     }
549 #endif
550
551     if (m_data->m_hBar) {
552         int clientWidth = visibleWidth();
553         m_data->m_hBar->setEnabled(contentsWidth() > clientWidth);
554         int pageStep = (clientWidth - PAGE_KEEP);
555         if (pageStep < 0) pageStep = clientWidth;
556         IntRect oldRect(m_data->m_hBar->frameGeometry());
557         IntRect hBarRect = IntRect(0,
558                                    height() - m_data->m_hBar->height(),
559                                    width() - (m_data->m_vBar ? m_data->m_vBar->width() : scrollbarOffset.x()),
560                                    m_data->m_hBar->height());
561         m_data->m_hBar->setRect(hBarRect);
562         if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_hBar->frameGeometry())
563             m_data->m_hBar->invalidate();
564
565         if (m_data->m_scrollbarsSuppressed)
566             m_data->m_hBar->setSuppressInvalidation(true);
567         m_data->m_hBar->setSteps(LINE_STEP, pageStep);
568         m_data->m_hBar->setProportion(clientWidth, contentsWidth());
569         m_data->m_hBar->setValue(scroll.width());
570         if (m_data->m_scrollbarsSuppressed)
571             m_data->m_hBar->setSuppressInvalidation(false); 
572     } 
573
574     if (m_data->m_vBar) {
575         int clientHeight = visibleHeight();
576         m_data->m_vBar->setEnabled(contentsHeight() > clientHeight);
577         int pageStep = (clientHeight - PAGE_KEEP);
578         if (pageStep < 0) pageStep = clientHeight;
579         IntRect oldRect(m_data->m_vBar->frameGeometry());
580         IntRect vBarRect = IntRect(width() - m_data->m_vBar->width(), 
581                                    0,
582                                    m_data->m_vBar->width(),
583                                    height() - (m_data->m_hBar ? m_data->m_hBar->height() : scrollbarOffset.y()));
584         m_data->m_vBar->setRect(vBarRect);
585         if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_vBar->frameGeometry())
586             m_data->m_vBar->invalidate();
587
588         if (m_data->m_scrollbarsSuppressed)
589             m_data->m_vBar->setSuppressInvalidation(true);
590         m_data->m_vBar->setSteps(LINE_STEP, pageStep);
591         m_data->m_vBar->setProportion(clientHeight, contentsHeight());
592         m_data->m_vBar->setValue(scroll.height());
593         if (m_data->m_scrollbarsSuppressed)
594             m_data->m_vBar->setSuppressInvalidation(false);
595     }
596
597     if (oldHasVertical != (m_data->m_vBar != 0) || oldHasHorizontal != (m_data->m_hBar != 0))
598         geometryChanged();
599
600     // See if our offset has changed in a situation where we might not have scrollbars.
601     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
602     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
603     // does not).
604     IntSize scrollDelta = scroll - m_data->m_scrollOffset;
605     if (scrollDelta != IntSize()) {
606        m_data->m_scrollOffset = scroll;
607        m_data->scrollBackingStore(scrollDelta);
608     }
609
610     m_data->m_inUpdateScrollbars = false;
611 }
612
613 PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mouseEvent)
614 {
615     IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos());
616     if (m_data->m_hBar && m_data->m_hBar->frameGeometry().contains(viewPoint))
617         return m_data->m_hBar.get();
618     if (m_data->m_vBar && m_data->m_vBar->frameGeometry().contains(viewPoint))
619         return m_data->m_vBar.get();
620     return 0;
621 }
622
623 void ScrollView::addChild(Widget* child)
624 {
625     child->setParent(this);
626     m_data->m_children.add(child);
627
628     if (child->nativeWidget())
629         topLevel()->incrementNativeWidgetCount();
630 }
631
632 void ScrollView::removeChild(Widget* child)
633 {
634     if (child->nativeWidget())
635         topLevel()->decrementNativeWidgetCount();
636
637     child->setParent(0);
638     child->hide();
639     m_data->m_children.remove(child);
640 }
641
642 static void drawScrollbarCorner(GraphicsContext* context, const IntRect& rect)
643 {
644 #if QT_VERSION < 0x040500
645     context->fillRect(rect, QApplication::palette().color(QPalette::Normal, QPalette::Window));
646 #else
647     QStyleOption option;
648     option.rect = rect;
649     QApplication::style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner,
650             &option, context->platformContext(), 0);
651 #endif
652 }
653
654
655 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
656 {
657     // FIXME: This code is here so we don't have to fork FrameView.h/.cpp.
658     // In the end, FrameView should just merge with ScrollView.
659     ASSERT(isFrameView());
660
661     if (context->paintingDisabled())
662         return;
663
664     IntRect documentDirtyRect = rect;
665
666     context->save();
667
668     context->translate(x(), y());
669     documentDirtyRect.move(-x(), -y());
670
671     context->translate(-contentsX(), -contentsY());
672     documentDirtyRect.move(contentsX(), contentsY());
673
674     documentDirtyRect.intersect(enclosingIntRect(visibleContentRect()));
675     context->clip(documentDirtyRect);
676
677     static_cast<const FrameView*>(this)->frame()->paint(context, documentDirtyRect);
678
679     context->restore();
680
681     // Now paint the scrollbars.
682     if (!m_data->m_scrollbarsSuppressed && (m_data->m_hBar || m_data->m_vBar)) {
683         context->save();
684         IntRect scrollViewDirtyRect = rect;
685         scrollViewDirtyRect.intersect(frameGeometry());
686         context->translate(x(), y());
687         scrollViewDirtyRect.move(-x(), -y());
688         if (m_data->m_hBar)
689             m_data->m_hBar->paint(context, scrollViewDirtyRect);
690         if (m_data->m_vBar)
691             m_data->m_vBar->paint(context, scrollViewDirtyRect);
692
693         // Fill the scroll corners using the current Qt style
694         IntRect hCorner;
695         if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) {
696             hCorner = IntRect(m_data->m_hBar->width(),
697                               height() - m_data->m_hBar->height(),
698                               width() - m_data->m_hBar->width(),
699                               m_data->m_hBar->height());
700             if (hCorner.intersects(scrollViewDirtyRect))
701                 drawScrollbarCorner(context, hCorner);
702         }
703
704         if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) {
705             IntRect vCorner(width() - m_data->m_vBar->width(),
706                             m_data->m_vBar->height(),
707                             m_data->m_vBar->width(),
708                             height() - m_data->m_vBar->height());
709             if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect))
710                 drawScrollbarCorner(context, vCorner);
711         }
712
713         context->restore();
714     }
715 }
716
717 void ScrollView::wheelEvent(PlatformWheelEvent& e)
718 {
719     float deltaX = e.deltaX();
720     float deltaY = e.deltaY();
721
722     PlatformMouseEvent mouseEvent(e.pos(), e.globalPos(), NoButton, MouseEventScroll,
723             0, e.shiftKey(), e.ctrlKey(), e.altKey(), e.metaKey(), 0);
724     PlatformScrollbar* scrollBar = scrollbarUnderMouse(mouseEvent);
725
726     if (scrollBar && scrollBar == verticalScrollBar()) {
727         deltaY = (deltaY == 0 ? deltaX : deltaY);
728         deltaX = 0;
729     } else if (scrollBar && scrollBar == horizontalScrollBar()) {
730         deltaX = (deltaX == 0 ? deltaY : deltaX);
731         deltaY = 0;
732     }
733
734     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
735     IntSize maxScrollDelta = maximumScroll();
736     if ((deltaX < 0 && maxScrollDelta.width() > 0) ||
737         (deltaX > 0 && scrollOffset().width() > 0) ||
738         (deltaY < 0 && maxScrollDelta.height() > 0) ||
739         (deltaY > 0 && scrollOffset().height() > 0)) {
740
741         e.accept();
742         scrollBy(int(-deltaX * LINE_STEP), int(-deltaY * LINE_STEP));
743     }
744 }
745
746 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
747 {
748     if (direction == ScrollUp || direction == ScrollDown) {
749         if (m_data->m_vBar)
750             return m_data->m_vBar->scroll(direction, granularity);
751     } else {
752         if (m_data->m_hBar)
753             return m_data->m_hBar->scroll(direction, granularity);
754     }
755     return false;
756 }
757
758 IntRect ScrollView::windowResizerRect()
759 {
760     ASSERT(isFrameView());
761     const FrameView* frameView = static_cast<const FrameView*>(this);
762     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
763     if (!page)
764         return IntRect();
765     return page->chrome()->windowResizerRect();
766 }
767
768 bool ScrollView::resizerOverlapsContent() const
769 {
770     return !m_data->m_scrollbarsAvoidingResizer;
771 }
772
773 void ScrollView::adjustOverlappingScrollbarCount(int overlapDelta)
774 {
775     int oldCount = m_data->m_scrollbarsAvoidingResizer;
776     m_data->m_scrollbarsAvoidingResizer += overlapDelta;
777     if (parent() && parent()->isFrameView())
778         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(overlapDelta);
779     else if (!m_data->m_scrollbarsSuppressed) {
780         // If we went from n to 0 or from 0 to n and we're the outermost view,
781         // we need to invalidate the windowResizerRect(), since it will now need to paint
782         // differently.
783         if (oldCount > 0 && m_data->m_scrollbarsAvoidingResizer == 0 ||
784             oldCount == 0 && m_data->m_scrollbarsAvoidingResizer > 0)
785             invalidateRect(windowResizerRect());
786     }
787 }
788
789 void ScrollView::setParent(ScrollView* parentView)
790 {
791     if (!parentView && m_data->m_scrollbarsAvoidingResizer && parent() && parent()->isFrameView())
792         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(false);
793     Widget::setParent(parentView);
794 }
795
796 void ScrollView::addToDirtyRegion(const IntRect& containingWindowRect)
797 {
798     ASSERT(isFrameView());
799     const FrameView* frameView = static_cast<const FrameView*>(this);
800     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
801     if (!page)
802         return;
803     page->chrome()->addToDirtyRegion(containingWindowRect);
804 }
805
806 void ScrollView::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect)
807 {
808     ASSERT(isFrameView());
809     const FrameView* frameView = static_cast<const FrameView*>(this);
810     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
811     if (!page)
812         return;
813     page->chrome()->scrollBackingStore(dx, dy, scrollViewRect, clipRect);
814 }
815
816 void ScrollView::updateBackingStore()
817 {
818     ASSERT(isFrameView());
819     const FrameView* frameView = static_cast<const FrameView*>(this);
820     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
821     if (!page)
822         return;
823     page->chrome()->updateBackingStore();
824 }
825
826 void ScrollView::incrementNativeWidgetCount()
827 {
828     ++m_data->m_nativeWidgets;
829 }
830
831 void ScrollView::decrementNativeWidgetCount()
832 {
833     --m_data->m_nativeWidgets;
834 }
835
836 bool ScrollView::hasNativeWidgets() const
837 {
838     return m_data->m_nativeWidgets != 0;
839 }
840
841 }
842
843 // vim: ts=4 sw=4 et