Back to index

lightning-sunbird  0.9+nobinonly
nsFirstLetterFrame.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsCOMPtr.h"
00038 #include "nsHTMLContainerFrame.h"
00039 #include "nsPresContext.h"
00040 #include "nsStyleContext.h"
00041 #include "nsIContent.h"
00042 #include "nsLineLayout.h"
00043 #include "nsHTMLAtoms.h"
00044 #include "nsLayoutAtoms.h"
00045 #include "nsAutoPtr.h"
00046 #include "nsStyleSet.h"
00047 #include "nsFrameManager.h"
00048 
00049 #define nsFirstLetterFrameSuper nsHTMLContainerFrame
00050 
00051 class nsFirstLetterFrame : public nsFirstLetterFrameSuper {
00052 public:
00053   nsFirstLetterFrame();
00054 
00055   NS_IMETHOD Init(nsPresContext*  aPresContext,
00056                   nsIContent*      aContent,
00057                   nsIFrame*        aParent,
00058                   nsStyleContext*  aContext,
00059                   nsIFrame*        aPrevInFlow);
00060   NS_IMETHOD SetInitialChildList(nsPresContext* aPresContext,
00061                                  nsIAtom*        aListName,
00062                                  nsIFrame*       aChildList);
00063 #ifdef NS_DEBUG
00064   NS_IMETHOD GetFrameName(nsAString& aResult) const;
00065 #endif
00066   virtual nsIAtom* GetType() const;
00067   NS_IMETHOD  Paint(nsPresContext*      aPresContext,
00068                     nsIRenderingContext& aRenderingContext,
00069                     const nsRect&        aDirtyRect,
00070                     nsFramePaintLayer    aWhichLayer,
00071                     PRUint32             aFlags = 0);
00072   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
00073                     nsHTMLReflowMetrics&     aDesiredSize,
00074                     const nsHTMLReflowState& aReflowState,
00075                     nsReflowStatus&          aStatus);
00076 
00077   NS_IMETHOD CanContinueTextRun(PRBool& aContinueTextRun) const;
00078 
00079   NS_IMETHOD SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread);
00080 
00081 //override of nsFrame method
00082   NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
00083                                            PRBool inHint,
00084                                            PRInt32* outFrameContentOffset,
00085                                            nsIFrame **outChildFrame);
00086 
00087 protected:
00088   virtual PRIntn GetSkipSides() const;
00089 
00090   void DrainOverflowFrames(nsPresContext* aPresContext);
00091 };
00092 
00093 nsresult
00094 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00095 {
00096   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00097   if (nsnull == aNewFrame) {
00098     return NS_ERROR_NULL_POINTER;
00099   }
00100   nsFirstLetterFrame* it = new (aPresShell) nsFirstLetterFrame;
00101   if (nsnull == it) {
00102     return NS_ERROR_OUT_OF_MEMORY;
00103   }
00104   *aNewFrame = it;
00105   return NS_OK;
00106 }
00107 
00108 nsFirstLetterFrame::nsFirstLetterFrame()
00109 {
00110 }
00111 
00112 #ifdef NS_DEBUG
00113 NS_IMETHODIMP
00114 nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
00115 {
00116   return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
00117 }
00118 #endif
00119 
00120 nsIAtom*
00121 nsFirstLetterFrame::GetType() const
00122 {
00123   return nsLayoutAtoms::letterFrame;
00124 }
00125 
00126 PRIntn
00127 nsFirstLetterFrame::GetSkipSides() const
00128 {
00129   return 0;
00130 }
00131 
00132 NS_IMETHODIMP
00133 nsFirstLetterFrame::Init(nsPresContext*  aPresContext,
00134                          nsIContent*      aContent,
00135                          nsIFrame*        aParent,
00136                          nsStyleContext*  aContext,
00137                          nsIFrame*        aPrevInFlow)
00138 {
00139   nsresult rv;
00140   nsRefPtr<nsStyleContext> newSC;
00141   if (aPrevInFlow) {
00142     // Get proper style context for ourselves.  We're creating the frame
00143     // that represents everything *except* the first letter, so just create
00144     // a style context like we would for a text node.
00145     nsStyleContext* parentStyleContext = aContext->GetParent();
00146     if (parentStyleContext) {
00147       newSC = aPresContext->StyleSet()->
00148         ResolveStyleForNonElement(parentStyleContext);
00149       if (newSC)
00150         aContext = newSC;
00151     }
00152   }
00153   rv = nsFirstLetterFrameSuper::Init(aPresContext, aContent, aParent,
00154                                      aContext, aPrevInFlow);
00155   return rv;
00156 }
00157 
00158 NS_IMETHODIMP
00159 nsFirstLetterFrame::SetInitialChildList(nsPresContext* aPresContext,
00160                                         nsIAtom*        aListName,
00161                                         nsIFrame*       aChildList)
00162 {
00163   mFrames.SetFrames(aChildList);
00164   nsFrameManager *frameManager = aPresContext->FrameManager();
00165 
00166   for (nsIFrame* frame = aChildList; frame; frame = frame->GetNextSibling()) {
00167     NS_ASSERTION(frame->GetParent() == this, "Unexpected parent");
00168     frameManager->ReParentStyleContext(frame);
00169   }
00170   return NS_OK;
00171 }
00172 
00173 NS_IMETHODIMP
00174 nsFirstLetterFrame::SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread)
00175 {
00176   if (aSelected && ParentDisablesSelection())
00177     return NS_OK;
00178   nsIFrame *child = GetFirstChild(nsnull);
00179   while (child)
00180   {
00181     child->SetSelected(aPresContext, aRange, aSelected, aSpread);
00182     // don't worry about result. there are more frames to come
00183     child = child->GetNextSibling();
00184   }
00185   return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsFirstLetterFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
00190                                                   PRBool inHint,
00191                                                   PRInt32* outFrameContentOffset,
00192                                                   nsIFrame **outChildFrame)
00193 {
00194   nsIFrame *kid = mFrames.FirstChild();
00195   if (kid)
00196   {
00197     return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
00198   }
00199   else
00200     return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsFirstLetterFrame::Paint(nsPresContext*      aPresContext,
00205                           nsIRenderingContext& aRenderingContext,
00206                           const nsRect&        aDirtyRect,
00207                           nsFramePaintLayer    aWhichLayer,
00208                           PRUint32             aFlags)
00209 {
00210   if (NS_FRAME_IS_UNFLOWABLE & mState) {
00211     return NS_OK;
00212   }
00213 
00214   // Paint inline element backgrounds in the foreground layer.
00215   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
00216     PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
00217   }
00218     
00219   PaintDecorationsAndChildren(aPresContext, aRenderingContext, aDirtyRect,
00220                               aWhichLayer, PR_FALSE, aFlags);
00221   return NS_OK;
00222 }
00223 
00224 
00225 NS_IMETHODIMP
00226 nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
00227                            nsHTMLReflowMetrics&     aMetrics,
00228                            const nsHTMLReflowState& aReflowState,
00229                            nsReflowStatus&          aReflowStatus)
00230 {
00231   DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame", aReflowState.reason);
00232   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
00233   nsresult rv = NS_OK;
00234 
00235   // Grab overflow list
00236   DrainOverflowFrames(aPresContext);
00237 
00238   nsIFrame* kid = mFrames.FirstChild();
00239 
00240   // Setup reflow state for our child
00241   nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
00242   const nsMargin& bp = aReflowState.mComputedBorderPadding;
00243   nscoord lr = bp.left + bp.right;
00244   nscoord tb = bp.top + bp.bottom;
00245   if (NS_UNCONSTRAINEDSIZE != availSize.width) {
00246     availSize.width -= lr;
00247   }
00248   if (NS_UNCONSTRAINEDSIZE != availSize.height) {
00249     availSize.height -= tb;
00250   }
00251 
00252   // Reflow the child
00253   if (!aReflowState.mLineLayout) {
00254     // When there is no lineLayout provided, we provide our own. The
00255     // only time that the first-letter-frame is not reflowing in a
00256     // line context is when its floating.
00257     nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
00258     nsLineLayout ll(aPresContext, nsnull, &aReflowState,
00259                     aMetrics.mComputeMEW);
00260     ll.BeginLineReflow(0, 0, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE,
00261                        PR_FALSE, PR_TRUE);
00262     rs.mLineLayout = &ll;
00263     ll.SetFirstLetterStyleOK(PR_TRUE);
00264 
00265     kid->WillReflow(aPresContext);
00266     kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
00267 
00268     ll.EndLineReflow();
00269   }
00270   else {
00271     // Pretend we are a span and reflow the child frame
00272     nsLineLayout* ll = aReflowState.mLineLayout;
00273     PRBool        pushedFrame;
00274 
00275     ll->BeginSpan(this, &aReflowState, bp.left, availSize.width);
00276     ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame);
00277     nsSize size;
00278     ll->EndSpan(this, size,
00279                 aMetrics.mComputeMEW ? &aMetrics.mMaxElementWidth : nsnull);
00280   }
00281 
00282   // Place and size the child and update the output metrics
00283   kid->SetRect(nsRect(bp.left, bp.top, aMetrics.width, aMetrics.height));
00284   kid->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
00285   aMetrics.width += lr;
00286   aMetrics.height += tb;
00287   aMetrics.ascent += bp.top;
00288   aMetrics.descent += bp.bottom;
00289   if (aMetrics.mComputeMEW) {
00290     aMetrics.mMaxElementWidth += lr;
00291   }
00292 
00293   // Create a continuation or remove existing continuations based on
00294   // the reflow completion status.
00295   if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
00296     nsIFrame* kidNextInFlow = kid->GetNextInFlow();
00297     if (kidNextInFlow) {
00298       // Remove all of the childs next-in-flows
00299       NS_STATIC_CAST(nsContainerFrame*, kidNextInFlow->GetParent())
00300         ->DeleteNextInFlowChild(aPresContext, kidNextInFlow);
00301     }
00302   }
00303   else {
00304     // Create a continuation for the child frame if it doesn't already
00305     // have one.
00306     nsIFrame* nextInFlow;
00307     rv = CreateNextInFlow(aPresContext, this, kid, nextInFlow);
00308     if (NS_FAILED(rv)) {
00309       return rv;
00310     }
00311 
00312     // And then push it to our overflow list
00313     if (nextInFlow) {
00314       kid->SetNextSibling(nsnull);
00315       SetOverflowFrames(aPresContext, nextInFlow);
00316     }
00317     else {
00318       nsIFrame* nextSib = kid->GetNextSibling();
00319       if (nextSib) {
00320         kid->SetNextSibling(nsnull);
00321         SetOverflowFrames(aPresContext, nextSib);
00322       }
00323     }
00324   }
00325 
00326   NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
00327   return rv;
00328 }
00329 
00330 NS_IMETHODIMP
00331 nsFirstLetterFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
00332 {
00333   // We can continue a text run through a first-letter frame.
00334   aContinueTextRun = PR_TRUE;
00335   return NS_OK;
00336 }
00337 
00338 void
00339 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
00340 {
00341   nsIFrame* overflowFrames;
00342 
00343   // Check for an overflow list with our prev-in-flow
00344   nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)mPrevInFlow;
00345   if (nsnull != prevInFlow) {
00346     overflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
00347     if (overflowFrames) {
00348       NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
00349 
00350       // When pushing and pulling frames we need to check for whether any
00351       // views need to be reparented.
00352       nsIFrame* f = overflowFrames;
00353       while (f) {
00354         nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
00355         f = f->GetNextSibling();
00356       }
00357       mFrames.InsertFrames(this, nsnull, overflowFrames);
00358     }
00359   }
00360 
00361   // It's also possible that we have an overflow list for ourselves
00362   overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
00363   if (overflowFrames) {
00364     NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
00365     mFrames.AppendFrames(nsnull, overflowFrames);
00366   }
00367 
00368   // Now repair our first frames style context (since we only reflow
00369   // one frame there is no point in doing any other ones until they
00370   // are reflowed)
00371   nsIFrame* kid = mFrames.FirstChild();
00372   if (kid) {
00373     nsRefPtr<nsStyleContext> sc;
00374     nsIContent* kidContent = kid->GetContent();
00375     if (kidContent) {
00376       NS_ASSERTION(kidContent->IsContentOfType(nsIContent::eTEXT),
00377                    "should contain only text nodes");
00378       sc = aPresContext->StyleSet()->ResolveStyleForNonElement(mStyleContext);
00379       if (sc) {
00380         kid->SetStyleContext(aPresContext, sc);
00381       }
00382     }
00383   }
00384 }