Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / view / src / nsScrollPortView.cpp
blob8f6fb574d0ceec2ac6f01f47dea9945349b9dd2b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Neil Cronin (neil@rackle.com)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsScrollPortView.h"
40 #include "nsIWidget.h"
41 #include "nsIDeviceContext.h"
42 #include "nsGUIEvent.h"
43 #include "nsWidgetsCID.h"
44 #include "nsViewsCID.h"
45 #include "nsIScrollableView.h"
46 #include "nsILookAndFeel.h"
47 #include "nsISupportsArray.h"
48 #include "nsIScrollPositionListener.h"
49 #include "nsIRegion.h"
50 #include "nsViewManager.h"
51 #include "nsIPrefBranch.h"
52 #include "nsIPrefService.h"
53 #include "nsCOMPtr.h"
54 #include "nsServiceManagerUtils.h"
56 #include <math.h>
58 static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
60 #define SMOOTH_SCROLL_MSECS_PER_FRAME 10
61 #define SMOOTH_SCROLL_FRAMES 10
63 #define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
65 class AsyncScroll {
66 public:
67 AsyncScroll() {}
68 ~AsyncScroll() {
69 if (mScrollTimer) mScrollTimer->Cancel();
72 nsCOMPtr<nsITimer> mScrollTimer;
73 PRInt32 mVelocities[SMOOTH_SCROLL_FRAMES*2];
74 PRInt32 mFrameIndex;
75 PRPackedBool mIsSmoothScroll;
78 nsScrollPortView::nsScrollPortView(nsViewManager* aViewManager)
79 : nsView(aViewManager)
81 mOffsetX = mOffsetY = 0;
82 mDestinationX = mDestinationY = 0;
83 nsCOMPtr<nsIDeviceContext> dev;
84 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
85 mLineHeight = dev->AppUnitsPerInch() / 6; // 12 pt
87 mListeners = nsnull;
88 mAsyncScroll = nsnull;
91 nsScrollPortView::~nsScrollPortView()
93 if (nsnull != mListeners) {
94 mListeners->Clear();
95 NS_RELEASE(mListeners);
98 if (nsnull != mViewManager) {
99 nsIScrollableView* scrollingView;
100 mViewManager->GetRootScrollableView(&scrollingView);
101 if ((nsnull != scrollingView) && (this == scrollingView)) {
102 mViewManager->SetRootScrollableView(nsnull);
106 delete mAsyncScroll;
109 nsresult nsScrollPortView::QueryInterface(const nsIID& aIID, void** aInstancePtr)
111 if (nsnull == aInstancePtr) {
112 return NS_ERROR_NULL_POINTER;
114 *aInstancePtr = nsnull;
115 if (aIID.Equals(NS_GET_IID(nsIScrollableView))) {
116 *aInstancePtr = (void*)(nsIScrollableView*)this;
117 return NS_OK;
120 return nsView::QueryInterface(aIID, aInstancePtr);
123 NS_IMETHODIMP_(nsIView*) nsScrollPortView::View()
125 return this;
128 NS_IMETHODIMP nsScrollPortView::AddScrollPositionListener(nsIScrollPositionListener* aListener)
130 if (nsnull == mListeners) {
131 nsresult rv = NS_NewISupportsArray(&mListeners);
132 if (NS_FAILED(rv))
133 return rv;
135 return mListeners->AppendElement(aListener);
138 NS_IMETHODIMP nsScrollPortView::RemoveScrollPositionListener(nsIScrollPositionListener* aListener)
140 if (nsnull != mListeners) {
141 return mListeners->RemoveElement(aListener);
143 return NS_ERROR_FAILURE;
146 NS_IMETHODIMP nsScrollPortView::CreateScrollControls(nsNativeWidget aNative)
148 nsWidgetInitData initData;
149 initData.clipChildren = PR_TRUE;
150 initData.clipSiblings = PR_TRUE;
152 CreateWidget(kWidgetCID, &initData,
153 mWindow ? nsnull : aNative);
155 return NS_OK;
158 NS_IMETHODIMP nsScrollPortView::GetContainerSize(nscoord *aWidth, nscoord *aHeight) const
160 if (!aWidth || !aHeight)
161 return NS_ERROR_NULL_POINTER;
163 *aWidth = 0;
164 *aHeight = 0;
166 nsView *scrolledView = GetScrolledView();
168 if (!scrolledView)
169 return NS_ERROR_FAILURE;
171 nsSize sz;
172 scrolledView->GetDimensions(sz);
173 *aWidth = sz.width;
174 *aHeight = sz.height;
175 return NS_OK;
178 static void ComputeVelocities(PRInt32 aCurVelocity, nscoord aCurPos, nscoord aDstPos,
179 PRInt32* aVelocities, PRInt32 aP2A) {
180 // scrolling always works in units of whole pixels. So compute velocities
181 // in pixels and then scale them up. This ensures, for example, that
182 // a 1-pixel scroll isn't broken into N frames of 1/N pixels each, each
183 // frame increment being rounded to 0 whole pixels.
184 aCurPos = NSAppUnitsToIntPixels(aCurPos, aP2A);
185 aDstPos = NSAppUnitsToIntPixels(aDstPos, aP2A);
187 PRInt32 i;
188 PRInt32 direction = (aCurPos < aDstPos ? 1 : -1);
189 PRInt32 absDelta = (aDstPos - aCurPos)*direction;
190 PRInt32 baseVelocity = absDelta/SMOOTH_SCROLL_FRAMES;
192 for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
193 aVelocities[i*2] = baseVelocity;
195 nscoord total = baseVelocity*SMOOTH_SCROLL_FRAMES;
196 for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
197 if (total < absDelta) {
198 aVelocities[i*2]++;
199 total++;
202 NS_ASSERTION(total == absDelta, "Invalid velocity sum");
204 PRInt32 scale = NSIntPixelsToAppUnits(direction, aP2A);
205 for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
206 aVelocities[i*2] *= scale;
210 static nsresult ClampScrollValues(nscoord& aX, nscoord& aY, nsScrollPortView* aThis) {
211 // make sure the new position in in bounds
212 nsView* scrolledView = aThis->GetScrolledView();
213 if (!scrolledView) return NS_ERROR_FAILURE;
215 nsRect scrolledRect;
216 scrolledView->GetDimensions(scrolledRect);
218 nsSize portSize;
219 aThis->GetDimensions(portSize);
221 nscoord maxX = scrolledRect.XMost() - portSize.width;
222 nscoord maxY = scrolledRect.YMost() - portSize.height;
224 if (aX > maxX)
225 aX = maxX;
227 if (aY > maxY)
228 aY = maxY;
230 if (aX < scrolledRect.x)
231 aX = scrolledRect.x;
233 if (aY < scrolledRect.y)
234 aY = scrolledRect.y;
236 return NS_OK;
240 * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
241 * based on the setting of the smooth scroll pref
243 NS_IMETHODIMP nsScrollPortView::ScrollTo(nscoord aDestinationX, nscoord aDestinationY,
244 PRUint32 aUpdateFlags)
246 // do nothing if the we aren't scrolling.
247 if (aDestinationX == mDestinationX && aDestinationY == mDestinationY) {
248 // kill any in-progress smooth scroll
249 delete mAsyncScroll;
250 mAsyncScroll = nsnull;
251 return NS_OK;
254 mDestinationX = aDestinationX;
255 mDestinationY = aDestinationY;
256 ClampScrollValues(mDestinationX, mDestinationY, this);
258 if (!(aUpdateFlags & (NS_VMREFRESH_DEFERRED | NS_VMREFRESH_SMOOTHSCROLL))) {
259 // Asynchronous scrolling is not allowed, so we'll kill any existing
260 // async-scrolling process and do an instant scroll
261 delete mAsyncScroll;
262 mAsyncScroll = nsnull;
263 return ScrollToImpl(mDestinationX, mDestinationY);
266 PRInt32 currentVelocityX = 0;
267 PRInt32 currentVelocityY = 0;
268 PRBool isSmoothScroll = (aUpdateFlags & NS_VMREFRESH_SMOOTHSCROLL) &&
269 IsSmoothScrollingEnabled();
271 if (mAsyncScroll) {
272 if (mAsyncScroll->mIsSmoothScroll) {
273 currentVelocityX = mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2];
274 currentVelocityY = mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2 + 1];
276 } else {
277 mAsyncScroll = new AsyncScroll;
278 if (mAsyncScroll) {
279 mAsyncScroll->mScrollTimer = do_CreateInstance("@mozilla.org/timer;1");
280 if (!mAsyncScroll->mScrollTimer) {
281 delete mAsyncScroll;
282 mAsyncScroll = nsnull;
285 if (!mAsyncScroll) {
286 // some allocation failed. Scroll the normal way.
287 return ScrollToImpl(mDestinationX, mDestinationY);
289 if (isSmoothScroll) {
290 mAsyncScroll->mScrollTimer->InitWithFuncCallback(
291 AsyncScrollCallback, this, SMOOTH_SCROLL_MSECS_PER_FRAME,
292 nsITimer::TYPE_REPEATING_PRECISE);
293 } else {
294 mAsyncScroll->mScrollTimer->InitWithFuncCallback(
295 AsyncScrollCallback, this, 0, nsITimer::TYPE_ONE_SHOT);
299 mAsyncScroll->mFrameIndex = 0;
300 mAsyncScroll->mIsSmoothScroll = isSmoothScroll;
302 if (isSmoothScroll) {
303 nsCOMPtr<nsIDeviceContext> dev;
304 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
305 PRInt32 p2a = dev->AppUnitsPerDevPixel();
307 // compute velocity vectors
308 ComputeVelocities(currentVelocityX, mOffsetX, mDestinationX,
309 mAsyncScroll->mVelocities, p2a);
310 ComputeVelocities(currentVelocityY, mOffsetY, mDestinationY,
311 mAsyncScroll->mVelocities + 1, p2a);
314 return NS_OK;
317 static void AdjustChildWidgets(nsView *aView,
318 nsPoint aWidgetToParentViewOrigin, PRInt32 aP2A, PRBool aInvalidate)
320 if (aView->HasWidget()) {
321 nsIWidget* widget = aView->GetWidget();
322 nsWindowType type;
323 widget->GetWindowType(type);
324 if (type != eWindowType_popup) {
325 nsRect bounds = aView->GetBounds();
326 nsPoint widgetOrigin = aWidgetToParentViewOrigin
327 + nsPoint(bounds.x, bounds.y);
328 widget->Move(NSAppUnitsToIntPixels(widgetOrigin.x, aP2A),
329 NSAppUnitsToIntPixels(widgetOrigin.y, aP2A));
330 if (aInvalidate) {
331 // Force the widget and everything in it to repaint. We can't
332 // just use Invalidate because the widget might have child
333 // widgets and they wouldn't get updated. We can't call
334 // UpdateView(aView) because the area to be repainted might be
335 // outside aView's clipped bounds. This isn't the greatest way
336 // to achieve this, perhaps, but it works.
337 widget->Show(PR_FALSE);
338 widget->Show(PR_TRUE);
341 } else {
342 // Don't recurse if the view haLs a widget, because we adjusted the view's
343 // widget position, and its child widgets are relative to its positon
344 nsPoint widgetToViewOrigin = aWidgetToParentViewOrigin
345 + aView->GetPosition();
347 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling())
349 AdjustChildWidgets(kid, widgetToViewOrigin, aP2A, aInvalidate);
355 NS_IMETHODIMP nsScrollPortView::SetScrolledView(nsIView *aScrolledView)
357 NS_ASSERTION(GetFirstChild() == nsnull || GetFirstChild()->GetNextSibling() == nsnull,
358 "Error scroll port has too many children");
360 // if there is already a child so remove it
361 if (GetFirstChild() != nsnull)
363 mViewManager->RemoveChild(GetFirstChild());
366 return mViewManager->InsertChild(this, aScrolledView, 0);
369 NS_IMETHODIMP nsScrollPortView::GetScrolledView(nsIView *&aScrolledView) const
371 aScrolledView = GetScrolledView();
372 return NS_OK;
375 NS_IMETHODIMP nsScrollPortView::GetScrollPosition(nscoord &aX, nscoord &aY) const
377 aX = mOffsetX;
378 aY = mOffsetY;
380 return NS_OK;
383 NS_IMETHODIMP nsScrollPortView::SetScrollProperties(PRUint32 aProperties)
385 mScrollProperties = aProperties;
386 return NS_OK;
389 NS_IMETHODIMP nsScrollPortView::GetScrollProperties(PRUint32 *aProperties)
391 *aProperties = mScrollProperties;
392 return NS_OK;
395 NS_IMETHODIMP nsScrollPortView::SetLineHeight(nscoord aHeight)
397 mLineHeight = aHeight;
398 return NS_OK;
401 NS_IMETHODIMP nsScrollPortView::GetLineHeight(nscoord *aHeight)
403 *aHeight = mLineHeight;
404 return NS_OK;
407 NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinesY,
408 PRUint32 aUpdateFlags)
410 nscoord dx = mLineHeight*aNumLinesX;
411 nscoord dy = mLineHeight*aNumLinesY;
413 return ScrollTo(mDestinationX + dx, mDestinationY + dy, aUpdateFlags);
416 NS_IMETHODIMP nsScrollPortView::GetPageScrollDistances(nsSize *aDistances)
418 nsSize size;
419 GetDimensions(size);
421 // The page increment is the size of the page, minus the smaller of
422 // 10% of the size or 2 lines.
423 aDistances->width = size.width - PR_MIN(size.width / 10, 2 * mLineHeight);
424 aDistances->height = size.height - PR_MIN(size.height / 10, 2 * mLineHeight);
426 return NS_OK;
429 NS_IMETHODIMP nsScrollPortView::ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPagesY,
430 PRUint32 aUpdateFlags)
432 nsSize delta;
433 GetPageScrollDistances(&delta);
435 // put in the number of pages.
436 delta.width *= aNumPagesX;
437 delta.height *= aNumPagesY;
439 return ScrollTo(mDestinationX + delta.width, mDestinationY + delta.height,
440 aUpdateFlags);
443 NS_IMETHODIMP nsScrollPortView::ScrollByWhole(PRBool aTop,
444 PRUint32 aUpdateFlags)
446 nscoord newPos = 0;
448 if (!aTop) {
449 nsSize scrolledSize;
450 nsView* scrolledView = GetScrolledView();
451 scrolledView->GetDimensions(scrolledSize);
452 newPos = scrolledSize.height;
455 ScrollTo(mDestinationX, newPos, aUpdateFlags);
457 return NS_OK;
460 NS_IMETHODIMP nsScrollPortView::ScrollByPixels(PRInt32 aNumPixelsX,
461 PRInt32 aNumPixelsY,
462 PRUint32 aUpdateFlags)
464 nsCOMPtr<nsIDeviceContext> dev;
465 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
466 PRInt32 p2a = dev->AppUnitsPerDevPixel();
468 nscoord dx = NSIntPixelsToAppUnits(aNumPixelsX, p2a);
469 nscoord dy = NSIntPixelsToAppUnits(aNumPixelsY, p2a);
471 return ScrollTo(mDestinationX + dx, mDestinationY + dy, aUpdateFlags);
474 NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
475 PRBool aForward,
476 PRBool &aResult)
478 nscoord offset = aHorizontal ? mOffsetX : mOffsetY;
480 nsView* scrolledView = GetScrolledView();
481 if (!scrolledView) {
482 aResult = PR_FALSE;
483 return NS_ERROR_FAILURE;
486 nsRect scrolledRect;
487 scrolledView->GetDimensions(scrolledRect);
489 // Can scroll to Top or to Left?
490 if (!aForward) {
491 aResult = offset > (aHorizontal ? scrolledRect.x : scrolledRect.y);
492 return NS_OK;
495 nsSize portSize;
496 GetDimensions(portSize);
498 nsCOMPtr<nsIDeviceContext> dev;
499 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
500 PRInt32 p2a = dev->AppUnitsPerDevPixel();
502 nscoord max;
503 if (aHorizontal) {
504 max = scrolledRect.XMost() - portSize.width;
505 // Round by pixel
506 nscoord maxPx = NSAppUnitsToIntPixels(max, p2a);
507 max = NSIntPixelsToAppUnits(maxPx, p2a);
508 } else {
509 max = scrolledRect.YMost() - portSize.height;
510 // Round by pixel
511 nscoord maxPx = NSAppUnitsToIntPixels(max, p2a);
512 max = NSIntPixelsToAppUnits(maxPx, p2a);
515 // Can scroll to Bottom or to Right?
516 aResult = (offset < max) ? PR_TRUE : PR_FALSE;
518 return NS_OK;
521 void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsPoint aPixDelta,
522 PRInt32 aP2A)
524 if (aTwipsDelta.x != 0 || aTwipsDelta.y != 0)
526 /* If we should invalidate our wrapped view, we should do so at this
527 * point.
529 if (aScrolledView->NeedsInvalidateFrameOnScroll())
530 GetViewManager()->GetViewObserver()->InvalidateFrameForView(aScrolledView);
532 nsIWidget *scrollWidget = GetWidget();
533 nsRegion updateRegion;
534 PRBool canBitBlit = scrollWidget &&
535 ((mScrollProperties & NS_SCROLL_PROPERTY_ALWAYS_BLIT) ||
536 mViewManager->CanScrollWithBitBlt(aScrolledView, aTwipsDelta, &updateRegion));
538 if (canBitBlit) {
539 // We're going to bit-blit. Let the viewmanager know so it can
540 // adjust dirty regions appropriately.
541 mViewManager->WillBitBlit(this, aTwipsDelta);
544 if (!scrollWidget)
546 NS_ASSERTION(!canBitBlit, "Someone screwed up");
547 nsPoint offsetToWidget;
548 GetNearestWidget(&offsetToWidget);
549 // We're moving the child widgets because we are scrolling. But
550 // the child widgets may stick outside our bounds, so their area
551 // may include area that's not supposed to be scrolled. We need
552 // to invalidate to ensure that any such area is properly
553 // repainted back to the right rendering.
554 AdjustChildWidgets(aScrolledView, offsetToWidget, aP2A, PR_TRUE);
555 // If we don't have a scroll widget then we must just update.
556 // We should call this after fixing up the widget positions to be
557 // consistent with the view hierarchy.
558 mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
559 } else if (!canBitBlit) {
560 // We can't blit for some reason.
561 // Just update the view and adjust widgets
562 // Recall that our widget's origin is at our bounds' top-left
563 nsRect bounds(GetBounds());
564 nsPoint topLeft(bounds.x, bounds.y);
565 AdjustChildWidgets(aScrolledView,
566 GetPosition() - topLeft, aP2A, PR_FALSE);
567 // We should call this after fixing up the widget positions to be
568 // consistent with the view hierarchy.
569 mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
570 } else { // if we can blit and have a scrollwidget then scroll.
571 nsRect* toScrollPtr = nsnull;
573 #ifdef XP_WIN
574 nsRect toScroll;
575 if (!updateRegion.IsEmpty()) {
576 nsRegion regionToScroll;
577 regionToScroll.Sub(nsRect(nsPoint(0,0), GetBounds().Size()),
578 updateRegion);
579 nsRegionRectIterator iter(regionToScroll);
580 nsRect biggestRect(0,0,0,0);
581 const nsRect* r;
582 for (r = iter.Next(); r; r = iter.Next()) {
583 if (r->width*r->height > biggestRect.width*biggestRect.height) {
584 biggestRect = *r;
587 toScrollPtr = &toScroll;
588 biggestRect.ScaleRoundIn(1.0/aP2A);
589 toScroll = biggestRect;
590 biggestRect *= aP2A;
591 regionToScroll.Sub(regionToScroll, biggestRect);
592 updateRegion.Or(updateRegion, regionToScroll);
594 #endif
596 // Scroll the contents of the widget by the specified amount, and scroll
597 // the child widgets
598 scrollWidget->Scroll(aPixDelta.x, aPixDelta.y, toScrollPtr);
599 mViewManager->UpdateViewAfterScroll(this, updateRegion);
604 NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY)
606 PRInt32 dxPx = 0, dyPx = 0;
608 // convert to pixels
609 nsCOMPtr<nsIDeviceContext> dev;
610 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
611 PRInt32 p2a = dev->AppUnitsPerDevPixel();
613 // Update the scrolled view's position
614 nsresult rv = ClampScrollValues(aX, aY, this);
615 if (NS_FAILED(rv)) {
616 return rv;
619 PRInt32 xPixels = NSAppUnitsToIntPixels(aX, p2a);
620 PRInt32 yPixels = NSAppUnitsToIntPixels(aY, p2a);
622 aX = NSIntPixelsToAppUnits(xPixels, p2a);
623 aY = NSIntPixelsToAppUnits(yPixels, p2a);
625 // do nothing if the we aren't scrolling.
626 // this needs to be rechecked because of the clamping and
627 // rounding
628 if (aX == mOffsetX && aY == mOffsetY) {
629 return NS_OK;
632 // figure out the diff by comparing old pos to new
633 dxPx = NSAppUnitsToIntPixels(mOffsetX, p2a) - xPixels;
634 dyPx = NSAppUnitsToIntPixels(mOffsetY, p2a) - yPixels;
636 // notify the listeners.
637 PRUint32 listenerCount;
638 const nsIID& kScrollPositionListenerIID = NS_GET_IID(nsIScrollPositionListener);
639 nsIScrollPositionListener* listener;
640 if (nsnull != mListeners) {
641 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
642 for (PRUint32 i = 0; i < listenerCount; i++) {
643 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
644 listener->ScrollPositionWillChange(this, aX, aY);
645 NS_RELEASE(listener);
651 nsView* scrolledView = GetScrolledView();
652 if (!scrolledView) return NS_ERROR_FAILURE;
654 // move the scrolled view to the new location
655 // Note that child widgets may be scrolled by the native widget scrolling,
656 // so don't update their positions
657 scrolledView->SetPositionIgnoringChildWidgets(-aX, -aY);
659 // notify the listeners.
660 if (nsnull != mListeners) {
661 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
662 for (PRUint32 i = 0; i < listenerCount; i++) {
663 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
664 listener->ViewPositionDidChange(this);
665 NS_RELEASE(listener);
671 nsPoint twipsDelta(aX - mOffsetX, aY - mOffsetY);
673 // store the new position
674 mOffsetX = aX;
675 mOffsetY = aY;
677 Scroll(scrolledView, twipsDelta, nsPoint(dxPx, dyPx), p2a);
679 mViewManager->SynthesizeMouseMove(PR_TRUE);
681 // notify the listeners.
682 if (nsnull != mListeners) {
683 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
684 for (PRUint32 i = 0; i < listenerCount; i++) {
685 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
686 listener->ScrollPositionDidChange(this, aX, aY);
687 NS_RELEASE(listener);
693 return NS_OK;
696 PRBool nsScrollPortView::IsSmoothScrollingEnabled() {
697 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
698 if (prefs) {
699 PRBool enabled;
700 nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled);
701 if (NS_SUCCEEDED(rv)) {
702 return enabled;
705 return PR_FALSE;
709 * Callback function from timer used in nsScrollPortView::ScrollTo
711 void
712 nsScrollPortView::AsyncScrollCallback(nsITimer *aTimer, void* anInstance)
714 nsScrollPortView* self = static_cast<nsScrollPortView*>(anInstance);
715 if (self) {
716 self->IncrementalScroll();
721 * manages data members and calls to ScrollTo from the (static) AsyncScrollCallback method
723 void
724 nsScrollPortView::IncrementalScroll()
726 if (!mAsyncScroll)
727 return;
729 if (mAsyncScroll->mIsSmoothScroll) {
730 if (mAsyncScroll->mFrameIndex < SMOOTH_SCROLL_FRAMES) {
731 ScrollToImpl(mOffsetX + mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2],
732 mOffsetY + mAsyncScroll->mVelocities[mAsyncScroll->mFrameIndex*2 + 1]);
733 mAsyncScroll->mFrameIndex++;
734 return;
736 } else {
737 ScrollToImpl(mDestinationX, mDestinationY);
739 delete mAsyncScroll;
740 mAsyncScroll = nsnull;