Bug 448909 - Need more controls WHATWG Video tag (followup patch). r=mconnor
[wine-gecko.git] / view / src / nsScrollPortView.cpp
blob53586af174527093620273289fe9a43ffe717191
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 SmoothScroll {
66 public:
67 SmoothScroll() {}
68 ~SmoothScroll() {
69 if (mScrollAnimationTimer) mScrollAnimationTimer->Cancel();
72 nsCOMPtr<nsITimer> mScrollAnimationTimer;
73 PRInt32 mVelocities[SMOOTH_SCROLL_FRAMES*2];
74 PRInt32 mFrameIndex;
75 nscoord mDestinationX;
76 nscoord mDestinationY;
79 nsScrollPortView::nsScrollPortView(nsViewManager* aViewManager)
80 : nsView(aViewManager)
82 mOffsetX = mOffsetY = 0;
83 nsCOMPtr<nsIDeviceContext> dev;
84 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
85 mLineHeight = dev->AppUnitsPerInch() / 6; // 12 pt
87 mListeners = nsnull;
88 mSmoothScroll = 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 mSmoothScroll;
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 == mOffsetX && aDestinationY == mOffsetY) {
248 // kill any in-progress smooth scroll
249 delete mSmoothScroll;
250 mSmoothScroll = nsnull;
251 return NS_OK;
254 if ((aUpdateFlags & NS_VMREFRESH_SMOOTHSCROLL) == 0
255 || !IsSmoothScrollingEnabled()) {
256 // Smooth scrolling is not allowed, so we'll kill any existing smooth-scrolling process
257 // and do an instant scroll
258 delete mSmoothScroll;
259 mSmoothScroll = nsnull;
260 return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
263 PRInt32 currentVelocityX;
264 PRInt32 currentVelocityY;
266 if (mSmoothScroll) {
267 currentVelocityX = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2];
268 currentVelocityY = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1];
269 } else {
270 currentVelocityX = 0;
271 currentVelocityY = 0;
273 mSmoothScroll = new SmoothScroll;
274 if (mSmoothScroll) {
275 mSmoothScroll->mScrollAnimationTimer = do_CreateInstance("@mozilla.org/timer;1");
276 if (!mSmoothScroll->mScrollAnimationTimer) {
277 delete mSmoothScroll;
278 mSmoothScroll = nsnull;
281 if (!mSmoothScroll) {
282 // some allocation failed. Scroll the normal way.
283 return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
285 mSmoothScroll->mScrollAnimationTimer->InitWithFuncCallback(
286 SmoothScrollAnimationCallback, this, SMOOTH_SCROLL_MSECS_PER_FRAME,
287 nsITimer::TYPE_REPEATING_PRECISE);
288 mSmoothScroll->mDestinationX = mOffsetX;
289 mSmoothScroll->mDestinationY = mOffsetY;
292 // need to store these so we know when to stop scrolling
293 // Treat the desired scroll destination as an offset
294 // relative to the current position. This makes things
295 // work when someone starts a smooth scroll
296 // while an existing smooth scroll has not yet been
297 // completed.
298 mSmoothScroll->mDestinationX += aDestinationX - mOffsetX;
299 mSmoothScroll->mDestinationY += aDestinationY - mOffsetY;
300 mSmoothScroll->mFrameIndex = 0;
301 ClampScrollValues(mSmoothScroll->mDestinationX, mSmoothScroll->mDestinationY, this);
303 nsCOMPtr<nsIDeviceContext> dev;
304 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
305 PRInt32 p2a = dev->AppUnitsPerDevPixel();
307 // compute velocity vectors
308 ComputeVelocities(currentVelocityX, mOffsetX,
309 mSmoothScroll->mDestinationX, mSmoothScroll->mVelocities,
310 p2a);
311 ComputeVelocities(currentVelocityY, mOffsetY,
312 mSmoothScroll->mDestinationY, mSmoothScroll->mVelocities + 1,
313 p2a);
315 return NS_OK;
318 static void AdjustChildWidgets(nsView *aView,
319 nsPoint aWidgetToParentViewOrigin, PRInt32 aP2A, PRBool aInvalidate)
321 if (aView->HasWidget()) {
322 nsIWidget* widget = aView->GetWidget();
323 nsWindowType type;
324 widget->GetWindowType(type);
325 if (type != eWindowType_popup) {
326 nsRect bounds = aView->GetBounds();
327 nsPoint widgetOrigin = aWidgetToParentViewOrigin
328 + nsPoint(bounds.x, bounds.y);
329 widget->Move(NSAppUnitsToIntPixels(widgetOrigin.x, aP2A),
330 NSAppUnitsToIntPixels(widgetOrigin.y, aP2A));
331 if (aInvalidate) {
332 // Force the widget and everything in it to repaint. We can't
333 // just use Invalidate because the widget might have child
334 // widgets and they wouldn't get updated. We can't call
335 // UpdateView(aView) because the area to be repainted might be
336 // outside aView's clipped bounds. This isn't the greatest way
337 // to achieve this, perhaps, but it works.
338 widget->Show(PR_FALSE);
339 widget->Show(PR_TRUE);
342 } else {
343 // Don't recurse if the view haLs a widget, because we adjusted the view's
344 // widget position, and its child widgets are relative to its positon
345 nsPoint widgetToViewOrigin = aWidgetToParentViewOrigin
346 + aView->GetPosition();
348 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling())
350 AdjustChildWidgets(kid, widgetToViewOrigin, aP2A, aInvalidate);
356 NS_IMETHODIMP nsScrollPortView::SetScrolledView(nsIView *aScrolledView)
358 NS_ASSERTION(GetFirstChild() == nsnull || GetFirstChild()->GetNextSibling() == nsnull,
359 "Error scroll port has too many children");
361 // if there is already a child so remove it
362 if (GetFirstChild() != nsnull)
364 mViewManager->RemoveChild(GetFirstChild());
367 return mViewManager->InsertChild(this, aScrolledView, 0);
370 NS_IMETHODIMP nsScrollPortView::GetScrolledView(nsIView *&aScrolledView) const
372 aScrolledView = GetScrolledView();
373 return NS_OK;
376 NS_IMETHODIMP nsScrollPortView::GetScrollPosition(nscoord &aX, nscoord &aY) const
378 aX = mOffsetX;
379 aY = mOffsetY;
381 return NS_OK;
384 NS_IMETHODIMP nsScrollPortView::SetScrollProperties(PRUint32 aProperties)
386 mScrollProperties = aProperties;
387 return NS_OK;
390 NS_IMETHODIMP nsScrollPortView::GetScrollProperties(PRUint32 *aProperties)
392 *aProperties = mScrollProperties;
393 return NS_OK;
396 NS_IMETHODIMP nsScrollPortView::SetLineHeight(nscoord aHeight)
398 mLineHeight = aHeight;
399 return NS_OK;
402 NS_IMETHODIMP nsScrollPortView::GetLineHeight(nscoord *aHeight)
404 *aHeight = mLineHeight;
405 return NS_OK;
408 NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinesY)
410 nscoord dx = mLineHeight*aNumLinesX;
411 nscoord dy = mLineHeight*aNumLinesY;
413 return ScrollTo(mOffsetX + dx, mOffsetY + dy, NS_VMREFRESH_SMOOTHSCROLL);
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)
431 nsSize delta;
432 GetPageScrollDistances(&delta);
434 // put in the number of pages.
435 delta.width *= aNumPagesX;
436 delta.height *= aNumPagesY;
438 return ScrollTo(mOffsetX + delta.width, mOffsetY + delta.height,
439 NS_VMREFRESH_SMOOTHSCROLL);
442 NS_IMETHODIMP nsScrollPortView::ScrollByWhole(PRBool aTop)
444 nscoord newPos = 0;
446 if (!aTop) {
447 nsSize scrolledSize;
448 nsView* scrolledView = GetScrolledView();
449 scrolledView->GetDimensions(scrolledSize);
450 newPos = scrolledSize.height;
453 ScrollTo(mOffsetX, newPos, 0);
455 return NS_OK;
458 NS_IMETHODIMP nsScrollPortView::ScrollByPixels(PRInt32 aNumPixelsX,
459 PRInt32 aNumPixelsY)
461 nsCOMPtr<nsIDeviceContext> dev;
462 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
463 PRInt32 p2a = dev->AppUnitsPerDevPixel();
465 nscoord dx = NSIntPixelsToAppUnits(aNumPixelsX, p2a);
466 nscoord dy = NSIntPixelsToAppUnits(aNumPixelsY, p2a);
468 return ScrollTo(mOffsetX + dx, mOffsetY + dy, 0);
471 NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
472 PRBool aForward,
473 PRBool &aResult)
475 nscoord offset = aHorizontal ? mOffsetX : mOffsetY;
477 nsView* scrolledView = GetScrolledView();
478 if (!scrolledView) {
479 aResult = PR_FALSE;
480 return NS_ERROR_FAILURE;
483 nsRect scrolledRect;
484 scrolledView->GetDimensions(scrolledRect);
486 // Can scroll to Top or to Left?
487 if (!aForward) {
488 aResult = offset > (aHorizontal ? scrolledRect.x : scrolledRect.y);
489 return NS_OK;
492 nsSize portSize;
493 GetDimensions(portSize);
495 nsCOMPtr<nsIDeviceContext> dev;
496 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
497 PRInt32 p2a = dev->AppUnitsPerDevPixel();
499 nscoord max;
500 if (aHorizontal) {
501 max = scrolledRect.XMost() - portSize.width;
502 // Round by pixel
503 nscoord maxPx = NSAppUnitsToIntPixels(max, p2a);
504 max = NSIntPixelsToAppUnits(maxPx, p2a);
505 } else {
506 max = scrolledRect.YMost() - portSize.height;
507 // Round by pixel
508 nscoord maxPx = NSAppUnitsToIntPixels(max, p2a);
509 max = NSIntPixelsToAppUnits(maxPx, p2a);
512 // Can scroll to Bottom or to Right?
513 aResult = (offset < max) ? PR_TRUE : PR_FALSE;
515 return NS_OK;
518 void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsPoint aPixDelta,
519 PRInt32 aP2A)
521 if (aTwipsDelta.x != 0 || aTwipsDelta.y != 0)
523 /* If we should invalidate our wrapped view, we should do so at this
524 * point.
526 if (aScrolledView->NeedsInvalidateFrameOnScroll())
527 GetViewManager()->GetViewObserver()->InvalidateFrameForView(aScrolledView);
529 nsIWidget *scrollWidget = GetWidget();
530 nsRegion updateRegion;
531 PRBool canBitBlit = scrollWidget &&
532 ((mScrollProperties & NS_SCROLL_PROPERTY_ALWAYS_BLIT) ||
533 mViewManager->CanScrollWithBitBlt(aScrolledView, aTwipsDelta, &updateRegion));
535 if (canBitBlit) {
536 // We're going to bit-blit. Let the viewmanager know so it can
537 // adjust dirty regions appropriately.
538 mViewManager->WillBitBlit(this, aTwipsDelta);
541 if (!scrollWidget)
543 NS_ASSERTION(!canBitBlit, "Someone screwed up");
544 nsPoint offsetToWidget;
545 GetNearestWidget(&offsetToWidget);
546 // We're moving the child widgets because we are scrolling. But
547 // the child widgets may stick outside our bounds, so their area
548 // may include area that's not supposed to be scrolled. We need
549 // to invalidate to ensure that any such area is properly
550 // repainted back to the right rendering.
551 AdjustChildWidgets(aScrolledView, offsetToWidget, aP2A, PR_TRUE);
552 // If we don't have a scroll widget then we must just update.
553 // We should call this after fixing up the widget positions to be
554 // consistent with the view hierarchy.
555 mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
556 } else if (!canBitBlit) {
557 // We can't blit for some reason.
558 // Just update the view and adjust widgets
559 // Recall that our widget's origin is at our bounds' top-left
560 nsRect bounds(GetBounds());
561 nsPoint topLeft(bounds.x, bounds.y);
562 AdjustChildWidgets(aScrolledView,
563 GetPosition() - topLeft, aP2A, PR_FALSE);
564 // We should call this after fixing up the widget positions to be
565 // consistent with the view hierarchy.
566 mViewManager->UpdateView(this, NS_VMREFRESH_DEFERRED);
567 } else { // if we can blit and have a scrollwidget then scroll.
568 nsRect* toScrollPtr = nsnull;
570 #ifdef XP_WIN
571 nsRect toScroll;
572 if (!updateRegion.IsEmpty()) {
573 nsRegion regionToScroll;
574 regionToScroll.Sub(nsRect(nsPoint(0,0), GetBounds().Size()),
575 updateRegion);
576 nsRegionRectIterator iter(regionToScroll);
577 nsRect biggestRect(0,0,0,0);
578 const nsRect* r;
579 for (r = iter.Next(); r; r = iter.Next()) {
580 if (r->width*r->height > biggestRect.width*biggestRect.height) {
581 biggestRect = *r;
584 toScrollPtr = &toScroll;
585 biggestRect.ScaleRoundIn(1.0/aP2A);
586 toScroll = biggestRect;
587 biggestRect *= aP2A;
588 regionToScroll.Sub(regionToScroll, biggestRect);
589 updateRegion.Or(updateRegion, regionToScroll);
591 #endif
593 // Scroll the contents of the widget by the specified amount, and scroll
594 // the child widgets
595 scrollWidget->Scroll(aPixDelta.x, aPixDelta.y, toScrollPtr);
596 mViewManager->UpdateViewAfterScroll(this, updateRegion);
601 NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aUpdateFlags)
603 PRInt32 dxPx = 0, dyPx = 0;
605 // convert to pixels
606 nsCOMPtr<nsIDeviceContext> dev;
607 mViewManager->GetDeviceContext(*getter_AddRefs(dev));
608 PRInt32 p2a = dev->AppUnitsPerDevPixel();
610 // Update the scrolled view's position
611 nsresult rv = ClampScrollValues(aX, aY, this);
612 if (NS_FAILED(rv)) {
613 return rv;
616 PRInt32 xPixels = NSAppUnitsToIntPixels(aX, p2a);
617 PRInt32 yPixels = NSAppUnitsToIntPixels(aY, p2a);
619 aX = NSIntPixelsToAppUnits(xPixels, p2a);
620 aY = NSIntPixelsToAppUnits(yPixels, p2a);
622 // do nothing if the we aren't scrolling.
623 // this needs to be rechecked because of the clamping and
624 // rounding
625 if (aX == mOffsetX && aY == mOffsetY) {
626 return NS_OK;
629 // figure out the diff by comparing old pos to new
630 dxPx = NSAppUnitsToIntPixels(mOffsetX, p2a) - xPixels;
631 dyPx = NSAppUnitsToIntPixels(mOffsetY, p2a) - yPixels;
633 // notify the listeners.
634 PRUint32 listenerCount;
635 const nsIID& kScrollPositionListenerIID = NS_GET_IID(nsIScrollPositionListener);
636 nsIScrollPositionListener* listener;
637 if (nsnull != mListeners) {
638 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
639 for (PRUint32 i = 0; i < listenerCount; i++) {
640 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
641 listener->ScrollPositionWillChange(this, aX, aY);
642 NS_RELEASE(listener);
648 nsView* scrolledView = GetScrolledView();
649 if (!scrolledView) return NS_ERROR_FAILURE;
651 // move the scrolled view to the new location
652 // Note that child widgets may be scrolled by the native widget scrolling,
653 // so don't update their positions
654 scrolledView->SetPositionIgnoringChildWidgets(-aX, -aY);
656 // notify the listeners.
657 if (nsnull != mListeners) {
658 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
659 for (PRUint32 i = 0; i < listenerCount; i++) {
660 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
661 listener->ViewPositionDidChange(this);
662 NS_RELEASE(listener);
668 nsPoint twipsDelta(aX - mOffsetX, aY - mOffsetY);
670 // store the new position
671 mOffsetX = aX;
672 mOffsetY = aY;
674 Scroll(scrolledView, twipsDelta, nsPoint(dxPx, dyPx), p2a);
676 mViewManager->SynthesizeMouseMove(PR_TRUE);
678 // notify the listeners.
679 if (nsnull != mListeners) {
680 if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
681 for (PRUint32 i = 0; i < listenerCount; i++) {
682 if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
683 listener->ScrollPositionDidChange(this, aX, aY);
684 NS_RELEASE(listener);
690 return NS_OK;
693 /************************
695 * smooth scrolling methods
697 ***********************/
699 PRBool nsScrollPortView::IsSmoothScrollingEnabled() {
700 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
701 if (prefs) {
702 PRBool enabled;
703 nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled);
704 if (NS_SUCCEEDED(rv)) {
705 return enabled;
708 return PR_FALSE;
712 * Callback function from timer used in nsScrollPortView::DoSmoothScroll
713 * this cleans up the target coordinates and incrementally calls
714 * nsScrollPortView::ScrollTo
716 void
717 nsScrollPortView::SmoothScrollAnimationCallback (nsITimer *aTimer, void* anInstance)
719 nsScrollPortView* self = static_cast<nsScrollPortView*>(anInstance);
720 if (self) {
721 self->IncrementalScroll();
726 * manages data members and calls to ScrollTo from the (static) SmoothScrollAnimationCallback method
728 void
729 nsScrollPortView::IncrementalScroll()
731 if (!mSmoothScroll) {
732 return;
735 if (mSmoothScroll->mFrameIndex < SMOOTH_SCROLL_FRAMES) {
736 ScrollToImpl(mOffsetX + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2],
737 mOffsetY + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1],
739 mSmoothScroll->mFrameIndex++;
740 } else {
741 delete mSmoothScroll;
742 mSmoothScroll = nsnull;