Back to index

lightning-sunbird  0.9+nobinonly
nsScrollPortView.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Neil Cronin (neil@rackle.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsScrollPortView.h"
00040 #include "nsIWidget.h"
00041 #include "nsUnitConversion.h"
00042 #include "nsIDeviceContext.h"
00043 #include "nsGUIEvent.h"
00044 #include "nsWidgetsCID.h"
00045 #include "nsViewsCID.h"
00046 #include "nsIScrollableView.h"
00047 #include "nsILookAndFeel.h"
00048 #include "nsISupportsArray.h"
00049 #include "nsIScrollPositionListener.h"
00050 #include "nsIRegion.h"
00051 #include "nsViewManager.h"
00052 #include "nsIPrefBranch.h"
00053 #include "nsIPrefService.h"
00054 #include "nsCOMPtr.h"
00055 #include "nsServiceManagerUtils.h"
00056 
00057 #include <math.h>
00058 
00059 static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
00060 
00061 #define SMOOTH_SCROLL_MSECS_PER_FRAME 10
00062 #define SMOOTH_SCROLL_FRAMES    10
00063 
00064 #define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
00065 
00066 class SmoothScroll {
00067 public:
00068   SmoothScroll() {}
00069   ~SmoothScroll() {
00070     if (mScrollAnimationTimer) mScrollAnimationTimer->Cancel();
00071   }
00072 
00073   nsCOMPtr<nsITimer> mScrollAnimationTimer;
00074   PRInt32 mVelocities[SMOOTH_SCROLL_FRAMES*2];
00075   PRInt32 mFrameIndex;
00076   nscoord mDestinationX;
00077   nscoord mDestinationY;
00078 };
00079 
00080 nsScrollPortView::nsScrollPortView(nsViewManager* aViewManager)
00081   : nsView(aViewManager)
00082 {
00083   mOffsetX = mOffsetY = 0;
00084   mOffsetXpx = mOffsetYpx = 0;
00085   mLineHeight = NSIntPointsToTwips(12);
00086 
00087   mListeners = nsnull;
00088   mSmoothScroll = nsnull;
00089 
00090   SetClipChildrenToBounds(PR_TRUE);
00091 }
00092 
00093 nsScrollPortView::~nsScrollPortView()
00094 {    
00095   if (nsnull != mListeners) {
00096     mListeners->Clear();
00097     NS_RELEASE(mListeners);
00098   }
00099 
00100   if (nsnull != mViewManager) {
00101      nsIScrollableView* scrollingView;
00102      mViewManager->GetRootScrollableView(&scrollingView);
00103      if ((nsnull != scrollingView) && (this == scrollingView)) {
00104        mViewManager->SetRootScrollableView(nsnull);
00105      }
00106   }
00107 
00108   delete mSmoothScroll;
00109 }
00110 
00111 nsresult nsScrollPortView::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00112 {
00113   if (nsnull == aInstancePtr) {
00114     return NS_ERROR_NULL_POINTER;
00115   }
00116   *aInstancePtr = nsnull;
00117   if (aIID.Equals(NS_GET_IID(nsIScrollableView))) {
00118     *aInstancePtr = (void*)(nsIScrollableView*)this;
00119     return NS_OK;
00120   }
00121   if (aIID.Equals(NS_GET_IID(nsIScrollableView_MOZILLA_1_8_BRANCH))) {
00122     *aInstancePtr = (void*)(nsIScrollableView_MOZILLA_1_8_BRANCH*)this;
00123     return NS_OK;
00124   }
00125 
00126   return nsView::QueryInterface(aIID, aInstancePtr);
00127 }
00128 
00129 NS_IMETHODIMP_(nsIView*) nsScrollPortView::View()
00130 {
00131   return this;
00132 }
00133 
00134 NS_IMETHODIMP nsScrollPortView::AddScrollPositionListener(nsIScrollPositionListener* aListener)
00135 {
00136   if (nsnull == mListeners) {
00137     nsresult rv = NS_NewISupportsArray(&mListeners);
00138     if (NS_FAILED(rv))
00139       return rv;
00140   }
00141   return mListeners->AppendElement(aListener);
00142 }
00143 
00144 NS_IMETHODIMP nsScrollPortView::RemoveScrollPositionListener(nsIScrollPositionListener* aListener)
00145 {
00146   if (nsnull != mListeners) {
00147     return mListeners->RemoveElement(aListener);
00148   }
00149   return NS_ERROR_FAILURE;
00150 }
00151 
00152 NS_IMETHODIMP nsScrollPortView::CreateScrollControls(nsNativeWidget aNative)
00153 {
00154   nsWidgetInitData  initData;
00155   initData.clipChildren = PR_TRUE;
00156   initData.clipSiblings = PR_TRUE;
00157 
00158   CreateWidget(kWidgetCID, &initData,
00159                mWindow ? nsnull : aNative);
00160   
00161   return NS_OK;
00162 }
00163 
00164 NS_IMETHODIMP nsScrollPortView::SetWidget(nsIWidget *aWidget)
00165 {
00166   if (nsnull != aWidget) {
00167     NS_ASSERTION(PR_FALSE, "please don't try and set a widget here");
00168     return NS_ERROR_FAILURE;
00169   }
00170   return NS_OK;
00171 }
00172 
00173 NS_IMETHODIMP nsScrollPortView::GetContainerSize(nscoord *aWidth, nscoord *aHeight) const
00174 {
00175   if (!aWidth || !aHeight)
00176     return NS_ERROR_NULL_POINTER;
00177 
00178   *aWidth  = 0;
00179   *aHeight = 0;
00180 
00181   nsView *scrolledView = GetScrolledView();
00182 
00183   if (!scrolledView)
00184     return NS_ERROR_FAILURE;
00185 
00186   nsSize sz;
00187   scrolledView->GetDimensions(sz);
00188   *aWidth = sz.width;
00189   *aHeight = sz.height;
00190   return NS_OK;
00191 }
00192 
00193 static void ComputeVelocities(PRInt32 aCurVelocity, nscoord aCurPos, nscoord aDstPos,
00194                               PRInt32* aVelocities, float aT2P, float aP2T) {
00195   // scrolling always works in units of whole pixels. So compute velocities
00196   // in pixels and then scale them up. This ensures, for example, that
00197   // a 1-pixel scroll isn't broken into N frames of 1/N pixels each, each
00198   // frame increment being rounded to 0 whole pixels.
00199   aCurPos = NSTwipsToIntPixels(aCurPos, aT2P);
00200   aDstPos = NSTwipsToIntPixels(aDstPos, aT2P);
00201 
00202   PRInt32 i;
00203   PRInt32 direction = (aCurPos < aDstPos ? 1 : -1);
00204   PRInt32 absDelta = (aDstPos - aCurPos)*direction;
00205   PRInt32 baseVelocity = absDelta/SMOOTH_SCROLL_FRAMES;
00206 
00207   for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
00208     aVelocities[i*2] = baseVelocity;
00209   }
00210   nscoord total = baseVelocity*SMOOTH_SCROLL_FRAMES;
00211   for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
00212     if (total < absDelta) {
00213       aVelocities[i*2]++;
00214       total++;
00215     }
00216   }
00217   NS_ASSERTION(total == absDelta, "Invalid velocity sum");
00218 
00219   PRInt32 scale = direction*((PRInt32)aP2T);
00220   for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
00221     aVelocities[i*2] *= scale;
00222   }
00223 }
00224   
00225 static nsresult ClampScrollValues(nscoord& aX, nscoord& aY, nsScrollPortView* aThis) {
00226   // make sure the new position in in bounds
00227   nsView* scrolledView = aThis->GetScrolledView();
00228   if (!scrolledView) return NS_ERROR_FAILURE;
00229   
00230   nsSize scrolledSize;
00231   scrolledView->GetDimensions(scrolledSize);
00232   
00233   nsSize portSize;
00234   aThis->GetDimensions(portSize);
00235   
00236   nscoord maxX = scrolledSize.width - portSize.width;
00237   nscoord maxY = scrolledSize.height - portSize.height;
00238   
00239   if (aX > maxX)
00240     aX = maxX;
00241   
00242   if (aY > maxY)
00243     aY = maxY;
00244   
00245   if (aX < 0)
00246     aX = 0;
00247   
00248   if (aY < 0)
00249     aY = 0;
00250   
00251   return NS_OK;
00252 }
00253   
00254 /*
00255  * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
00256  *  based on the setting of the smooth scroll pref
00257  */
00258 NS_IMETHODIMP nsScrollPortView::ScrollTo(nscoord aDestinationX, nscoord aDestinationY,
00259                                          PRUint32 aUpdateFlags)
00260 {
00261   // do nothing if the we aren't scrolling.
00262   if (aDestinationX == mOffsetX && aDestinationY == mOffsetY) {
00263     // kill any in-progress smooth scroll
00264     delete mSmoothScroll;
00265     mSmoothScroll = nsnull;
00266     return NS_OK;
00267   }
00268   
00269   if ((aUpdateFlags & NS_VMREFRESH_SMOOTHSCROLL) == 0
00270       || !IsSmoothScrollingEnabled()) {
00271     // Smooth scrolling is not allowed, so we'll kill any existing smooth-scrolling process
00272     // and do an instant scroll
00273     delete mSmoothScroll;
00274     mSmoothScroll = nsnull;
00275     return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
00276   }
00277 
00278   PRInt32 currentVelocityX;
00279   PRInt32 currentVelocityY;
00280 
00281   if (mSmoothScroll) {
00282     currentVelocityX = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2];
00283     currentVelocityY = mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1];
00284   } else {
00285     currentVelocityX = 0;
00286     currentVelocityY = 0;
00287 
00288     mSmoothScroll = new SmoothScroll;
00289     if (mSmoothScroll) {
00290       mSmoothScroll->mScrollAnimationTimer = do_CreateInstance("@mozilla.org/timer;1");
00291       if (!mSmoothScroll->mScrollAnimationTimer) {
00292         delete mSmoothScroll;
00293         mSmoothScroll = nsnull;
00294       }
00295     }
00296     if (!mSmoothScroll) {
00297       // some allocation failed. Scroll the normal way.
00298       return ScrollToImpl(aDestinationX, aDestinationY, aUpdateFlags);
00299     }
00300     mSmoothScroll->mScrollAnimationTimer->InitWithFuncCallback(
00301       SmoothScrollAnimationCallback, this, SMOOTH_SCROLL_MSECS_PER_FRAME,
00302       nsITimer::TYPE_REPEATING_PRECISE);
00303     mSmoothScroll->mDestinationX = mOffsetX;
00304     mSmoothScroll->mDestinationY = mOffsetY;
00305   }
00306 
00307   // need to store these so we know when to stop scrolling
00308   // Treat the desired scroll destination as an offset
00309   // relative to the current position. This makes things
00310   // work when someone starts a smooth scroll
00311   // while an existing smooth scroll has not yet been
00312   // completed.
00313   mSmoothScroll->mDestinationX += aDestinationX - mOffsetX;
00314   mSmoothScroll->mDestinationY += aDestinationY - mOffsetY;
00315   mSmoothScroll->mFrameIndex = 0;
00316   ClampScrollValues(mSmoothScroll->mDestinationX, mSmoothScroll->mDestinationY, this);
00317 
00318   nsCOMPtr<nsIDeviceContext> dev;
00319   mViewManager->GetDeviceContext(*getter_AddRefs(dev));
00320   float p2t, t2p;
00321   p2t = dev->DevUnitsToAppUnits(); 
00322   t2p = dev->AppUnitsToDevUnits();
00323 
00324   // compute velocity vectors
00325   ComputeVelocities(currentVelocityX, mOffsetX,
00326                     mSmoothScroll->mDestinationX, mSmoothScroll->mVelocities,
00327                     t2p, p2t);
00328   ComputeVelocities(currentVelocityY, mOffsetY,
00329                     mSmoothScroll->mDestinationY, mSmoothScroll->mVelocities + 1,
00330                     t2p, p2t);
00331 
00332   return NS_OK;
00333 }
00334 
00335 static void AdjustChildWidgets(nsView *aView,
00336   nsPoint aWidgetToParentViewOrigin, float aScale, PRBool aInvalidate)
00337 {
00338   if (aView->HasWidget()) {
00339     nsIWidget* widget = aView->GetWidget();
00340     nsWindowType type;
00341     widget->GetWindowType(type);
00342     if (type != eWindowType_popup) {
00343       nsRect bounds = aView->GetBounds();
00344       nsPoint widgetOrigin = aWidgetToParentViewOrigin
00345         + nsPoint(bounds.x, bounds.y);
00346       widget->Move(NSTwipsToIntPixels(widgetOrigin.x, aScale),
00347                    NSTwipsToIntPixels(widgetOrigin.y, aScale));
00348       if (aInvalidate) {
00349         // Force the widget and everything in it to repaint. We can't
00350         // just use Invalidate because the widget might have child
00351         // widgets and they wouldn't get updated. We can't call
00352         // UpdateView(aView) because the area to be repainted might be
00353         // outside aView's clipped bounds. This isn't the greatest way
00354         // to achieve this, perhaps, but it works.
00355         widget->Show(PR_FALSE);
00356         widget->Show(PR_TRUE);
00357       }
00358     }
00359   } else {
00360     // Don't recurse if the view has a widget, because we adjusted the view's
00361     // widget position, and its child widgets are relative to its positon
00362     nsPoint widgetToViewOrigin = aWidgetToParentViewOrigin
00363       + aView->GetPosition();
00364 
00365     for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling())
00366     {
00367       AdjustChildWidgets(kid, widgetToViewOrigin, aScale, aInvalidate);
00368     }
00369   }
00370 }
00371 
00372 
00373 NS_IMETHODIMP nsScrollPortView::SetScrolledView(nsIView *aScrolledView)
00374 {
00375   NS_ASSERTION(GetFirstChild() == nsnull || GetFirstChild()->GetNextSibling() == nsnull,
00376                "Error scroll port has too many children");
00377 
00378   // if there is already a child so remove it
00379   if (GetFirstChild() != nsnull)
00380   {
00381     mViewManager->RemoveChild(GetFirstChild());
00382   }
00383 
00384   return mViewManager->InsertChild(this, aScrolledView, 0);
00385 }
00386 
00387 NS_IMETHODIMP nsScrollPortView::GetScrolledView(nsIView *&aScrolledView) const
00388 {
00389   aScrolledView = GetScrolledView();
00390   return NS_OK;
00391 }
00392 
00393 NS_IMETHODIMP nsScrollPortView::GetScrollPosition(nscoord &aX, nscoord &aY) const
00394 {
00395   aX = mOffsetX;
00396   aY = mOffsetY;
00397 
00398   return NS_OK;
00399 }
00400 
00401 NS_IMETHODIMP nsScrollPortView::SetScrollProperties(PRUint32 aProperties)
00402 {
00403   mScrollProperties = aProperties;
00404   return NS_OK;
00405 }
00406 
00407 NS_IMETHODIMP nsScrollPortView::GetScrollProperties(PRUint32 *aProperties)
00408 {
00409   *aProperties = mScrollProperties;
00410   return NS_OK;
00411 }
00412 
00413 NS_IMETHODIMP nsScrollPortView::SetLineHeight(nscoord aHeight)
00414 {
00415   mLineHeight = aHeight;
00416   return NS_OK;
00417 }
00418 
00419 NS_IMETHODIMP nsScrollPortView::GetLineHeight(nscoord *aHeight)
00420 {
00421   *aHeight = mLineHeight;
00422   return NS_OK;
00423 }
00424 
00425 NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinesY)
00426 {
00427   nscoord dx = mLineHeight*aNumLinesX;
00428   nscoord dy = mLineHeight*aNumLinesY;
00429 
00430   return ScrollTo(mOffsetX + dx, mOffsetY + dy, NS_VMREFRESH_SMOOTHSCROLL);
00431 }
00432 
00433 NS_IMETHODIMP nsScrollPortView::GetPageScrollDistances(nsSize *aDistances)
00434 {
00435   nsSize size;
00436   GetDimensions(size);
00437 
00438   // The page increment is the size of the page, minus the smaller of
00439   // 10% of the size or 2 lines.
00440   aDistances->width  = size.width  - PR_MIN(size.width  / 10, 2 * mLineHeight);
00441   aDistances->height = size.height - PR_MIN(size.height / 10, 2 * mLineHeight);
00442 
00443   return NS_OK;
00444 }
00445 
00446 NS_IMETHODIMP nsScrollPortView::ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPagesY)
00447 {
00448   nsSize delta;
00449   GetPageScrollDistances(&delta);
00450     
00451   // put in the number of pages.
00452   delta.width *= aNumPagesX;
00453   delta.height *= aNumPagesY;
00454 
00455   return ScrollTo(mOffsetX + delta.width, mOffsetY + delta.height,
00456                   NS_VMREFRESH_SMOOTHSCROLL);
00457 }
00458 
00459 NS_IMETHODIMP nsScrollPortView::ScrollByWhole(PRBool aTop)
00460 {
00461   nscoord   newPos = 0;
00462 
00463   if (!aTop) {
00464     nsSize scrolledSize;
00465     nsView* scrolledView = GetScrolledView();
00466     scrolledView->GetDimensions(scrolledSize);
00467     newPos = scrolledSize.height;
00468   }
00469 
00470   ScrollTo(mOffsetX, newPos, 0);
00471 
00472   return NS_OK;
00473 }
00474 
00475 NS_IMETHODIMP nsScrollPortView::ScrollByPixels(PRInt32 aNumPixelsX,
00476                                                PRInt32 aNumPixelsY)
00477 {
00478   nsCOMPtr<nsIDeviceContext> dev;
00479   mViewManager->GetDeviceContext(*getter_AddRefs(dev));
00480   float p2t = dev->DevUnitsToAppUnits(); 
00481 
00482   nscoord dx = NSIntPixelsToTwips(aNumPixelsX, p2t);
00483   nscoord dy = NSIntPixelsToTwips(aNumPixelsY, p2t);
00484 
00485   return ScrollTo(mOffsetX + dx, mOffsetY + dy, 0);
00486 }
00487 
00488 NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
00489                                           PRBool aForward,
00490                                           PRBool &aResult)
00491 {
00492   nscoord offset = aHorizontal ? mOffsetX : mOffsetY;
00493 
00494   // Can scroll to Top or to Left?
00495   if (!aForward) {
00496     aResult = (offset > 0) ? PR_TRUE : PR_FALSE;
00497     return NS_OK;
00498   }
00499 
00500   nsView* scrolledView = GetScrolledView();
00501   if (!scrolledView) {
00502     aResult = PR_FALSE;
00503     return NS_ERROR_FAILURE;
00504   }
00505 
00506   nsSize scrolledSize;
00507   scrolledView->GetDimensions(scrolledSize);
00508 
00509   nsSize portSize;
00510   GetDimensions(portSize);
00511 
00512   nsCOMPtr<nsIDeviceContext> dev;
00513   mViewManager->GetDeviceContext(*getter_AddRefs(dev));
00514   float t2p, p2t;
00515   t2p = dev->AppUnitsToDevUnits();
00516   p2t = dev->DevUnitsToAppUnits();
00517 
00518   nscoord max;
00519   if (aHorizontal) {
00520     max = scrolledSize.width - portSize.width;
00521     // Round by pixel
00522     nscoord maxPx = NSTwipsToIntPixels(max, t2p);
00523     max = NSIntPixelsToTwips(maxPx, p2t);
00524   } else {
00525     max = scrolledSize.height - portSize.height;
00526     // Round by pixel
00527     nscoord maxPx = NSTwipsToIntPixels(max, t2p);
00528     max = NSIntPixelsToTwips(maxPx, p2t);
00529   }
00530 
00531   // Can scroll to Bottom or to Right?
00532   aResult = (offset < max) ? PR_TRUE : PR_FALSE;
00533 
00534   return NS_OK;
00535 }
00536 
00537 PRBool nsScrollPortView::CannotBitBlt(nsView* aScrolledView)
00538 {
00539   PRUint32  scrolledViewFlags = aScrolledView->GetViewFlags();
00540 
00541   return (mScrollProperties & NS_SCROLL_PROPERTY_NEVER_BLIT) ||
00542          (scrolledViewFlags & NS_VIEW_FLAG_DONT_BITBLT) ||
00543          (!(mScrollProperties & NS_SCROLL_PROPERTY_ALWAYS_BLIT) && 
00544           !mViewManager->CanScrollWithBitBlt(aScrolledView));
00545 }
00546 
00547 
00548 void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsPoint aPixDelta,
00549                               float aT2P)
00550 {
00551   if (aTwipsDelta.x != 0 || aTwipsDelta.y != 0)
00552   {
00553     nsIWidget *scrollWidget = GetWidget();
00554     PRBool canBitBlit = scrollWidget && !CannotBitBlt(aScrolledView);
00555 
00556     if (canBitBlit) {
00557       // We're going to bit-blit.  Let the viewmanager know so it can
00558       // adjust dirty regions appropriately.
00559       mViewManager->WillBitBlit(this, aTwipsDelta);
00560     }
00561     
00562     if (!scrollWidget)
00563     {
00564       NS_ASSERTION(!canBitBlit, "Someone screwed up");
00565       nsPoint offsetToWidget;
00566       GetNearestWidget(&offsetToWidget);
00567       // We're moving the child widgets because we are scrolling. But
00568       // the child widgets may stick outside our bounds, so their area
00569       // may include area that's not supposed to be scrolled. We need
00570       // to invalidate to ensure that any such area is properly
00571       // repainted back to the right rendering.
00572       AdjustChildWidgets(aScrolledView, offsetToWidget, aT2P, PR_TRUE);
00573       // If we don't have a scroll widget then we must just update.
00574       // We should call this after fixing up the widget positions to be
00575       // consistent with the view hierarchy.
00576       mViewManager->UpdateView(this, 0);
00577     } else if (!canBitBlit) {
00578       // We can't blit for some reason.
00579       // Just update the view and adjust widgets
00580       // Recall that our widget's origin is at our bounds' top-left
00581       nsRect bounds(GetBounds());
00582       nsPoint topLeft(bounds.x, bounds.y);
00583       AdjustChildWidgets(aScrolledView,
00584                          GetPosition() - topLeft, aT2P, PR_FALSE);
00585       // We should call this after fixing up the widget positions to be
00586       // consistent with the view hierarchy.
00587       mViewManager->UpdateView(this, 0);
00588     } else { // if we can blit and have a scrollwidget then scroll.
00589       // Scroll the contents of the widget by the specfied amount, and scroll
00590       // the child widgets
00591       scrollWidget->Scroll(aPixDelta.x, aPixDelta.y, nsnull);
00592       mViewManager->UpdateViewAfterScroll(this);
00593     }
00594   }
00595 }
00596 
00597 NS_IMETHODIMP nsScrollPortView::Paint(nsIRenderingContext& rc, const nsRect& rect,
00598                                       PRUint32 aPaintFlags, PRBool &aResult)
00599 {
00600   rc.PushState();
00601   nsRect bounds;
00602   GetDimensions(bounds);
00603   bounds.x = bounds.y = 0;
00604   rc.SetClipRect(bounds, nsClipCombine_kIntersect);
00605 
00606   nsresult rv = nsView::Paint(rc, rect, aPaintFlags, aResult);
00607 
00608   rc.PopState();
00609     
00610   return rv;
00611 }
00612 
00613 NS_IMETHODIMP nsScrollPortView::Paint(nsIRenderingContext& aRC, const nsIRegion& aRegion,
00614                                       PRUint32 aPaintFlags, PRBool &aResult)
00615 {
00616   aRC.PushState();
00617   nsRect bounds;
00618   GetDimensions(bounds);
00619   bounds.x = bounds.y = 0;
00620   aRC.SetClipRect(bounds, nsClipCombine_kIntersect);
00621 
00622   nsresult rv = nsView::Paint(aRC, aRegion, aPaintFlags, aResult);
00623 
00624   aRC.PopState();
00625     
00626   return rv;
00627 }
00628 
00629 NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aUpdateFlags)
00630 {
00631   PRInt32           dxPx = 0, dyPx = 0;
00632 
00633   // convert to pixels
00634   nsCOMPtr<nsIDeviceContext> dev;
00635   mViewManager->GetDeviceContext(*getter_AddRefs(dev));
00636   float t2p, p2t;
00637   t2p = dev->AppUnitsToDevUnits();
00638   p2t = dev->DevUnitsToAppUnits();
00639 
00640   // Update the scrolled view's position
00641   nsresult rv = ClampScrollValues(aX, aY, this);
00642   if (NS_FAILED(rv)) {
00643     return rv;
00644   }
00645   
00646   // convert aX and aY in pixels
00647   nscoord aXpx = NSTwipsToIntPixels(aX, t2p);
00648   nscoord aYpx = NSTwipsToIntPixels(aY, t2p);
00649   
00650   aX = NSIntPixelsToTwips(aXpx,p2t);
00651   aY = NSIntPixelsToTwips(aYpx,p2t);
00652   
00653   // do nothing if the we aren't scrolling.
00654   // this needs to be rechecked because of the clamping and
00655   // rounding
00656   if (aX == mOffsetX && aY == mOffsetY) {
00657     return NS_OK;
00658   }
00659 
00660   // figure out the diff by comparing old pos to new
00661   dxPx = mOffsetXpx - aXpx;
00662   dyPx = mOffsetYpx - aYpx;
00663 
00664   // notify the listeners.
00665   PRUint32 listenerCount;
00666   const nsIID& kScrollPositionListenerIID = NS_GET_IID(nsIScrollPositionListener);
00667   nsIScrollPositionListener* listener;
00668   if (nsnull != mListeners) {
00669     if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
00670       for (PRUint32 i = 0; i < listenerCount; i++) {
00671         if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
00672           listener->ScrollPositionWillChange(this, aX, aY);
00673           NS_RELEASE(listener);
00674         }
00675       }
00676     }
00677   }
00678   
00679   nsView* scrolledView = GetScrolledView();
00680   if (!scrolledView) return NS_ERROR_FAILURE;
00681 
00682   // move the scrolled view to the new location
00683   // Note that child widgets may be scrolled by the native widget scrolling,
00684   // so don't update their positions
00685   scrolledView->SetPositionIgnoringChildWidgets(-aX, -aY);
00686       
00687   // store old position in pixels. We need to do this to make sure there is no
00688   // round off errors. This could cause weird scrolling.
00689   mOffsetXpx = aXpx;
00690   mOffsetYpx = aYpx;
00691       
00692   nsPoint twipsDelta(aX - mOffsetX, aY - mOffsetY);
00693 
00694   // store the new position
00695   mOffsetX = aX;
00696   mOffsetY = aY;
00697   
00698   Scroll(scrolledView, twipsDelta, nsPoint(dxPx, dyPx), t2p);
00699 
00700   mViewManager->SynthesizeMouseMove(PR_TRUE);
00701   
00702   // notify the listeners.
00703   if (nsnull != mListeners) {
00704     if (NS_SUCCEEDED(mListeners->Count(&listenerCount))) {
00705       for (PRUint32 i = 0; i < listenerCount; i++) {
00706         if (NS_SUCCEEDED(mListeners->QueryElementAt(i, kScrollPositionListenerIID, (void**)&listener))) {
00707           listener->ScrollPositionDidChange(this, aX, aY);
00708           NS_RELEASE(listener);
00709         }
00710       }
00711     }
00712   }
00713  
00714   return NS_OK;
00715 }
00716 
00717 /************************
00718  *
00719  * smooth scrolling methods
00720  *
00721  ***********************/
00722 
00723 PRBool nsScrollPortView::IsSmoothScrollingEnabled() {
00724   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
00725   if (prefs) {
00726     PRBool enabled;
00727     nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled);
00728     if (NS_SUCCEEDED(rv)) {
00729       return enabled;
00730     }
00731   }
00732   return PR_FALSE;
00733 }
00734 
00735 /*
00736  * Callback function from timer used in nsScrollPortView::DoSmoothScroll
00737  * this cleans up the target coordinates and incrementally calls 
00738  * nsScrollPortView::ScrollTo
00739  */
00740 void
00741 nsScrollPortView::SmoothScrollAnimationCallback (nsITimer *aTimer, void* anInstance) 
00742 {
00743   nsScrollPortView* self = NS_STATIC_CAST(nsScrollPortView*, anInstance);
00744   if (self) {
00745     self->IncrementalScroll();
00746   }
00747 }
00748 
00749 /*
00750  * manages data members and calls to ScrollTo from the (static) SmoothScrollAnimationCallback method
00751  */ 
00752 void
00753 nsScrollPortView::IncrementalScroll()
00754 {
00755   if (!mSmoothScroll) {
00756     return;
00757   }
00758 
00759   if (mSmoothScroll->mFrameIndex < SMOOTH_SCROLL_FRAMES) {
00760     ScrollToImpl(mOffsetX + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2],
00761                  mOffsetY + mSmoothScroll->mVelocities[mSmoothScroll->mFrameIndex*2 + 1],
00762                  0);
00763     mSmoothScroll->mFrameIndex++;
00764   } else {
00765     delete mSmoothScroll;
00766     mSmoothScroll = nsnull;
00767   }
00768 }