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