Back to index

lightning-sunbird  0.9+nobinonly
nsFrameContentIterator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
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 "nsCOMPtr.h"
00040 #include "nsIContentIterator.h"
00041 #include "nsPresContext.h"
00042 #include "nsIFrame.h"
00043 #include "nsIContent.h"
00044 #include "nsIEnumerator.h"
00045 #include "nsFrameList.h"
00046 
00047 class nsFrameContentIterator : public nsIContentIterator
00048 {
00049 public:
00050   nsFrameContentIterator(nsPresContext* aPresContext, nsIFrame* aFrame);
00051   virtual ~nsFrameContentIterator();
00052 
00053   // nsISupports
00054   NS_DECL_ISUPPORTS
00055 
00056   // nsIContentIterator
00057   virtual nsresult Init(nsIContent* aRoot);
00058   virtual nsresult Init(nsIDOMRange* aRange);
00059 
00060   virtual void First();
00061   virtual void Last();
00062   virtual void Next();
00063   virtual void Prev();
00064 
00065   virtual nsIContent *GetCurrentNode();
00066   virtual PRBool IsDone();
00067   virtual nsresult PositionAt(nsIContent* aCurNode);
00068 
00069 private:
00070   nsCOMPtr<nsPresContext>  mPresContext;
00071   nsIFrame*                 mParentFrame;
00072   nsIFrame*                 mCurrentChild;
00073   PRBool                    mIsDone;
00074 };
00075 
00076 nsFrameContentIterator::nsFrameContentIterator(nsPresContext* aPresContext,
00077                                                nsIFrame*       aFrame)
00078   : mPresContext(aPresContext), mParentFrame(aFrame), mIsDone(PR_FALSE)
00079 {
00080   First();
00081 }
00082 
00083 NS_IMPL_ISUPPORTS1(nsFrameContentIterator, nsIContentIterator)
00084 
00085 nsFrameContentIterator::~nsFrameContentIterator()
00086 {
00087 }
00088 
00089 nsresult
00090 nsFrameContentIterator::Init(nsIContent* aRoot)
00091 {
00092   return NS_ERROR_ALREADY_INITIALIZED;
00093 }
00094 
00095 nsresult
00096 nsFrameContentIterator::Init(nsIDOMRange* aRange)
00097 {
00098   return NS_ERROR_ALREADY_INITIALIZED;
00099 }
00100 
00101 void
00102 nsFrameContentIterator::First()
00103 {
00104   // Get the first child frame and make it the current node
00105   mCurrentChild = mParentFrame->GetFirstChild(nsnull);
00106 
00107   mIsDone = !mCurrentChild;
00108 }
00109 
00110 
00111 static nsIFrame*
00112 GetNextChildFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
00113 {
00114   NS_PRECONDITION(aFrame, "null pointer");
00115 
00116   // Get the last-in-flow
00117   aFrame = aFrame->GetLastInFlow();
00118 
00119   // Get its next sibling
00120   nsIFrame* nextSibling = aFrame->GetNextSibling();
00121 
00122   // If there's no next sibling, then check if the parent frame
00123   // has a next-in-flow and look there
00124   if (!nextSibling) {
00125     nsIFrame* parent = aFrame->GetParent()->GetNextInFlow();
00126 
00127     if (parent) {
00128       nextSibling = parent->GetFirstChild(nsnull);
00129     }
00130   }
00131 
00132   return nextSibling;
00133 }
00134 
00135 void
00136 nsFrameContentIterator::Last()
00137 {
00138   // Starting with the first child walk and find the last child
00139   mCurrentChild = nsnull;
00140   nsIFrame* nextChild = mParentFrame->GetFirstChild(nsnull);
00141   while (nextChild) {
00142     mCurrentChild = nextChild;
00143     nextChild = ::GetNextChildFrame(mPresContext, nextChild);
00144   }
00145 
00146   mIsDone = !mCurrentChild;
00147 }
00148 
00149 void
00150 nsFrameContentIterator::Next()
00151 {
00152   nsIFrame* nextChild = ::GetNextChildFrame(mPresContext, mCurrentChild);
00153 
00154   if (nextChild) {
00155     // Advance to the next child
00156     mCurrentChild = nextChild;
00157 
00158     // If we're at the end then the collection is at the end
00159     mIsDone = (nsnull == ::GetNextChildFrame(mPresContext, mCurrentChild));
00160 
00161     return;
00162   }
00163 
00164   // No next frame, we're done.
00165   mIsDone = PR_TRUE;
00166 }
00167 
00168 static nsIFrame*
00169 GetPrevChildFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
00170 {
00171   NS_PRECONDITION(aFrame, "null pointer");
00172 
00173   // Get its previous sibling. Because we have a singly linked list we
00174   // need to search from the first child
00175   nsIFrame* parent = aFrame->GetParent();
00176   nsIFrame* prevSibling;
00177   nsIFrame* firstChild = parent->GetFirstChild(nsnull);
00178 
00179   NS_ASSERTION(firstChild, "parent has no first child");
00180   nsFrameList frameList(firstChild);
00181   prevSibling = frameList.GetPrevSiblingFor(aFrame);
00182 
00183   // If there's no previous sibling, then check if the parent frame
00184   // has a prev-in-flow and look there
00185   if (!prevSibling) {
00186     parent = parent->GetPrevInFlow();
00187 
00188     if (parent) {
00189       firstChild = parent->GetFirstChild(nsnull);
00190       frameList.SetFrames(firstChild);
00191       prevSibling = frameList.LastChild();
00192     }
00193   }
00194   
00195   // Get the first-in-flow
00196   while (PR_TRUE) {
00197     nsIFrame* prevInFlow = prevSibling->GetPrevInFlow();
00198     if (prevInFlow) {
00199       prevSibling = prevInFlow;
00200     } else {
00201       break;
00202     }
00203   }
00204 
00205   return prevSibling;
00206 }
00207 
00208 void
00209 nsFrameContentIterator::Prev()
00210 {
00211   nsIFrame* prevChild = ::GetPrevChildFrame(mPresContext, mCurrentChild);
00212 
00213   if (prevChild) {
00214     // Make the previous child the current child
00215     mCurrentChild = prevChild;
00216     
00217     // If we're at the beginning then the collection is at the end
00218     mIsDone = (nsnull == ::GetPrevChildFrame(mPresContext, mCurrentChild));
00219 
00220     return;
00221   }
00222 
00223   // No previous frame, we're done.
00224   mIsDone = PR_TRUE;
00225 }
00226 
00227 nsIContent *
00228 nsFrameContentIterator::GetCurrentNode()
00229 {
00230   if (mCurrentChild && !mIsDone) {
00231     return mCurrentChild->GetContent();
00232   }
00233 
00234   return nsnull;
00235 }
00236 
00237 PRBool
00238 nsFrameContentIterator::IsDone()
00239 {
00240   return mIsDone;
00241 }
00242 
00243 nsresult
00244 nsFrameContentIterator::PositionAt(nsIContent* aCurNode)
00245 {
00246   // Starting with the first child frame search for the child frame
00247   // with the matching content object
00248   nsIFrame* child = mParentFrame->GetFirstChild(nsnull);
00249   while (child) {
00250     if (child->GetContent() == aCurNode) {
00251       break;
00252     }
00253     child = ::GetNextChildFrame(mPresContext, child);
00254   }
00255 
00256   if (child) {
00257     // Make it the current child
00258     mCurrentChild = child;
00259     mIsDone = PR_FALSE;
00260   }
00261 
00262   return NS_OK;
00263 }
00264 
00265 nsresult
00266 NS_NewFrameContentIterator(nsPresContext*      aPresContext,
00267                            nsIFrame*            aFrame,
00268                            nsIContentIterator** aIterator)
00269 {
00270   NS_ENSURE_ARG_POINTER(aIterator);
00271   if (!aIterator) {
00272     return NS_ERROR_NULL_POINTER;
00273   }
00274   NS_ENSURE_ARG_POINTER(aFrame);
00275   if (!aFrame) {
00276     return NS_ERROR_NULL_POINTER;
00277   }
00278   
00279   // Make sure the frame corresponds to generated content
00280   NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT, "unexpected frame");
00281 
00282   nsFrameContentIterator* it = new nsFrameContentIterator(aPresContext, aFrame);
00283   if (!it) {
00284     return NS_ERROR_OUT_OF_MEMORY;
00285   }
00286   return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aIterator);
00287 }