Back to index

lightning-sunbird  0.9+nobinonly
nsScrollBoxObject.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 Communicator client 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  *   Original Author: David W. Hyatt (hyatt@netscape.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 #include "nsCOMPtr.h"
00039 #include "nsIScrollBoxObject.h"
00040 #include "nsBoxObject.h"
00041 #include "nsIPresShell.h"
00042 #include "nsIContent.h"
00043 #include "nsIDocument.h"
00044 #include "nsIDOMDocument.h"
00045 #include "nsIDOMNSDocument.h"
00046 #include "nsIDOMElement.h"
00047 #include "nsPresContext.h"
00048 #include "nsIFrame.h"
00049 #include "nsIScrollableView.h"
00050 #include "nsIScrollableFrame.h"
00051 
00052 
00053 class nsScrollBoxObject : public nsIScrollBoxObject, public nsBoxObject
00054 {
00055 public:
00056   NS_DECL_ISUPPORTS_INHERITED
00057   NS_DECL_NSISCROLLBOXOBJECT
00058 
00059   nsScrollBoxObject();
00060   virtual ~nsScrollBoxObject();
00061 
00062   virtual nsIScrollableView* GetScrollableView();
00063 
00064   /* additional members */
00065 };
00066 
00067 /* Implementation file */
00068 
00069 NS_INTERFACE_MAP_BEGIN(nsScrollBoxObject)
00070   NS_INTERFACE_MAP_ENTRY(nsIScrollBoxObject)
00071 NS_INTERFACE_MAP_END_INHERITING(nsBoxObject)
00072 
00073 NS_IMPL_ADDREF_INHERITED(nsScrollBoxObject, nsBoxObject)
00074 NS_IMPL_RELEASE_INHERITED(nsScrollBoxObject, nsBoxObject)
00075 
00076 nsScrollBoxObject::nsScrollBoxObject()
00077 {
00078   /* member initializers and constructor code */
00079 }
00080 
00081 nsScrollBoxObject::~nsScrollBoxObject()
00082 {
00083   /* destructor code */
00084 }
00085 
00086 /* void scrollTo (in long x, in long y); */
00087 NS_IMETHODIMP nsScrollBoxObject::ScrollTo(PRInt32 x, PRInt32 y)
00088 {
00089   nsIScrollableView* scrollableView = GetScrollableView();
00090   if (!scrollableView)
00091     return NS_ERROR_FAILURE;
00092 
00093   nsCOMPtr<nsIPresShell> shell = GetPresShell();
00094   if (!shell) {
00095     return NS_ERROR_UNEXPECTED;
00096   }
00097   
00098   float pixelsToTwips = shell->GetPresContext()->PixelsToTwips();
00099 
00100   return scrollableView->ScrollTo(NSToIntRound(x * pixelsToTwips),
00101                                   NSToIntRound(y * pixelsToTwips),
00102                                   NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00103 }
00104 
00105 /* void scrollBy (in long dx, in long dy); */
00106 NS_IMETHODIMP nsScrollBoxObject::ScrollBy(PRInt32 dx, PRInt32 dy)
00107 {
00108   PRInt32 x, y;
00109   nsresult rv = GetPosition(&x, &y);
00110   if (NS_FAILED(rv))
00111     return rv;
00112 
00113   return ScrollTo(x + dx, y + dy);
00114 }
00115 
00116 /* void scrollByLine (in long dlines); */
00117 NS_IMETHODIMP nsScrollBoxObject::ScrollByLine(PRInt32 dlines)
00118 {
00119   nsIScrollableView* scrollableView = GetScrollableView();
00120   if (!scrollableView)
00121     return NS_ERROR_FAILURE;
00122 
00123   return scrollableView->ScrollByLines(0, dlines);
00124 }
00125 
00126 // XUL <scrollbox> elements have a single box child element.
00127 // Get a pointer to that box.
00128 // Note that now that the <scrollbox> is just a regular box
00129 // with 'overflow:hidden', the boxobject's frame is an nsXULScrollFrame,
00130 // the <scrollbox>'s box frame is the scrollframe's "scrolled frame", and
00131 // the <scrollbox>'s child box is a child of that.
00132 static nsIFrame* GetScrolledBox(nsBoxObject* aScrollBox) {
00133   nsIFrame* frame = aScrollBox->GetFrame();
00134   if (!frame) 
00135     return nsnull;
00136   nsIScrollableFrame* scrollFrame;
00137   if (NS_FAILED(CallQueryInterface(frame, &scrollFrame))) {
00138     NS_WARNING("nsIScrollBoxObject attached to something that's not a scroll frame!");
00139     return nsnull;
00140   }
00141   nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
00142   if (!scrolledFrame)
00143     return nsnull;
00144   nsIBox* scrolledBox;
00145   if (NS_FAILED(scrolledFrame->GetChildBox(&scrolledBox)))
00146     return nsnull;
00147   return scrolledBox;
00148 }
00149 
00150 /* void scrollByIndex (in long dindexes); */
00151 NS_IMETHODIMP nsScrollBoxObject::ScrollByIndex(PRInt32 dindexes)
00152 {
00153     nsIScrollableView* scrollableView = GetScrollableView();
00154     if (!scrollableView)
00155        return NS_ERROR_FAILURE;
00156     nsIFrame* scrolledBox = GetScrolledBox(this);
00157     if (!scrolledBox)
00158        return NS_ERROR_FAILURE;
00159 
00160     nsRect rect;
00161     nsIFrame* child;
00162 
00163     // now get the scrolled boxes first child.
00164     scrolledBox->GetChildBox(&child);
00165 
00166     PRBool horiz = PR_FALSE;
00167     scrolledBox->GetOrientation(horiz);
00168     nsPoint cp;
00169     scrollableView->GetScrollPosition(cp.x,cp.y);
00170     nscoord diff = 0;
00171     PRInt32 curIndex = 0;
00172     PRBool isLTR = scrolledBox->IsNormalDirection();
00173 
00174     PRInt32 frameWidth = 0;
00175     if (!isLTR && horiz) {
00176       GetWidth(&frameWidth);
00177       nsCOMPtr<nsIPresShell> shell = GetPresShell();
00178       if (!shell) {
00179         return NS_ERROR_UNEXPECTED;
00180       }
00181       frameWidth = NSToIntRound(frameWidth *
00182                                 shell->GetPresContext()->PixelsToTwips());
00183     }
00184 
00185     // first find out what index we are currently at
00186     while(child) {
00187       rect = child->GetRect();
00188       if (horiz) {
00189         // In the left-to-right case we break from the loop when the center of
00190         // the current child rect is greater than the scrolled position of
00191         // the left edge of the scrollbox
00192         // In the right-to-left case we break when the center of the current
00193         // child rect is less than the scrolled position of the right edge of
00194         // the scrollbox.
00195         diff = rect.x + rect.width/2; // use the center, to avoid rounding errors
00196         if ((isLTR && diff > cp.x) ||
00197             (!isLTR && diff < cp.x + frameWidth)) {
00198           break;
00199         }
00200       } else {
00201         diff = rect.y + rect.height/2;// use the center, to avoid rounding errors
00202         if (diff > cp.y) {
00203           break;
00204         }
00205       }
00206       child->GetNextBox(&child);
00207       curIndex++;
00208     }
00209 
00210     PRInt32 count = 0;
00211 
00212     if (dindexes == 0)
00213        return NS_OK;
00214 
00215     if (dindexes > 0) {
00216       while(child) {
00217         child->GetNextBox(&child);
00218         if (child)
00219           rect = child->GetRect();
00220         count++;
00221         if (count >= dindexes)
00222           break;
00223       }
00224 
00225    } else if (dindexes < 0) {
00226       scrolledBox->GetChildBox(&child);
00227       while(child) {
00228         rect = child->GetRect();
00229         if (count >= curIndex + dindexes)
00230           break;
00231 
00232         count++;
00233         child->GetNextBox(&child);
00234 
00235       }
00236    }
00237 
00238    if (horiz)
00239        // In the left-to-right case we scroll so that the left edge of the
00240        // selected child is scrolled to the left edge of the scrollbox.
00241        // In the right-to-left case we scroll so that the right edge of the
00242        // selected child is scrolled to the right edge of the scrollbox.
00243        return scrollableView->ScrollTo((isLTR) ? rect.x :
00244                                        rect.x + rect.width - frameWidth, 
00245                                        cp.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00246    else
00247        return scrollableView->ScrollTo(cp.x, rect.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00248 }
00249 
00250 /* void scrollToLine (in long line); */
00251 NS_IMETHODIMP nsScrollBoxObject::ScrollToLine(PRInt32 line)
00252 {
00253   nsIScrollableView* scrollableView = GetScrollableView();
00254   if (!scrollableView)
00255     return NS_ERROR_FAILURE;
00256   
00257   nscoord height = 0;
00258   scrollableView->GetLineHeight(&height);
00259   scrollableView->ScrollTo(0,height*line, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00260 
00261   return NS_OK;
00262 }
00263 
00264 /* void scrollToElement (in nsIDOMElement child); */
00265 NS_IMETHODIMP nsScrollBoxObject::ScrollToElement(nsIDOMElement *child)
00266 {
00267     NS_ENSURE_ARG_POINTER(child);
00268     nsIScrollableView* scrollableView = GetScrollableView();
00269     if (!scrollableView)
00270        return NS_ERROR_FAILURE;
00271 
00272     nsCOMPtr<nsIPresShell> shell = GetPresShell();
00273     if (!shell) {
00274       return NS_ERROR_UNEXPECTED;
00275     }
00276     
00277     // prepare for twips
00278     float pixelsToTwips = shell->GetPresContext()->PixelsToTwips();
00279     
00280     nsIFrame* scrolledBox = GetScrolledBox(this);
00281     if (!scrolledBox)
00282        return NS_ERROR_FAILURE;
00283 
00284     nsRect rect, crect;
00285     nsCOMPtr<nsIDOMDocument> doc;
00286     child->GetOwnerDocument(getter_AddRefs(doc));
00287     nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(doc));
00288     if(!nsDoc)
00289       return NS_ERROR_UNEXPECTED;
00290 
00291     nsCOMPtr<nsIBoxObject> childBoxObject;
00292     nsDoc->GetBoxObjectFor(child, getter_AddRefs(childBoxObject));
00293     if(!childBoxObject)
00294       return NS_ERROR_UNEXPECTED;
00295 
00296     PRInt32 x,y;
00297     childBoxObject->GetX(&x);
00298     childBoxObject->GetY(&y);
00299     // get the twips rectangle from the boxobject (which has pixels)    
00300     rect.x = NSToIntRound(x * pixelsToTwips);
00301     rect.y = NSToIntRound(y * pixelsToTwips);
00302     
00303     // TODO: make sure the child is inside the box
00304 
00305     // get our current info
00306     PRBool horiz = PR_FALSE;
00307     scrolledBox->GetOrientation(horiz);
00308     nsPoint cp;
00309     scrollableView->GetScrollPosition(cp.x,cp.y);
00310 
00311     GetOffsetRect(crect);    
00312     crect.x = NSToIntRound(crect.x * pixelsToTwips);
00313     crect.y = NSToIntRound(crect.y * pixelsToTwips);
00314     nscoord newx=cp.x, newy=cp.y;
00315 
00316     // we only scroll in the direction of the scrollbox orientation
00317     // always scroll to left or top edge of child element
00318     if (horiz) {
00319         newx = rect.x - crect.x;
00320     } else {
00321         newy = rect.y - crect.y;
00322     }
00323     // scroll away
00324     return scrollableView->ScrollTo(newx, newy, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00325 }
00326 
00327 /* void scrollToIndex (in long index); */
00328 NS_IMETHODIMP nsScrollBoxObject::ScrollToIndex(PRInt32 index)
00329 {
00330     return NS_ERROR_NOT_IMPLEMENTED;
00331 }
00332 
00333 /* void getPosition (out long x, out long y); */
00334 NS_IMETHODIMP nsScrollBoxObject::GetPosition(PRInt32 *x, PRInt32 *y)
00335 {
00336   nsIScrollableView* scrollableView = GetScrollableView();
00337   if (!scrollableView)
00338     return NS_ERROR_FAILURE;
00339 
00340   nscoord xc, yc;
00341   nsresult rv = scrollableView->GetScrollPosition(xc, yc);
00342   if (NS_FAILED(rv))
00343     return rv;
00344 
00345   nsCOMPtr<nsIPresShell> shell = GetPresShell();
00346   if (!shell) {
00347     return NS_ERROR_UNEXPECTED;
00348   }
00349 
00350   float twipsToPixels = shell->GetPresContext()->TwipsToPixels();
00351 
00352   *x = NSToIntRound(xc * twipsToPixels);
00353   *y = NSToIntRound(yc * twipsToPixels);
00354 
00355   return NS_OK;  
00356 }
00357 
00358 /* void getScrolledSize (out long width, out long height); */
00359 NS_IMETHODIMP nsScrollBoxObject::GetScrolledSize(PRInt32 *width, PRInt32 *height)
00360 {
00361     nsIFrame* scrolledBox = GetScrolledBox(this);
00362     if (!scrolledBox)
00363         return NS_ERROR_FAILURE;
00364               
00365     nsRect scrollRect = scrolledBox->GetRect();
00366 
00367     nsCOMPtr<nsIPresShell> shell = GetPresShell();
00368     if (!shell) {
00369       return NS_ERROR_UNEXPECTED;
00370     }
00371 
00372     float twipsToPixels = shell->GetPresContext()->TwipsToPixels();
00373  
00374     *width  = NSTwipsToIntPixels(scrollRect.width, twipsToPixels);
00375     *height = NSTwipsToIntPixels(scrollRect.height, twipsToPixels);
00376 
00377     return NS_OK;
00378 }
00379 
00380 /* void ensureElementIsVisible (in nsIDOMElement child); */
00381 NS_IMETHODIMP nsScrollBoxObject::EnsureElementIsVisible(nsIDOMElement *child)
00382 {
00383     NS_ENSURE_ARG_POINTER(child);
00384     nsIScrollableView* scrollableView = GetScrollableView();
00385     if (!scrollableView)
00386        return NS_ERROR_FAILURE;
00387 
00388     nsCOMPtr<nsIPresShell> shell = GetPresShell();
00389     if (!shell) {
00390       return NS_ERROR_UNEXPECTED;
00391     }
00392 
00393     // prepare for twips
00394     float pixelsToTwips = 0.0;
00395     pixelsToTwips = shell->GetPresContext()->PixelsToTwips();
00396     
00397     nsIFrame* scrolledBox = GetScrolledBox(this);
00398     if (!scrolledBox)
00399        return NS_ERROR_FAILURE;
00400 
00401     nsRect rect, crect;
00402     nsCOMPtr<nsIDOMDocument> doc;
00403     child->GetOwnerDocument(getter_AddRefs(doc));
00404     nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(doc));
00405     if(!nsDoc)
00406         return NS_ERROR_UNEXPECTED;
00407 
00408     nsCOMPtr<nsIBoxObject> childBoxObject;
00409     nsDoc->GetBoxObjectFor(child, getter_AddRefs(childBoxObject));
00410     if(!childBoxObject)
00411       return NS_ERROR_UNEXPECTED;
00412 
00413     PRInt32 x,y,width,height;
00414     childBoxObject->GetX(&x);
00415     childBoxObject->GetY(&y);
00416     childBoxObject->GetWidth(&width);
00417     childBoxObject->GetHeight(&height);
00418     // get the twips rectangle from the boxobject (which has pixels)    
00419     rect.x = NSToIntRound(x * pixelsToTwips);
00420     rect.y = NSToIntRound(y * pixelsToTwips);
00421     rect.width = NSToIntRound(width * pixelsToTwips);
00422     rect.height = NSToIntRound(height * pixelsToTwips);
00423 
00424     // TODO: make sure the child is inside the box
00425 
00426     // get our current info
00427     PRBool horiz = PR_FALSE;
00428     scrolledBox->GetOrientation(horiz);
00429 
00430     nsPoint cp;
00431     scrollableView->GetScrollPosition(cp.x,cp.y);
00432     GetOffsetRect(crect);    
00433     crect.x = NSToIntRound(crect.x * pixelsToTwips);
00434     crect.y = NSToIntRound(crect.y * pixelsToTwips);
00435     crect.width = NSToIntRound(crect.width * pixelsToTwips);
00436     crect.height = NSToIntRound(crect.height * pixelsToTwips);
00437 
00438 
00439     nscoord newx=cp.x, newy=cp.y;
00440 
00441     // we only scroll in the direction of the scrollbox orientation
00442     if (horiz) {
00443         if ((rect.x - crect.x) + rect.width > cp.x + crect.width) {
00444             newx = cp.x + (((rect.x - crect.x) + rect.width)-(cp.x + crect.width));
00445         } else if (rect.x - crect.x < cp.x) {
00446             newx = rect.x - crect.x;
00447         }
00448     } else {
00449         if ((rect.y - crect.y) + rect.height > cp.y + crect.height) {
00450             newy = cp.y + (((rect.y - crect.y) + rect.height)-(cp.y + crect.height));
00451         } else if (rect.y - crect.y < cp.y) {
00452             newy = rect.y - crect.y;
00453         }
00454     }
00455     
00456     // scroll away
00457     return scrollableView->ScrollTo(newx, newy, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
00458 }
00459 
00460 /* void ensureIndexIsVisible (in long index); */
00461 NS_IMETHODIMP nsScrollBoxObject::EnsureIndexIsVisible(PRInt32 index)
00462 {
00463     return NS_ERROR_NOT_IMPLEMENTED;
00464 }
00465 
00466 /* void ensureLineIsVisible (in long line); */
00467 NS_IMETHODIMP nsScrollBoxObject::EnsureLineIsVisible(PRInt32 line)
00468 {
00469     return NS_ERROR_NOT_IMPLEMENTED;
00470 }
00471 
00472 nsIScrollableView* 
00473 nsScrollBoxObject::GetScrollableView()
00474 {
00475   // get the frame.
00476   nsIFrame* frame = GetFrame();
00477   if (!frame) 
00478     return nsnull;
00479   
00480   nsIScrollableFrame* scrollFrame;
00481   if (NS_FAILED(CallQueryInterface(frame, &scrollFrame)))
00482     return nsnull;
00483 
00484   nsIScrollableView* scrollingView = scrollFrame->GetScrollableView();
00485   if (!scrollingView)
00486     return nsnull;
00487 
00488   return scrollingView;
00489 }
00490 
00491 nsresult
00492 NS_NewScrollBoxObject(nsIBoxObject** aResult)
00493 {
00494   *aResult = new nsScrollBoxObject;
00495   if (!*aResult)
00496     return NS_ERROR_OUT_OF_MEMORY;
00497   NS_ADDREF(*aResult);
00498   return NS_OK;
00499 }
00500