From 0bbef0f75eaa240db73d65cfe2113e7c8c825860 Mon Sep 17 00:00:00 2001 From: "kipp%netscape.com" Date: Tue, 1 Dec 1998 16:13:49 +0000 Subject: [PATCH] new git-svn-id: svn://10.0.0.236/trunk@15521 18797224-902f-48f8-a5cc-f745e15eee43 --- .../layout/generic/nsBlockReflowContext.cpp | 407 ++++++++++++++++ mozilla/layout/generic/nsBlockReflowContext.h | 129 ++++++ mozilla/layout/generic/nsBulletFrame.cpp | 433 ++++++++++++++++++ mozilla/layout/generic/nsBulletFrame.h | 65 +++ mozilla/layout/generic/nsLineBox.cpp | 275 +++++++++++ mozilla/layout/generic/nsLineBox.h | 192 ++++++++ .../html/base/src/nsBlockReflowContext.cpp | 407 ++++++++++++++++ .../html/base/src/nsBlockReflowContext.h | 129 ++++++ .../layout/html/base/src/nsBulletFrame.cpp | 433 ++++++++++++++++++ mozilla/layout/html/base/src/nsBulletFrame.h | 65 +++ mozilla/layout/html/base/src/nsLineBox.cpp | 275 +++++++++++ mozilla/layout/html/base/src/nsLineBox.h | 192 ++++++++ 12 files changed, 3002 insertions(+) create mode 100644 mozilla/layout/generic/nsBlockReflowContext.cpp create mode 100644 mozilla/layout/generic/nsBlockReflowContext.h create mode 100644 mozilla/layout/generic/nsBulletFrame.cpp create mode 100644 mozilla/layout/generic/nsBulletFrame.h create mode 100644 mozilla/layout/generic/nsLineBox.cpp create mode 100644 mozilla/layout/generic/nsLineBox.h create mode 100644 mozilla/layout/html/base/src/nsBlockReflowContext.cpp create mode 100644 mozilla/layout/html/base/src/nsBlockReflowContext.h create mode 100644 mozilla/layout/html/base/src/nsBulletFrame.cpp create mode 100644 mozilla/layout/html/base/src/nsBulletFrame.h create mode 100644 mozilla/layout/html/base/src/nsLineBox.cpp create mode 100644 mozilla/layout/html/base/src/nsLineBox.h diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp new file mode 100644 index 00000000000..e0c15a823e6 --- /dev/null +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -0,0 +1,407 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsBlockReflowContext.h" +#include "nsFrameReflowState.h" +#include "nsLineLayout.h" +#include "nsHTMLIIDs.h" +#include "nsISpaceManager.h" +#include "nsIFontMetrics.h" +#include "nsIPresContext.h" +#include "nsIStyleContext.h" +#include "nsHTMLContainerFrame.h" + +nsBlockReflowContext::nsBlockReflowContext(nsIPresContext& aPresContext, + nsLineLayout& aLineLayout, + nsFrameReflowState& aParentRS) + : mPresContext(aPresContext), + mLineLayout(aLineLayout), + mOuterReflowState(aParentRS), + mMetrics(aParentRS.mComputeMaxElementSize ? &mMaxElementSize : nsnull) +{ + mRunInFrame = nsnull; + mCompactMarginWidth = 0; + mSpacing = nsnull; +} + +void +nsBlockReflowContext::ComputeMarginsFor(nsIPresContext& aPresContext, + nsIFrame* aFrame, + const nsStyleSpacing* aSpacing, + const nsFrameReflowState& aParentRS, + nsMargin& aResult) +{ + // XXX pass in mSpacing to ComputeMarginFor + nsHTMLReflowState::ComputeMarginFor(aFrame, &aParentRS, aResult); + + // Compute auto top/bottom margin values + nsStyleUnit topUnit = aSpacing->mMargin.GetTopUnit(); + nsStyleUnit bottomUnit = aSpacing->mMargin.GetBottomUnit(); + nscoord fontHeight = 0; + if ((eStyleUnit_Auto == topUnit) || (eStyleUnit_Auto == bottomUnit)) { + // XXX Use the font for the frame, not the default font??? + const nsFont& defaultFont = aPresContext.GetDefaultFont(); + nsIFontMetrics* fm = aPresContext.GetMetricsFor(defaultFont); + fm->GetHeight(fontHeight); + NS_RELEASE(fm); + } + + // For auto margins use the font height computed above + if (eStyleUnit_Auto == topUnit) { + aResult.top = fontHeight; + } + if (eStyleUnit_Auto == bottomUnit) { + aResult.bottom = fontHeight; + } +} + +nsresult +nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace) +{ + nsReflowStatus reflowStatus; + + mFrame = aFrame; + mSpace = aSpace; + + // Compute the margins (including auto margins) + aFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)mSpacing); + ComputeMarginsFor(mPresContext, aFrame, mSpacing, mOuterReflowState, + mMargin); + + // Compute x/y coordinate where reflow will begin. Use the rules + // from 10.3.3 to determine what to apply. At this point in the + // reflow auto left/right margins will have a zero value. + nscoord x = aSpace.x + mMargin.left; + nscoord y = aSpace.y; + mX = x; + mY = y; + nsSize availSize(aSpace.width, aSpace.height); + if (NS_UNCONSTRAINEDSIZE != aSpace.width) { + availSize.width -= mMargin.left; + } + if (NS_UNCONSTRAINEDSIZE != aSpace.height) { + availSize.height -= mMargin.top + mMargin.bottom; + } + + // Get reflow reason set correctly. It's possible that a child was + // created and then it was decided that it could not be reflowed + // (for example, a block frame that isn't at the start of a + // line). In this case the reason will be wrong so we need to check + // the frame state. + nsReflowReason reason = eReflowReason_Resize; + nsFrameState state; + aFrame->GetFrameState(state); + if (NS_FRAME_FIRST_REFLOW & state) { + reason = eReflowReason_Initial; + } + else if (mOuterReflowState.mNextRCFrame == aFrame) { + reason = eReflowReason_Incremental; + // Make sure we only incrementally reflow once + // XXX caller should do this, yes? + mOuterReflowState.mNextRCFrame = nsnull; + } + + // Setup reflow state for reflowing the frame + nsHTMLReflowState reflowState(mPresContext, aFrame, mOuterReflowState, + availSize); + reflowState.lineLayout = nsnull; + reflowState.mRunInFrame = mRunInFrame; + reflowState.mCompactMarginWidth = mCompactMarginWidth; + reflowState.reason = reason; + mLineLayout.SetUnderstandsWhiteSpace(PR_FALSE); + + // Let frame know that we are reflowing it + nsIHTMLReflow* htmlReflow; + nsresult rv = aFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); + if (!NS_SUCCEEDED(rv)) { + return rv; + } + htmlReflow->WillReflow(mPresContext); + + // Adjust spacemanager coordinate system for the frame. The + // spacemanager coordinates are inside the callers + // border+padding, but the x/y coordinates are not (recall that + // frame coordinates are relative to the parents origin and that the + // parents border/padding is inside the parent + // frame. Therefore we have to subtract out the parents + // border+padding before translating. + nscoord tx = x - mOuterReflowState.mBorderPadding.left; + nscoord ty = y - mOuterReflowState.mBorderPadding.top; + aFrame->MoveTo(x, y); + mOuterReflowState.spaceManager->Translate(tx, ty); + htmlReflow->Reflow(mPresContext, mMetrics, reflowState, reflowStatus); + mOuterReflowState.spaceManager->Translate(-tx, -ty); + + aFrame->GetFrameState(state); + if (0 == (NS_FRAME_OUTSIDE_CHILDREN & state)) { + // Provide combined area for child that doesn't have any + mMetrics.mCombinedArea.x = 0; + mMetrics.mCombinedArea.y = 0; + mMetrics.mCombinedArea.width = mMetrics.width; + mMetrics.mCombinedArea.height = mMetrics.height; + } + + // Now that frame has been reflowed at least one time make sure that + // the NS_FRAME_FIRST_REFLOW bit is cleared so that never give it an + // initial reflow reason again. + if (eReflowReason_Initial == reason) { + aFrame->SetFrameState(state & ~NS_FRAME_FIRST_REFLOW); + } + + if (!NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) { + // If frame is complete and has a next-in-flow, we need to delete + // them now. Do not do this when a break-before is signaled because + // the frame is going to get reflowed again (and may end up wanting + // a next-in-flow where it ends up). + if (NS_FRAME_IS_COMPLETE(reflowStatus)) { + nsIFrame* kidNextInFlow; + aFrame->GetNextInFlow(kidNextInFlow); + if (nsnull != kidNextInFlow) { + // Remove all of the childs next-in-flows. Make sure that we ask + // the right parent to do the removal (it's possible that the + // parent is not this because we are executing pullup code) +/* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */ + nsHTMLContainerFrame* parent; + aFrame->GetGeometricParent((nsIFrame*&) parent); + parent->DeleteChildsNextInFlow(mPresContext, aFrame); + } + } + } + + return reflowStatus; +} + +/** + * The CSS2 spec, section 8.3.1 defines margin collapsing to apply to + * vertical margins of block boxes. And the spec also indicates that + * this should be done for two or more adjacent vertical margins. It + * also indicates that the margins collapse between boxes that are + * next to each other or nested. + */ +void +nsBlockReflowContext::CollapseMargins(const nsMargin& aMargin, + nscoord aCarriedOutTopMargin, + nscoord aCarriedOutBottomMargin, + nscoord aFrameHeight, + nscoord aPrevBottomMargin, + nscoord& aTopMarginResult, + nscoord& aBottomMarginResult) +{ + // Compute the collapsed top margin value. The top margin value is a + // 3 way margin collapse. First we collapse the carried out top + // margin with the block frames top margin (this is a CSS2 "nested" + // collapse). Then we collapse that value with the previous bottom + // margin (because the collapsed margin is adjacent to the previous + // bottom margin). + nscoord carriedTopMargin = aCarriedOutTopMargin; + nscoord topMargin = aMargin.top; + topMargin = MaxMargin(topMargin, carriedTopMargin); + topMargin = MaxMargin(topMargin, aPrevBottomMargin); + + // Compute the collapsed bottom margin value. The bottom margin + // value is a 2 way margin collapse. Collapse the carried out bottom + // margin with the block frames bottom margin (this is a CSS2 + // "nested" collapse). + nscoord carriedBottomMargin = aCarriedOutBottomMargin; + nscoord bottomMargin = aMargin.bottom; + bottomMargin = MaxMargin(bottomMargin, carriedBottomMargin); + + // If the block line is empty then we collapse the top and bottom + // margin values (because the margins are adjacent). + if (0 == aFrameHeight) { + nscoord collapsedMargin = MaxMargin(topMargin, bottomMargin); + topMargin = 0; + bottomMargin = collapsedMargin; + } + + aTopMarginResult = topMargin; + aBottomMarginResult = bottomMargin; +} + +/** + * Attempt to place the block frame within the available space. If + * it fits, apply horizontal positioning (CSS 10.3.3), collapse + * margins (CSS2 8.3.1). Also apply relative positioning. + */ +PRBool +nsBlockReflowContext::PlaceBlock(PRBool aForceFit, PRBool aApplyTopMargin, + nscoord aPrevBottomMargin, + nsRect& aInFlowBounds, + nsRect& aCombinedRect) +{ + // Compute collapsed margin value + CollapseMargins(mMargin, mMetrics.mCarriedOutTopMargin, + mMetrics.mCarriedOutBottomMargin, + mMetrics.height, aPrevBottomMargin, + mTopMargin, mBottomMargin); + + // See if the block will fit in the available space + PRBool fits; + if (0 == mMetrics.height) { + // Empty blocks do not have anything special done to them and they + // always fit. + nsRect r(mX, mY, 0, 0); + mFrame->SetRect(r); + aInFlowBounds = r; + aCombinedRect = r; + fits = PR_TRUE; + } + else { + nscoord x = mX; + nscoord y = mY; + + // Apply top margin unless it's going to be carried out. + if (aApplyTopMargin) { + y += mTopMargin; + } + + // Position block horizontally according to CSS2 10.3.3 + if (NS_UNCONSTRAINEDSIZE != mSpace.width) { + // XXX left/right block margins should be applied right here. + // Because of the issue with floaters and block-left/right + // margins this code is not yet complete. This code may need to + // move to before reflowing the frame instead of after (because + // of floater positioning). + nscoord remainder = mSpace.XMost() - (x + mMetrics.width); + if (remainder >= 0) { + // The block frame didn't use all of the available space. Apply + // auto margins. + const nsStyleSpacing* spacing = mSpacing; + if (nsnull != mSpacing) { + nsStyleUnit leftUnit = mSpacing->mMargin.GetLeftUnit(); + nsStyleUnit rightUnit = mSpacing->mMargin.GetRightUnit(); + if (eStyleUnit_Auto == leftUnit) { + if (eStyleUnit_Auto == rightUnit) { + // When both margins are auto, we center the block + x += remainder / 2; + } + else { + x += remainder; + } + } + else if (eStyleUnit_Auto != rightUnit) { + // When neither margin is auto then text-align applies + const nsStyleText* styleText = mOuterReflowState.mStyleText; + switch (styleText->mTextAlign) { + case NS_STYLE_TEXT_ALIGN_DEFAULT: + case NS_STYLE_TEXT_ALIGN_JUSTIFY: + if (NS_STYLE_DIRECTION_RTL == mOuterReflowState.mDirection) { + // When given a default alignment, and a right-to-left + // direction, right align the frame. + x += remainder; + } + break; + case NS_STYLE_TEXT_ALIGN_RIGHT: + x += remainder; + break; + case NS_STYLE_TEXT_ALIGN_CENTER: + x += remainder / 2; + break; + } + } + } + } + else { + // XXX Handle over-constrained case by forcing the appropriate + // margin to be auto + } + } + + // See if the frame fit. If its the first frame then it always + // fits. + if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) { + fits = PR_TRUE; + + // Update the in-flow bounding box's bounds + aInFlowBounds.SetRect(x, y, mMetrics.width, mMetrics.height); + + // Apply CSS relative positioning to update x,y coordinates + const nsStylePosition* stylePos; + mFrame->GetStyleData(eStyleStruct_Position, + (const nsStyleStruct*&)stylePos); + if (NS_STYLE_POSITION_RELATIVE == stylePos->mPosition) { + ComputeRelativePosition(mFrame, stylePos, x, y); + } + + // Compute combined-rect in callers coordinate system. The value + // returned in the reflow metrics is relative to the child + // frame. + aCombinedRect.x = mMetrics.mCombinedArea.x + x; + aCombinedRect.y = mMetrics.mCombinedArea.y + y; + aCombinedRect.width = mMetrics.mCombinedArea.width; + aCombinedRect.height = mMetrics.mCombinedArea.height; + + // Now place the frame + mFrame->SetRect(nsRect(x, y, mMetrics.width, mMetrics.height)); + + // If the block frame ended up moving then we need to slide + // anything inside of it that impacts the space manager + // (otherwise the impacted space in the space manager will be + // out of sync with where the frames really are). + nscoord dx = x - mX; + nscoord dy = y - mY; + if ((0 != dx) || (0 != dy)) { + nsIHTMLReflow* htmlReflow; + nsresult rv; + rv = mFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); + if (NS_SUCCEEDED(rv)) { + // If the child has any floaters that impact the space manager, + // slide them now + htmlReflow->MoveInSpaceManager(mPresContext, + mOuterReflowState.spaceManager, + dx, dy); + } + } + } + else { + fits = PR_FALSE; + } + } + + return fits; +} + +void +nsBlockReflowContext::ComputeRelativePosition(nsIFrame* aFrame, + const nsStylePosition* aStylePos, + nscoord& aX, nscoord& aY) +{ + nscoord dx = 0; + switch (aStylePos->mLeftOffset.GetUnit()) { + case eStyleUnit_Percent: + printf("XXX: not yet implemented: % relative position\n"); + case eStyleUnit_Auto: + break; + case eStyleUnit_Coord: + dx = aStylePos->mLeftOffset.GetCoordValue(); + break; + } + aX += dx; + + nscoord dy = 0; + switch (aStylePos->mTopOffset.GetUnit()) { + case eStyleUnit_Percent: + printf("XXX: not yet implemented: % relative position\n"); + case eStyleUnit_Auto: + break; + case eStyleUnit_Coord: + dy = aStylePos->mTopOffset.GetCoordValue(); + break; + } + aY += dy; +} diff --git a/mozilla/layout/generic/nsBlockReflowContext.h b/mozilla/layout/generic/nsBlockReflowContext.h new file mode 100644 index 00000000000..58ddd2279d9 --- /dev/null +++ b/mozilla/layout/generic/nsBlockReflowContext.h @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsBlockReflowContext_h___ +#define nsBlockReflowContext_h___ + +#include "nsIHTMLReflow.h" + +class nsBlockFrame; +class nsFrameReflowState; +class nsIFrame; +class nsIPresContext; +class nsLineLayout; +struct nsStylePosition; +struct nsStyleSpacing; + +/** + * An encapsulation of the state and algorithm for reflowing block frames. + */ +class nsBlockReflowContext { +public: + nsBlockReflowContext(nsIPresContext& aPresContext, + nsLineLayout& aLineLayout, + nsFrameReflowState& aParentRS); + ~nsBlockReflowContext() { } + + void SetRunInFrame(nsBlockFrame* aRunInFrame) { + mRunInFrame = aRunInFrame; + } + + void SetCompactMarginWidth(nscoord aCompactMarginWidth) { + mCompactMarginWidth = aCompactMarginWidth; + } + + nsresult ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace); + + PRBool PlaceBlock(PRBool aForceFit, PRBool aApplyTopMargin, + nscoord aPrevBottomMargin, + nsRect& aInFlowBounds, + nsRect& aCombinedRect); + + nscoord GetCollapsedTopMargin() const { + return mTopMargin; + } + + nscoord GetCollapsedBottomMargin() const { + return mBottomMargin; + } + + nscoord GetCarriedOutTopMargin() const { + return mMetrics.mCarriedOutTopMargin; + } + + nscoord GetCarriedOutBottomMargin() const { + return mMetrics.mCarriedOutBottomMargin; + } + + const nsSize& GetMaxElementSize() const { + return mMaxElementSize; + } + + static void ComputeMarginsFor(nsIPresContext& aPresContext, + nsIFrame* aFrame, + const nsStyleSpacing* aSpacing, + const nsFrameReflowState& aParentRS, + nsMargin& aResult); + + static void CollapseMargins(const nsMargin& aMargin, + nscoord aCarriedOutTopMargin, + nscoord aCarriedOutBottomMargin, + nscoord aFrameHeight, + nscoord aPrevBottomMargin, + nscoord& aTopMarginResult, + nscoord& aBottomMarginResult); + + static nscoord MaxMargin(nscoord a, nscoord b) { + if (a < 0) { + if (b < 0) { + if (a < b) return a; + return b; + } + return b + a; + } + else if (b < 0) { + return a + b; + } + if (a > b) return a; + return b; + } + + static void ComputeRelativePosition(nsIFrame* aFrame, + const nsStylePosition* aStylePos, + nscoord& aX, nscoord& aY); + +protected: + nsIPresContext& mPresContext; + nsLineLayout& mLineLayout; + nsFrameReflowState& mOuterReflowState; + + nsIFrame* mFrame; + nsBlockFrame* mRunInFrame; + nscoord mCompactMarginWidth; + nsRect mSpace; + + const nsStyleSpacing* mSpacing; + nsMargin mMargin; + nscoord mX, mY; + nsHTMLReflowMetrics mMetrics; + nscoord mTopMargin; + nscoord mBottomMargin; + nsSize mMaxElementSize; +}; + +#endif /* nsBlockReflowContext_h___ */ diff --git a/mozilla/layout/generic/nsBulletFrame.cpp b/mozilla/layout/generic/nsBulletFrame.cpp new file mode 100644 index 00000000000..e7455208697 --- /dev/null +++ b/mozilla/layout/generic/nsBulletFrame.cpp @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsBulletFrame.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLParts.h" +#include "nsHTMLValue.h" +#include "nsHTMLContainerFrame.h" +#include "nsIFontMetrics.h" +#include "nsIHTMLContent.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIReflowCommand.h" +#include "nsIRenderingContext.h" +#include "prprf.h" + +nsBulletFrame::nsBulletFrame(nsIContent* aContent, nsIFrame* aParentFrame) + : nsFrame(aContent, aParentFrame) +{ +} + +nsBulletFrame::~nsBulletFrame() +{ +} + +NS_IMETHODIMP +nsBulletFrame::DeleteFrame(nsIPresContext& aPresContext) +{ + // Release image loader first so that its refcnt can go to zero + mImageLoader.DestroyLoader(); + return nsFrame::DeleteFrame(aPresContext); +} + +NS_IMETHODIMP +nsBulletFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("Bullet", aResult); +} + +NS_IMETHODIMP +nsBulletFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const +{ + PRInt32 i; + for (i = aIndent; --i >= 0; ) fputs(" ", out); + fprintf(out, "Bullet(%d)@%p ", ContentIndexInContainer(this), this); + nsIView* view; + GetView(view); + if (nsnull != view) { + fprintf(out, " [view=%p]", view); + } + + out << mRect; + if (0 != mState) { + fprintf(out, " [state=%08x]", mState); + } + fputs("<>\n", out); + return NS_OK; +} + +NS_METHOD +nsBulletFrame::Paint(nsIPresContext& aCX, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + const nsStyleDisplay* disp = + (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + nscoord width; + + if (disp->mVisible) { + const nsStyleList* myList = + (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List); + + if (myList->mListStyleImage.Length() > 0) { + nsIImage* image = mImageLoader.GetImage(); + if (nsnull == image) { + if (!mImageLoader.GetLoadImageFailed()) { + // No image yet + return NS_OK; + } + } + else { + nsRect innerArea(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + aRenderingContext.DrawImage(image, innerArea); + return NS_OK; + } + } + + const nsStyleFont* myFont = + (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + const nsStyleColor* myColor = + (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + nsIFontMetrics* fm; + aRenderingContext.SetColor(myColor->mColor); + + nsAutoString text; + switch (myList->mListStyleType) { + case NS_STYLE_LIST_STYLE_NONE: + break; + + default: + case NS_STYLE_LIST_STYLE_BASIC: + case NS_STYLE_LIST_STYLE_DISC: + aRenderingContext.FillEllipse(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_CIRCLE: + aRenderingContext.DrawEllipse(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_SQUARE: + aRenderingContext.FillRect(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_DECIMAL: + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + fm = aCX.GetMetricsFor(myFont->mFont); + GetListItemText(aCX, *myList, text); + aRenderingContext.SetFont(fm); + aRenderingContext.GetWidth(text, width); + aRenderingContext.DrawString(text, mPadding.left, mPadding.top, width); + NS_RELEASE(fm); + break; + } + } + return NS_OK; +} + +PRInt32 +nsBulletFrame::SetListItemOrdinal(PRInt32 aNextOrdinal) +{ + // Assume that the ordinal comes from the block reflow state + mOrdinal = aNextOrdinal; + + // Try to get value directly from the list-item, if it specifies a + // value attribute. Note: we do this with our parent's content + // because our parent is the list-item. + nsHTMLValue value; + nsIContent* parentContent; + mContentParent->GetContent(parentContent); + nsIHTMLContent* hc; + if (NS_OK == parentContent->QueryInterface(kIHTMLContentIID, (void**) &hc)) { + if (NS_CONTENT_ATTR_HAS_VALUE == + hc->GetAttribute(nsHTMLAtoms::value, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + // Use ordinal specified by the value attribute + mOrdinal = value.GetIntValue(); + if (mOrdinal <= 0) { + mOrdinal = 1; + } + } + } + NS_RELEASE(hc); + } + NS_RELEASE(parentContent); + + return mOrdinal + 1; +} + +static const char* gLowerRomanCharsA = "ixcm"; +static const char* gUpperRomanCharsA = "IXCM"; +static const char* gLowerRomanCharsB = "vld?"; +static const char* gUpperRomanCharsB = "VLD?"; +static const char* gLowerAlphaChars = "abcdefghijklmnopqrstuvwxyz"; +static const char* gUpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +// XXX change roman/alpha to use unsigned math so that maxint and +// maxnegint will work +void +nsBulletFrame::GetListItemText(nsIPresContext& aCX, + const nsStyleList& aListStyle, + nsString& result) +{ + PRInt32 ordinal = mOrdinal; + char cbuf[40]; + switch (aListStyle.mListStyleType) { + case NS_STYLE_LIST_STYLE_DECIMAL: + PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal); + result.Append(cbuf); + break; + + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + { + if (ordinal <= 0) { + ordinal = 1; + } + nsAutoString addOn, decStr; + decStr.Append(ordinal, 10); + PRIntn len = decStr.Length(); + const PRUnichar* dp = decStr.GetUnicode(); + const PRUnichar* end = dp + len; + PRIntn romanPos = len; + PRIntn n; + + const char* achars; + const char* bchars; + if (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ROMAN) { + achars = gLowerRomanCharsA; + bchars = gLowerRomanCharsB; + } + else { + achars = gUpperRomanCharsA; + bchars = gUpperRomanCharsB; + } + for (; dp < end; dp++) { + romanPos--; + addOn.SetLength(0); + switch(*dp) { + case '3': addOn.Append(achars[romanPos]); + case '2': addOn.Append(achars[romanPos]); + case '1': addOn.Append(achars[romanPos]); + break; + case '4': + addOn.Append(achars[romanPos]); + // FALLTHROUGH + case '5': case '6': + case '7': case '8': + addOn.Append(bchars[romanPos]); + for(n=0;n<(*dp-'5');n++) { + addOn.Append(achars[romanPos]); + } + break; + case '9': + addOn.Append(achars[romanPos]); + addOn.Append(achars[romanPos+1]); + break; + default: + break; + } + result.Append(addOn); + } + } + break; + + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + { + PRInt32 anOffset = -1; + PRInt32 aBase = 26; + PRInt32 ndex=0; + PRInt32 root=1; + PRInt32 next=aBase; + PRInt32 expn=1; + const char* chars = + (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ALPHA) + ? gLowerAlphaChars : gUpperAlphaChars; + + // must be positive here... + if (ordinal <= 0) { + ordinal = 1; + } + ordinal--; // a == 0 + + // scale up in baseN; exceed current value. + while (next<=ordinal) { + root=next; + next*=aBase; + expn++; + } + while (0!=(expn--)) { + ndex = ((root<=ordinal) && (0!=root)) ? (ordinal/root): 0; + ordinal %= root; + if (root>1) + result.Append(chars[ndex+anOffset]); + else + result.Append(chars[ndex]); + root /= aBase; + } + } + break; + } + result.Append("."); +} + +#define MIN_BULLET_SIZE 5 // from laytext.c + +static nsresult +UpdateBulletCB(nsIPresContext& aPresContext, nsIFrame* aFrame, PRIntn aStatus) +{ + nsresult rv = NS_OK; + if (NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE & aStatus) { + // Now that the size is available, trigger a reflow of the bullet + // frame. + nsIPresShell* shell; + shell = aPresContext.GetShell(); + if (nsnull != shell) { + nsIReflowCommand* cmd; + rv = NS_NewHTMLReflowCommand(&cmd, aFrame, + nsIReflowCommand::ContentChanged); + if (NS_OK == rv) { + shell->EnterReflowLock(); + shell->AppendReflowCommand(cmd); + NS_RELEASE(cmd); + shell->ExitReflowLock(); + } + NS_RELEASE(shell); + } + } + return rv; +} + +void +nsBulletFrame::GetDesiredSize(nsIPresContext* aCX, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aMetrics) +{ + const nsStyleList* myList = + (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List); + nscoord ascent; + + if (myList->mListStyleImage.Length() > 0) { + mImageLoader.SetURL(myList->mListStyleImage); + mImageLoader.GetDesiredSize(aCX, aReflowState, this, UpdateBulletCB, + aMetrics); + if (!mImageLoader.GetLoadImageFailed()) { + nsHTMLContainerFrame::CreateViewForFrame(*aCX, this, mStyleContext, + PR_FALSE); + aMetrics.ascent = aMetrics.height; + aMetrics.descent = 0; + return; + } + } + + const nsStyleFont* myFont = + (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + nsIFontMetrics* fm = aCX->GetMetricsFor(myFont->mFont); + nscoord bulletSize; + float p2t; + float t2p; + + nsAutoString text; + switch (myList->mListStyleType) { + case NS_STYLE_LIST_STYLE_NONE: + aMetrics.width = 0; + aMetrics.height = 0; + aMetrics.ascent = 0; + aMetrics.descent = 0; + break; + + default: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_BASIC: + case NS_STYLE_LIST_STYLE_SQUARE: + t2p = aCX->GetTwipsToPixels(); + fm->GetMaxAscent(ascent); + bulletSize = NSTwipsToIntPixels( + (nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p); + if (bulletSize < 1) { + bulletSize = MIN_BULLET_SIZE; + } + p2t = aCX->GetPixelsToTwips(); + bulletSize = NSIntPixelsToTwips(bulletSize, p2t); + mPadding.bottom = ascent / 8; + aMetrics.width = mPadding.right + bulletSize; + aMetrics.height = mPadding.bottom + bulletSize; + aMetrics.ascent = mPadding.bottom + bulletSize; + aMetrics.descent = 0; + break; + + case NS_STYLE_LIST_STYLE_DECIMAL: + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + GetListItemText(*aCX, *myList, text); + fm->GetHeight(aMetrics.height); + aReflowState.rendContext->SetFont(fm); + aReflowState.rendContext->GetWidth(text, aMetrics.width); + aMetrics.width += mPadding.right; + fm->GetMaxAscent(aMetrics.ascent); + fm->GetMaxDescent(aMetrics.descent); + break; + } + NS_RELEASE(fm); +} + +NS_IMETHODIMP +nsBulletFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aMetrics, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + // Get the base size + GetDesiredSize(&aPresContext, aReflowState, aMetrics); + + // Add in the border and padding; split the top/bottom between the + // ascent and descent to make things look nice + const nsStyleSpacing* space =(const nsStyleSpacing*) + mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin borderPadding; + nsHTMLReflowState::ComputeBorderPaddingFor(this, + aReflowState.parentReflowState, + borderPadding); + aMetrics.width += borderPadding.left + borderPadding.right; + aMetrics.height += borderPadding.top + borderPadding.bottom; + aMetrics.ascent += borderPadding.top; + aMetrics.descent += borderPadding.bottom; + + if (nsnull != aMetrics.maxElementSize) { + aMetrics.maxElementSize->width = aMetrics.width; + aMetrics.maxElementSize->height = aMetrics.height; + } + aStatus = NS_FRAME_COMPLETE; + return NS_OK; +} diff --git a/mozilla/layout/generic/nsBulletFrame.h b/mozilla/layout/generic/nsBulletFrame.h new file mode 100644 index 00000000000..37b8930918e --- /dev/null +++ b/mozilla/layout/generic/nsBulletFrame.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsBulletFrame_h___ +#define nsBulletFrame_h___ + +#include "nsHTMLImage.h" +#include "nsIStyleContext.h" + +/** + * A simple class that manages the layout and rendering of html bullets. + * This class also supports the CSS list-style properties. + */ +class nsBulletFrame : public nsFrame { +public: + nsBulletFrame(nsIContent* aContent, nsIFrame* aParentFrame); + virtual ~nsBulletFrame(); + + // nsIFrame + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); + NS_IMETHOD Paint(nsIPresContext &aCX, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD GetFrameName(nsString& aResult) const; + NS_IMETHOD List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const; + + // nsIHTMLReflow + NS_IMETHOD Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aMetrics, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + // nsBulletFrame + PRInt32 SetListItemOrdinal(PRInt32 aNextOrdinal); + +protected: + void GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aMetrics); + + void GetListItemText(nsIPresContext& aCX, + const nsStyleList& aStyleList, + nsString& aResult); + + PRInt32 mOrdinal; + nsMargin mPadding; + nsHTMLImageLoader mImageLoader; +}; + +#endif /* nsBulletFrame_h___ */ diff --git a/mozilla/layout/generic/nsLineBox.cpp b/mozilla/layout/generic/nsLineBox.cpp new file mode 100644 index 00000000000..617a9585b2f --- /dev/null +++ b/mozilla/layout/generic/nsLineBox.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsLineBox.h" +#include "nsISpaceManager.h" +#include "prprf.h" + +nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags) +{ + mFirstChild = aFrame; + mChildCount = aCount; + mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags; + mFloaters = nsnull; + mNext = nsnull; + mBounds.SetRect(0,0,0,0); + mCombinedArea.SetRect(0,0,0,0); + mCarriedOutTopMargin = 0; + mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; +} + +nsLineBox::~nsLineBox() +{ + if (nsnull != mFloaters) { + delete mFloaters; + } +} + +static void +ListFloaters(FILE* out, PRInt32 aIndent, nsVoidArray* aFloaters) +{ + PRInt32 j, i, n = aFloaters->Count(); + for (i = 0; i < n; i++) { + for (j = aIndent; --j >= 0; ) fputs(" ", out); + nsPlaceholderFrame* ph = (nsPlaceholderFrame*) aFloaters->ElementAt(i); + if (nsnull != ph) { + fprintf(out, "placeholder@%p\n", ph); + nsIFrame* frame = ph->GetAnchoredItem(); + if (nsnull != frame) { + frame->List(out, aIndent + 1, nsnull); + } + } + } +} + +char* +nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const +{ + PR_snprintf(aBuf, aBufSize, "%s,%s", + (mState & LINE_IS_DIRTY) ? "dirty" : "clean", + (mState & LINE_IS_BLOCK) ? "block" : "inline"); + return aBuf; +} + +void +nsLineBox::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter, + PRBool aOutputMe) const +{ + PRInt32 i; + + if (aOutputMe) { + for (i = aIndent; --i >= 0; ) fputs(" ", out); + char cbuf[100]; + fprintf(out, "line %p: count=%d state=%s ", + this, ChildCount(), StateToString(cbuf, sizeof(cbuf))); + if (0 != mCarriedOutTopMargin) { + fprintf(out, "tm=%d ", mCarriedOutTopMargin); + } + if (0 != mCarriedOutBottomMargin) { + fprintf(out, "bm=%d ", mCarriedOutBottomMargin); + } + out << mBounds; + fprintf(out, " ca="); + out << mCombinedArea; + fprintf(out, " <\n"); + } + + nsIFrame* frame = mFirstChild; + PRInt32 n = ChildCount(); + while (--n >= 0) { + frame->List(out, aIndent + 1, aFilter); + frame->GetNextSibling(frame); + } + + if (aOutputMe) { + for (i = aIndent; --i >= 0; ) fputs(" ", out); + if (nsnull != mFloaters) { + fputs("> floaters <\n", out); + ListFloaters(out, aIndent + 1, mFloaters); + for (i = aIndent; --i >= 0; ) fputs(" ", out); + } + fputs(">\n", out); + } +} + +nsIFrame* +nsLineBox::LastChild() const +{ + nsIFrame* frame = mFirstChild; + PRInt32 n = ChildCount() - 1; + while (--n >= 0) { + frame->GetNextSibling(frame); + } + return frame; +} + +PRBool +nsLineBox::IsLastChild(nsIFrame* aFrame) const +{ + nsIFrame* lastFrame = LastChild(); + return aFrame == lastFrame; +} + +PRBool +nsLineBox::Contains(nsIFrame* aFrame) const +{ + PRInt32 n = ChildCount(); + nsIFrame* frame = mFirstChild; + while (--n >= 0) { + if (frame == aFrame) { + return PR_TRUE; + } + frame->GetNextSibling(frame); + } + return PR_FALSE; +} + +#if 0 +static PRInt32 +LengthOf(nsIFrame* aFrame) +{ + PRInt32 result = 0; + while (nsnull != aFrame) { + result++; + aFrame->GetNextSibling(aFrame); + } + return result; +} + +void +nsLineBox::Verify() +{ + nsIFrame* lastFrame = LastChild(); + if (nsnull != lastFrame) { + nsIFrame* nextInFlow; + lastFrame->GetNextInFlow(nextInFlow); + if (nsnull != mNext) { + nsIFrame* nextSibling; + lastFrame->GetNextSibling(nextSibling); + NS_ASSERTION(mNext->mFirstChild == nextSibling, "bad line list"); + } + } + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(len >= ChildCount(), "bad mChildCount"); +} + +static void +VerifyLines(nsLineBox* aLine) +{ + while (nsnull != aLine) { + aLine->Verify(); + aLine = aLine->mNext; + } +} + +static void +VerifyChildCount(nsLineBox* aLines, PRBool aEmptyOK = PR_FALSE) +{ + if (nsnull != aLines) { + PRInt32 childCount = LengthOf(aLines->mFirstChild); + PRInt32 sum = 0; + nsLineBox* line = aLines; + while (nsnull != line) { + if (!aEmptyOK) { + NS_ASSERTION(0 != line->ChildCount(), "empty line left in line list"); + } + sum += line->ChildCount(); + line = line->mNext; + } + if (sum != childCount) { + printf("Bad sibling list/line mChildCount's\n"); + nsLineBox* line = aLines; + while (nsnull != line) { + line->List(stdout, 1); + if (nsnull != line->mNext) { + nsIFrame* lastFrame = line->LastChild(); + if (nsnull != lastFrame) { + nsIFrame* nextSibling; + lastFrame->GetNextSibling(nextSibling); + if (line->mNext->mFirstChild != nextSibling) { + printf(" [list broken: nextSibling=%p mNext->mFirstChild=%p]\n", + nextSibling, line->mNext->mFirstChild); + } + } + } + line = line->mNext; + } + NS_ASSERTION(sum == childCount, "bad sibling list/line mChildCount's"); + } + } +} +#endif + +void +nsLineBox::DeleteLineList(nsIPresContext& aPresContext, nsLineBox* aLine) +{ + if (nsnull != aLine) { + // Delete our child frames before doing anything else. In particular + // we do all of this before our base class releases it's hold on the + // view. + for (nsIFrame* child = aLine->mFirstChild; child; ) { + nsIFrame* nextChild; + child->GetNextSibling(nextChild); + child->DeleteFrame(aPresContext); + child = nextChild; + } + + while (nsnull != aLine) { + nsLineBox* next = aLine->mNext; + delete aLine; + aLine = next; + } + } +} + +nsLineBox* +nsLineBox::LastLine(nsLineBox* aLine) +{ + if (nsnull != aLine) { + while (nsnull != aLine->mNext) { + aLine = aLine->mNext; + } + } + return aLine; +} + +nsLineBox* +nsLineBox::FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame) +{ + while (nsnull != aLine) { + if (aLine->Contains(aFrame)) { + return aLine; + } + aLine = aLine->mNext; + } + return nsnull; +} + +void +nsLineBox::UnplaceFloaters(nsISpaceManager* aSpaceManager) +{ + if (nsnull != mFloaters) { + PRInt32 i, n = mFloaters->Count(); + for (i = 0; i < n; i++) { + nsPlaceholderFrame* pf = (nsPlaceholderFrame*) mFloaters->ElementAt(i); + nsIFrame* floater = pf->GetAnchoredItem(); + aSpaceManager->RemoveRegion(floater); + } + } +} diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h new file mode 100644 index 00000000000..a39dbc065dc --- /dev/null +++ b/mozilla/layout/generic/nsLineBox.h @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsLineBox_h___ +#define nsLineBox_h___ + +#include "nsVoidArray.h" +#include "nsPlaceholderFrame.h" + +// bits in nsLineBox.mFlags +#define LINE_IS_DIRTY 0x1 +#define LINE_IS_BLOCK 0x2 +#define LINE_NEED_DID_REFLOW 0x8 +#define LINE_TOP_MARGIN_IS_AUTO 0x10 +#define LINE_BOTTOM_MARGIN_IS_AUTO 0x20 +#define LINE_OUTSIDE_CHILDREN 0x40 + +class nsISpaceManager; +class nsLineBox; + +/** + * A list of block lines + */ +class nsLineBoxList { +public: + +protected: + nsLineBox* mLines; +}; + +/** + * The nsLineBox class represents a horizontal line of frames. It contains + * enough state to support incremental reflow of the frames, event handling + * for the frames, and rendering of the frames. + */ +class nsLineBox { +public: + nscoord GetCarriedOutTopMargin() const { + return mCarriedOutTopMargin; + } + + nscoord GetCarriedOutBottomMargin() const { + return mCarriedOutBottomMargin; + } + + nscoord GetCarriedOutMarginFlags() const { + return 0;/* XXX write me */ + } + + nscoord GetHeight() const { return mBounds.height; } + + //---------------------------------------------------------------------- + // XXX old junk + nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags); + + ~nsLineBox(); + + static void DeleteLineList(nsIPresContext& aPresContext, nsLineBox* aLine); + + static nsLineBox* LastLine(nsLineBox* aLine); + + static nsLineBox* FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame); + + void List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter, + PRBool aOutputMe) const; + + PRInt32 ChildCount() const { + return PRInt32(mChildCount); + } + + nsIFrame* LastChild() const; + + PRBool IsLastChild(nsIFrame* aFrame) const; + + PRBool IsBlock() const { + return 0 != (LINE_IS_BLOCK & mState); + } + + void SetIsBlock() { + mState |= LINE_IS_BLOCK; + } + + void ClearIsBlock() { + mState &= ~LINE_IS_BLOCK; + } + + void SetIsBlock(PRBool aValue) { + if (aValue) { + SetIsBlock(); + } + else { + ClearIsBlock(); + } + } + + void MarkDirty() { + mState |= LINE_IS_DIRTY; + } + + void ClearDirty() { + mState &= ~LINE_IS_DIRTY; + } + + void SetNeedDidReflow() { + mState |= LINE_NEED_DID_REFLOW; + } + + void ClearNeedDidReflow() { + mState &= ~LINE_NEED_DID_REFLOW; + } + + PRBool NeedsDidReflow() { + return 0 != (LINE_NEED_DID_REFLOW & mState); + } + + PRBool IsDirty() const { + return 0 != (LINE_IS_DIRTY & mState); + } + + void SetOutsideChildren() { + mState |= LINE_OUTSIDE_CHILDREN; + } + + void ClearOutsideChildren() { + mState &= ~LINE_OUTSIDE_CHILDREN; + } + + PRBool OutsideChildren() const { + return 0 != (LINE_OUTSIDE_CHILDREN & mState); + } + + PRUint16 GetState() const { return mState; } + + char* StateToString(char* aBuf, PRInt32 aBufSize) const; + + PRBool Contains(nsIFrame* aFrame) const; + + // XXX ick + void SetMarginFlags(PRUintn aFlags) { + mState &= ~(LINE_TOP_MARGIN_IS_AUTO|LINE_BOTTOM_MARGIN_IS_AUTO); + if (NS_CARRIED_TOP_MARGIN_IS_AUTO & aFlags) { + mState |= LINE_TOP_MARGIN_IS_AUTO; + } + if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aFlags) { + mState |= LINE_BOTTOM_MARGIN_IS_AUTO; + } + } + + PRUintn GetMarginFlags() { + return ((LINE_TOP_MARGIN_IS_AUTO & mState) + ? NS_CARRIED_TOP_MARGIN_IS_AUTO : 0) | + ((LINE_BOTTOM_MARGIN_IS_AUTO & mState) ? + NS_CARRIED_BOTTOM_MARGIN_IS_AUTO : 0); + } + + void UnplaceFloaters(nsISpaceManager* aSpaceManager); + +#ifdef NS_DEBUG + void Verify(); +#endif + +//XXX protected: + nsIFrame* mFirstChild; + PRUint16 mChildCount; + PRUint16 mState; + PRUint8 mBreakType; + nsRect mBounds; + nsRect mCombinedArea; + nscoord mCarriedOutTopMargin;/* XXX switch to 16 bits */ + nscoord mCarriedOutBottomMargin;/* XXX switch to 16 bits */ + nsVoidArray* mFloaters; + nsLineBox* mNext; + +protected: +}; + +#endif /* nsLineBox_h___ */ diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp new file mode 100644 index 00000000000..e0c15a823e6 --- /dev/null +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp @@ -0,0 +1,407 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsBlockReflowContext.h" +#include "nsFrameReflowState.h" +#include "nsLineLayout.h" +#include "nsHTMLIIDs.h" +#include "nsISpaceManager.h" +#include "nsIFontMetrics.h" +#include "nsIPresContext.h" +#include "nsIStyleContext.h" +#include "nsHTMLContainerFrame.h" + +nsBlockReflowContext::nsBlockReflowContext(nsIPresContext& aPresContext, + nsLineLayout& aLineLayout, + nsFrameReflowState& aParentRS) + : mPresContext(aPresContext), + mLineLayout(aLineLayout), + mOuterReflowState(aParentRS), + mMetrics(aParentRS.mComputeMaxElementSize ? &mMaxElementSize : nsnull) +{ + mRunInFrame = nsnull; + mCompactMarginWidth = 0; + mSpacing = nsnull; +} + +void +nsBlockReflowContext::ComputeMarginsFor(nsIPresContext& aPresContext, + nsIFrame* aFrame, + const nsStyleSpacing* aSpacing, + const nsFrameReflowState& aParentRS, + nsMargin& aResult) +{ + // XXX pass in mSpacing to ComputeMarginFor + nsHTMLReflowState::ComputeMarginFor(aFrame, &aParentRS, aResult); + + // Compute auto top/bottom margin values + nsStyleUnit topUnit = aSpacing->mMargin.GetTopUnit(); + nsStyleUnit bottomUnit = aSpacing->mMargin.GetBottomUnit(); + nscoord fontHeight = 0; + if ((eStyleUnit_Auto == topUnit) || (eStyleUnit_Auto == bottomUnit)) { + // XXX Use the font for the frame, not the default font??? + const nsFont& defaultFont = aPresContext.GetDefaultFont(); + nsIFontMetrics* fm = aPresContext.GetMetricsFor(defaultFont); + fm->GetHeight(fontHeight); + NS_RELEASE(fm); + } + + // For auto margins use the font height computed above + if (eStyleUnit_Auto == topUnit) { + aResult.top = fontHeight; + } + if (eStyleUnit_Auto == bottomUnit) { + aResult.bottom = fontHeight; + } +} + +nsresult +nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace) +{ + nsReflowStatus reflowStatus; + + mFrame = aFrame; + mSpace = aSpace; + + // Compute the margins (including auto margins) + aFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)mSpacing); + ComputeMarginsFor(mPresContext, aFrame, mSpacing, mOuterReflowState, + mMargin); + + // Compute x/y coordinate where reflow will begin. Use the rules + // from 10.3.3 to determine what to apply. At this point in the + // reflow auto left/right margins will have a zero value. + nscoord x = aSpace.x + mMargin.left; + nscoord y = aSpace.y; + mX = x; + mY = y; + nsSize availSize(aSpace.width, aSpace.height); + if (NS_UNCONSTRAINEDSIZE != aSpace.width) { + availSize.width -= mMargin.left; + } + if (NS_UNCONSTRAINEDSIZE != aSpace.height) { + availSize.height -= mMargin.top + mMargin.bottom; + } + + // Get reflow reason set correctly. It's possible that a child was + // created and then it was decided that it could not be reflowed + // (for example, a block frame that isn't at the start of a + // line). In this case the reason will be wrong so we need to check + // the frame state. + nsReflowReason reason = eReflowReason_Resize; + nsFrameState state; + aFrame->GetFrameState(state); + if (NS_FRAME_FIRST_REFLOW & state) { + reason = eReflowReason_Initial; + } + else if (mOuterReflowState.mNextRCFrame == aFrame) { + reason = eReflowReason_Incremental; + // Make sure we only incrementally reflow once + // XXX caller should do this, yes? + mOuterReflowState.mNextRCFrame = nsnull; + } + + // Setup reflow state for reflowing the frame + nsHTMLReflowState reflowState(mPresContext, aFrame, mOuterReflowState, + availSize); + reflowState.lineLayout = nsnull; + reflowState.mRunInFrame = mRunInFrame; + reflowState.mCompactMarginWidth = mCompactMarginWidth; + reflowState.reason = reason; + mLineLayout.SetUnderstandsWhiteSpace(PR_FALSE); + + // Let frame know that we are reflowing it + nsIHTMLReflow* htmlReflow; + nsresult rv = aFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); + if (!NS_SUCCEEDED(rv)) { + return rv; + } + htmlReflow->WillReflow(mPresContext); + + // Adjust spacemanager coordinate system for the frame. The + // spacemanager coordinates are inside the callers + // border+padding, but the x/y coordinates are not (recall that + // frame coordinates are relative to the parents origin and that the + // parents border/padding is inside the parent + // frame. Therefore we have to subtract out the parents + // border+padding before translating. + nscoord tx = x - mOuterReflowState.mBorderPadding.left; + nscoord ty = y - mOuterReflowState.mBorderPadding.top; + aFrame->MoveTo(x, y); + mOuterReflowState.spaceManager->Translate(tx, ty); + htmlReflow->Reflow(mPresContext, mMetrics, reflowState, reflowStatus); + mOuterReflowState.spaceManager->Translate(-tx, -ty); + + aFrame->GetFrameState(state); + if (0 == (NS_FRAME_OUTSIDE_CHILDREN & state)) { + // Provide combined area for child that doesn't have any + mMetrics.mCombinedArea.x = 0; + mMetrics.mCombinedArea.y = 0; + mMetrics.mCombinedArea.width = mMetrics.width; + mMetrics.mCombinedArea.height = mMetrics.height; + } + + // Now that frame has been reflowed at least one time make sure that + // the NS_FRAME_FIRST_REFLOW bit is cleared so that never give it an + // initial reflow reason again. + if (eReflowReason_Initial == reason) { + aFrame->SetFrameState(state & ~NS_FRAME_FIRST_REFLOW); + } + + if (!NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) { + // If frame is complete and has a next-in-flow, we need to delete + // them now. Do not do this when a break-before is signaled because + // the frame is going to get reflowed again (and may end up wanting + // a next-in-flow where it ends up). + if (NS_FRAME_IS_COMPLETE(reflowStatus)) { + nsIFrame* kidNextInFlow; + aFrame->GetNextInFlow(kidNextInFlow); + if (nsnull != kidNextInFlow) { + // Remove all of the childs next-in-flows. Make sure that we ask + // the right parent to do the removal (it's possible that the + // parent is not this because we are executing pullup code) +/* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */ + nsHTMLContainerFrame* parent; + aFrame->GetGeometricParent((nsIFrame*&) parent); + parent->DeleteChildsNextInFlow(mPresContext, aFrame); + } + } + } + + return reflowStatus; +} + +/** + * The CSS2 spec, section 8.3.1 defines margin collapsing to apply to + * vertical margins of block boxes. And the spec also indicates that + * this should be done for two or more adjacent vertical margins. It + * also indicates that the margins collapse between boxes that are + * next to each other or nested. + */ +void +nsBlockReflowContext::CollapseMargins(const nsMargin& aMargin, + nscoord aCarriedOutTopMargin, + nscoord aCarriedOutBottomMargin, + nscoord aFrameHeight, + nscoord aPrevBottomMargin, + nscoord& aTopMarginResult, + nscoord& aBottomMarginResult) +{ + // Compute the collapsed top margin value. The top margin value is a + // 3 way margin collapse. First we collapse the carried out top + // margin with the block frames top margin (this is a CSS2 "nested" + // collapse). Then we collapse that value with the previous bottom + // margin (because the collapsed margin is adjacent to the previous + // bottom margin). + nscoord carriedTopMargin = aCarriedOutTopMargin; + nscoord topMargin = aMargin.top; + topMargin = MaxMargin(topMargin, carriedTopMargin); + topMargin = MaxMargin(topMargin, aPrevBottomMargin); + + // Compute the collapsed bottom margin value. The bottom margin + // value is a 2 way margin collapse. Collapse the carried out bottom + // margin with the block frames bottom margin (this is a CSS2 + // "nested" collapse). + nscoord carriedBottomMargin = aCarriedOutBottomMargin; + nscoord bottomMargin = aMargin.bottom; + bottomMargin = MaxMargin(bottomMargin, carriedBottomMargin); + + // If the block line is empty then we collapse the top and bottom + // margin values (because the margins are adjacent). + if (0 == aFrameHeight) { + nscoord collapsedMargin = MaxMargin(topMargin, bottomMargin); + topMargin = 0; + bottomMargin = collapsedMargin; + } + + aTopMarginResult = topMargin; + aBottomMarginResult = bottomMargin; +} + +/** + * Attempt to place the block frame within the available space. If + * it fits, apply horizontal positioning (CSS 10.3.3), collapse + * margins (CSS2 8.3.1). Also apply relative positioning. + */ +PRBool +nsBlockReflowContext::PlaceBlock(PRBool aForceFit, PRBool aApplyTopMargin, + nscoord aPrevBottomMargin, + nsRect& aInFlowBounds, + nsRect& aCombinedRect) +{ + // Compute collapsed margin value + CollapseMargins(mMargin, mMetrics.mCarriedOutTopMargin, + mMetrics.mCarriedOutBottomMargin, + mMetrics.height, aPrevBottomMargin, + mTopMargin, mBottomMargin); + + // See if the block will fit in the available space + PRBool fits; + if (0 == mMetrics.height) { + // Empty blocks do not have anything special done to them and they + // always fit. + nsRect r(mX, mY, 0, 0); + mFrame->SetRect(r); + aInFlowBounds = r; + aCombinedRect = r; + fits = PR_TRUE; + } + else { + nscoord x = mX; + nscoord y = mY; + + // Apply top margin unless it's going to be carried out. + if (aApplyTopMargin) { + y += mTopMargin; + } + + // Position block horizontally according to CSS2 10.3.3 + if (NS_UNCONSTRAINEDSIZE != mSpace.width) { + // XXX left/right block margins should be applied right here. + // Because of the issue with floaters and block-left/right + // margins this code is not yet complete. This code may need to + // move to before reflowing the frame instead of after (because + // of floater positioning). + nscoord remainder = mSpace.XMost() - (x + mMetrics.width); + if (remainder >= 0) { + // The block frame didn't use all of the available space. Apply + // auto margins. + const nsStyleSpacing* spacing = mSpacing; + if (nsnull != mSpacing) { + nsStyleUnit leftUnit = mSpacing->mMargin.GetLeftUnit(); + nsStyleUnit rightUnit = mSpacing->mMargin.GetRightUnit(); + if (eStyleUnit_Auto == leftUnit) { + if (eStyleUnit_Auto == rightUnit) { + // When both margins are auto, we center the block + x += remainder / 2; + } + else { + x += remainder; + } + } + else if (eStyleUnit_Auto != rightUnit) { + // When neither margin is auto then text-align applies + const nsStyleText* styleText = mOuterReflowState.mStyleText; + switch (styleText->mTextAlign) { + case NS_STYLE_TEXT_ALIGN_DEFAULT: + case NS_STYLE_TEXT_ALIGN_JUSTIFY: + if (NS_STYLE_DIRECTION_RTL == mOuterReflowState.mDirection) { + // When given a default alignment, and a right-to-left + // direction, right align the frame. + x += remainder; + } + break; + case NS_STYLE_TEXT_ALIGN_RIGHT: + x += remainder; + break; + case NS_STYLE_TEXT_ALIGN_CENTER: + x += remainder / 2; + break; + } + } + } + } + else { + // XXX Handle over-constrained case by forcing the appropriate + // margin to be auto + } + } + + // See if the frame fit. If its the first frame then it always + // fits. + if (aForceFit || (y + mMetrics.height <= mSpace.YMost())) { + fits = PR_TRUE; + + // Update the in-flow bounding box's bounds + aInFlowBounds.SetRect(x, y, mMetrics.width, mMetrics.height); + + // Apply CSS relative positioning to update x,y coordinates + const nsStylePosition* stylePos; + mFrame->GetStyleData(eStyleStruct_Position, + (const nsStyleStruct*&)stylePos); + if (NS_STYLE_POSITION_RELATIVE == stylePos->mPosition) { + ComputeRelativePosition(mFrame, stylePos, x, y); + } + + // Compute combined-rect in callers coordinate system. The value + // returned in the reflow metrics is relative to the child + // frame. + aCombinedRect.x = mMetrics.mCombinedArea.x + x; + aCombinedRect.y = mMetrics.mCombinedArea.y + y; + aCombinedRect.width = mMetrics.mCombinedArea.width; + aCombinedRect.height = mMetrics.mCombinedArea.height; + + // Now place the frame + mFrame->SetRect(nsRect(x, y, mMetrics.width, mMetrics.height)); + + // If the block frame ended up moving then we need to slide + // anything inside of it that impacts the space manager + // (otherwise the impacted space in the space manager will be + // out of sync with where the frames really are). + nscoord dx = x - mX; + nscoord dy = y - mY; + if ((0 != dx) || (0 != dy)) { + nsIHTMLReflow* htmlReflow; + nsresult rv; + rv = mFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); + if (NS_SUCCEEDED(rv)) { + // If the child has any floaters that impact the space manager, + // slide them now + htmlReflow->MoveInSpaceManager(mPresContext, + mOuterReflowState.spaceManager, + dx, dy); + } + } + } + else { + fits = PR_FALSE; + } + } + + return fits; +} + +void +nsBlockReflowContext::ComputeRelativePosition(nsIFrame* aFrame, + const nsStylePosition* aStylePos, + nscoord& aX, nscoord& aY) +{ + nscoord dx = 0; + switch (aStylePos->mLeftOffset.GetUnit()) { + case eStyleUnit_Percent: + printf("XXX: not yet implemented: % relative position\n"); + case eStyleUnit_Auto: + break; + case eStyleUnit_Coord: + dx = aStylePos->mLeftOffset.GetCoordValue(); + break; + } + aX += dx; + + nscoord dy = 0; + switch (aStylePos->mTopOffset.GetUnit()) { + case eStyleUnit_Percent: + printf("XXX: not yet implemented: % relative position\n"); + case eStyleUnit_Auto: + break; + case eStyleUnit_Coord: + dy = aStylePos->mTopOffset.GetCoordValue(); + break; + } + aY += dy; +} diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.h b/mozilla/layout/html/base/src/nsBlockReflowContext.h new file mode 100644 index 00000000000..58ddd2279d9 --- /dev/null +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.h @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsBlockReflowContext_h___ +#define nsBlockReflowContext_h___ + +#include "nsIHTMLReflow.h" + +class nsBlockFrame; +class nsFrameReflowState; +class nsIFrame; +class nsIPresContext; +class nsLineLayout; +struct nsStylePosition; +struct nsStyleSpacing; + +/** + * An encapsulation of the state and algorithm for reflowing block frames. + */ +class nsBlockReflowContext { +public: + nsBlockReflowContext(nsIPresContext& aPresContext, + nsLineLayout& aLineLayout, + nsFrameReflowState& aParentRS); + ~nsBlockReflowContext() { } + + void SetRunInFrame(nsBlockFrame* aRunInFrame) { + mRunInFrame = aRunInFrame; + } + + void SetCompactMarginWidth(nscoord aCompactMarginWidth) { + mCompactMarginWidth = aCompactMarginWidth; + } + + nsresult ReflowBlock(nsIFrame* aFrame, const nsRect& aSpace); + + PRBool PlaceBlock(PRBool aForceFit, PRBool aApplyTopMargin, + nscoord aPrevBottomMargin, + nsRect& aInFlowBounds, + nsRect& aCombinedRect); + + nscoord GetCollapsedTopMargin() const { + return mTopMargin; + } + + nscoord GetCollapsedBottomMargin() const { + return mBottomMargin; + } + + nscoord GetCarriedOutTopMargin() const { + return mMetrics.mCarriedOutTopMargin; + } + + nscoord GetCarriedOutBottomMargin() const { + return mMetrics.mCarriedOutBottomMargin; + } + + const nsSize& GetMaxElementSize() const { + return mMaxElementSize; + } + + static void ComputeMarginsFor(nsIPresContext& aPresContext, + nsIFrame* aFrame, + const nsStyleSpacing* aSpacing, + const nsFrameReflowState& aParentRS, + nsMargin& aResult); + + static void CollapseMargins(const nsMargin& aMargin, + nscoord aCarriedOutTopMargin, + nscoord aCarriedOutBottomMargin, + nscoord aFrameHeight, + nscoord aPrevBottomMargin, + nscoord& aTopMarginResult, + nscoord& aBottomMarginResult); + + static nscoord MaxMargin(nscoord a, nscoord b) { + if (a < 0) { + if (b < 0) { + if (a < b) return a; + return b; + } + return b + a; + } + else if (b < 0) { + return a + b; + } + if (a > b) return a; + return b; + } + + static void ComputeRelativePosition(nsIFrame* aFrame, + const nsStylePosition* aStylePos, + nscoord& aX, nscoord& aY); + +protected: + nsIPresContext& mPresContext; + nsLineLayout& mLineLayout; + nsFrameReflowState& mOuterReflowState; + + nsIFrame* mFrame; + nsBlockFrame* mRunInFrame; + nscoord mCompactMarginWidth; + nsRect mSpace; + + const nsStyleSpacing* mSpacing; + nsMargin mMargin; + nscoord mX, mY; + nsHTMLReflowMetrics mMetrics; + nscoord mTopMargin; + nscoord mBottomMargin; + nsSize mMaxElementSize; +}; + +#endif /* nsBlockReflowContext_h___ */ diff --git a/mozilla/layout/html/base/src/nsBulletFrame.cpp b/mozilla/layout/html/base/src/nsBulletFrame.cpp new file mode 100644 index 00000000000..e7455208697 --- /dev/null +++ b/mozilla/layout/html/base/src/nsBulletFrame.cpp @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsBulletFrame.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLParts.h" +#include "nsHTMLValue.h" +#include "nsHTMLContainerFrame.h" +#include "nsIFontMetrics.h" +#include "nsIHTMLContent.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIReflowCommand.h" +#include "nsIRenderingContext.h" +#include "prprf.h" + +nsBulletFrame::nsBulletFrame(nsIContent* aContent, nsIFrame* aParentFrame) + : nsFrame(aContent, aParentFrame) +{ +} + +nsBulletFrame::~nsBulletFrame() +{ +} + +NS_IMETHODIMP +nsBulletFrame::DeleteFrame(nsIPresContext& aPresContext) +{ + // Release image loader first so that its refcnt can go to zero + mImageLoader.DestroyLoader(); + return nsFrame::DeleteFrame(aPresContext); +} + +NS_IMETHODIMP +nsBulletFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("Bullet", aResult); +} + +NS_IMETHODIMP +nsBulletFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const +{ + PRInt32 i; + for (i = aIndent; --i >= 0; ) fputs(" ", out); + fprintf(out, "Bullet(%d)@%p ", ContentIndexInContainer(this), this); + nsIView* view; + GetView(view); + if (nsnull != view) { + fprintf(out, " [view=%p]", view); + } + + out << mRect; + if (0 != mState) { + fprintf(out, " [state=%08x]", mState); + } + fputs("<>\n", out); + return NS_OK; +} + +NS_METHOD +nsBulletFrame::Paint(nsIPresContext& aCX, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect) +{ + const nsStyleDisplay* disp = + (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + nscoord width; + + if (disp->mVisible) { + const nsStyleList* myList = + (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List); + + if (myList->mListStyleImage.Length() > 0) { + nsIImage* image = mImageLoader.GetImage(); + if (nsnull == image) { + if (!mImageLoader.GetLoadImageFailed()) { + // No image yet + return NS_OK; + } + } + else { + nsRect innerArea(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + aRenderingContext.DrawImage(image, innerArea); + return NS_OK; + } + } + + const nsStyleFont* myFont = + (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + const nsStyleColor* myColor = + (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + nsIFontMetrics* fm; + aRenderingContext.SetColor(myColor->mColor); + + nsAutoString text; + switch (myList->mListStyleType) { + case NS_STYLE_LIST_STYLE_NONE: + break; + + default: + case NS_STYLE_LIST_STYLE_BASIC: + case NS_STYLE_LIST_STYLE_DISC: + aRenderingContext.FillEllipse(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_CIRCLE: + aRenderingContext.DrawEllipse(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_SQUARE: + aRenderingContext.FillRect(mPadding.left, mPadding.top, + mRect.width - (mPadding.left + mPadding.right), + mRect.height - (mPadding.top + mPadding.bottom)); + break; + + case NS_STYLE_LIST_STYLE_DECIMAL: + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + fm = aCX.GetMetricsFor(myFont->mFont); + GetListItemText(aCX, *myList, text); + aRenderingContext.SetFont(fm); + aRenderingContext.GetWidth(text, width); + aRenderingContext.DrawString(text, mPadding.left, mPadding.top, width); + NS_RELEASE(fm); + break; + } + } + return NS_OK; +} + +PRInt32 +nsBulletFrame::SetListItemOrdinal(PRInt32 aNextOrdinal) +{ + // Assume that the ordinal comes from the block reflow state + mOrdinal = aNextOrdinal; + + // Try to get value directly from the list-item, if it specifies a + // value attribute. Note: we do this with our parent's content + // because our parent is the list-item. + nsHTMLValue value; + nsIContent* parentContent; + mContentParent->GetContent(parentContent); + nsIHTMLContent* hc; + if (NS_OK == parentContent->QueryInterface(kIHTMLContentIID, (void**) &hc)) { + if (NS_CONTENT_ATTR_HAS_VALUE == + hc->GetAttribute(nsHTMLAtoms::value, value)) { + if (eHTMLUnit_Integer == value.GetUnit()) { + // Use ordinal specified by the value attribute + mOrdinal = value.GetIntValue(); + if (mOrdinal <= 0) { + mOrdinal = 1; + } + } + } + NS_RELEASE(hc); + } + NS_RELEASE(parentContent); + + return mOrdinal + 1; +} + +static const char* gLowerRomanCharsA = "ixcm"; +static const char* gUpperRomanCharsA = "IXCM"; +static const char* gLowerRomanCharsB = "vld?"; +static const char* gUpperRomanCharsB = "VLD?"; +static const char* gLowerAlphaChars = "abcdefghijklmnopqrstuvwxyz"; +static const char* gUpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +// XXX change roman/alpha to use unsigned math so that maxint and +// maxnegint will work +void +nsBulletFrame::GetListItemText(nsIPresContext& aCX, + const nsStyleList& aListStyle, + nsString& result) +{ + PRInt32 ordinal = mOrdinal; + char cbuf[40]; + switch (aListStyle.mListStyleType) { + case NS_STYLE_LIST_STYLE_DECIMAL: + PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal); + result.Append(cbuf); + break; + + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + { + if (ordinal <= 0) { + ordinal = 1; + } + nsAutoString addOn, decStr; + decStr.Append(ordinal, 10); + PRIntn len = decStr.Length(); + const PRUnichar* dp = decStr.GetUnicode(); + const PRUnichar* end = dp + len; + PRIntn romanPos = len; + PRIntn n; + + const char* achars; + const char* bchars; + if (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ROMAN) { + achars = gLowerRomanCharsA; + bchars = gLowerRomanCharsB; + } + else { + achars = gUpperRomanCharsA; + bchars = gUpperRomanCharsB; + } + for (; dp < end; dp++) { + romanPos--; + addOn.SetLength(0); + switch(*dp) { + case '3': addOn.Append(achars[romanPos]); + case '2': addOn.Append(achars[romanPos]); + case '1': addOn.Append(achars[romanPos]); + break; + case '4': + addOn.Append(achars[romanPos]); + // FALLTHROUGH + case '5': case '6': + case '7': case '8': + addOn.Append(bchars[romanPos]); + for(n=0;n<(*dp-'5');n++) { + addOn.Append(achars[romanPos]); + } + break; + case '9': + addOn.Append(achars[romanPos]); + addOn.Append(achars[romanPos+1]); + break; + default: + break; + } + result.Append(addOn); + } + } + break; + + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + { + PRInt32 anOffset = -1; + PRInt32 aBase = 26; + PRInt32 ndex=0; + PRInt32 root=1; + PRInt32 next=aBase; + PRInt32 expn=1; + const char* chars = + (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ALPHA) + ? gLowerAlphaChars : gUpperAlphaChars; + + // must be positive here... + if (ordinal <= 0) { + ordinal = 1; + } + ordinal--; // a == 0 + + // scale up in baseN; exceed current value. + while (next<=ordinal) { + root=next; + next*=aBase; + expn++; + } + while (0!=(expn--)) { + ndex = ((root<=ordinal) && (0!=root)) ? (ordinal/root): 0; + ordinal %= root; + if (root>1) + result.Append(chars[ndex+anOffset]); + else + result.Append(chars[ndex]); + root /= aBase; + } + } + break; + } + result.Append("."); +} + +#define MIN_BULLET_SIZE 5 // from laytext.c + +static nsresult +UpdateBulletCB(nsIPresContext& aPresContext, nsIFrame* aFrame, PRIntn aStatus) +{ + nsresult rv = NS_OK; + if (NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE & aStatus) { + // Now that the size is available, trigger a reflow of the bullet + // frame. + nsIPresShell* shell; + shell = aPresContext.GetShell(); + if (nsnull != shell) { + nsIReflowCommand* cmd; + rv = NS_NewHTMLReflowCommand(&cmd, aFrame, + nsIReflowCommand::ContentChanged); + if (NS_OK == rv) { + shell->EnterReflowLock(); + shell->AppendReflowCommand(cmd); + NS_RELEASE(cmd); + shell->ExitReflowLock(); + } + NS_RELEASE(shell); + } + } + return rv; +} + +void +nsBulletFrame::GetDesiredSize(nsIPresContext* aCX, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aMetrics) +{ + const nsStyleList* myList = + (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List); + nscoord ascent; + + if (myList->mListStyleImage.Length() > 0) { + mImageLoader.SetURL(myList->mListStyleImage); + mImageLoader.GetDesiredSize(aCX, aReflowState, this, UpdateBulletCB, + aMetrics); + if (!mImageLoader.GetLoadImageFailed()) { + nsHTMLContainerFrame::CreateViewForFrame(*aCX, this, mStyleContext, + PR_FALSE); + aMetrics.ascent = aMetrics.height; + aMetrics.descent = 0; + return; + } + } + + const nsStyleFont* myFont = + (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + nsIFontMetrics* fm = aCX->GetMetricsFor(myFont->mFont); + nscoord bulletSize; + float p2t; + float t2p; + + nsAutoString text; + switch (myList->mListStyleType) { + case NS_STYLE_LIST_STYLE_NONE: + aMetrics.width = 0; + aMetrics.height = 0; + aMetrics.ascent = 0; + aMetrics.descent = 0; + break; + + default: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_BASIC: + case NS_STYLE_LIST_STYLE_SQUARE: + t2p = aCX->GetTwipsToPixels(); + fm->GetMaxAscent(ascent); + bulletSize = NSTwipsToIntPixels( + (nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p); + if (bulletSize < 1) { + bulletSize = MIN_BULLET_SIZE; + } + p2t = aCX->GetPixelsToTwips(); + bulletSize = NSIntPixelsToTwips(bulletSize, p2t); + mPadding.bottom = ascent / 8; + aMetrics.width = mPadding.right + bulletSize; + aMetrics.height = mPadding.bottom + bulletSize; + aMetrics.ascent = mPadding.bottom + bulletSize; + aMetrics.descent = 0; + break; + + case NS_STYLE_LIST_STYLE_DECIMAL: + case NS_STYLE_LIST_STYLE_LOWER_ROMAN: + case NS_STYLE_LIST_STYLE_UPPER_ROMAN: + case NS_STYLE_LIST_STYLE_LOWER_ALPHA: + case NS_STYLE_LIST_STYLE_UPPER_ALPHA: + GetListItemText(*aCX, *myList, text); + fm->GetHeight(aMetrics.height); + aReflowState.rendContext->SetFont(fm); + aReflowState.rendContext->GetWidth(text, aMetrics.width); + aMetrics.width += mPadding.right; + fm->GetMaxAscent(aMetrics.ascent); + fm->GetMaxDescent(aMetrics.descent); + break; + } + NS_RELEASE(fm); +} + +NS_IMETHODIMP +nsBulletFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aMetrics, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + // Get the base size + GetDesiredSize(&aPresContext, aReflowState, aMetrics); + + // Add in the border and padding; split the top/bottom between the + // ascent and descent to make things look nice + const nsStyleSpacing* space =(const nsStyleSpacing*) + mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin borderPadding; + nsHTMLReflowState::ComputeBorderPaddingFor(this, + aReflowState.parentReflowState, + borderPadding); + aMetrics.width += borderPadding.left + borderPadding.right; + aMetrics.height += borderPadding.top + borderPadding.bottom; + aMetrics.ascent += borderPadding.top; + aMetrics.descent += borderPadding.bottom; + + if (nsnull != aMetrics.maxElementSize) { + aMetrics.maxElementSize->width = aMetrics.width; + aMetrics.maxElementSize->height = aMetrics.height; + } + aStatus = NS_FRAME_COMPLETE; + return NS_OK; +} diff --git a/mozilla/layout/html/base/src/nsBulletFrame.h b/mozilla/layout/html/base/src/nsBulletFrame.h new file mode 100644 index 00000000000..37b8930918e --- /dev/null +++ b/mozilla/layout/html/base/src/nsBulletFrame.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsBulletFrame_h___ +#define nsBulletFrame_h___ + +#include "nsHTMLImage.h" +#include "nsIStyleContext.h" + +/** + * A simple class that manages the layout and rendering of html bullets. + * This class also supports the CSS list-style properties. + */ +class nsBulletFrame : public nsFrame { +public: + nsBulletFrame(nsIContent* aContent, nsIFrame* aParentFrame); + virtual ~nsBulletFrame(); + + // nsIFrame + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext); + NS_IMETHOD Paint(nsIPresContext &aCX, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect); + NS_IMETHOD GetFrameName(nsString& aResult) const; + NS_IMETHOD List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const; + + // nsIHTMLReflow + NS_IMETHOD Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aMetrics, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + // nsBulletFrame + PRInt32 SetListItemOrdinal(PRInt32 aNextOrdinal); + +protected: + void GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aMetrics); + + void GetListItemText(nsIPresContext& aCX, + const nsStyleList& aStyleList, + nsString& aResult); + + PRInt32 mOrdinal; + nsMargin mPadding; + nsHTMLImageLoader mImageLoader; +}; + +#endif /* nsBulletFrame_h___ */ diff --git a/mozilla/layout/html/base/src/nsLineBox.cpp b/mozilla/layout/html/base/src/nsLineBox.cpp new file mode 100644 index 00000000000..617a9585b2f --- /dev/null +++ b/mozilla/layout/html/base/src/nsLineBox.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsLineBox.h" +#include "nsISpaceManager.h" +#include "prprf.h" + +nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags) +{ + mFirstChild = aFrame; + mChildCount = aCount; + mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags; + mFloaters = nsnull; + mNext = nsnull; + mBounds.SetRect(0,0,0,0); + mCombinedArea.SetRect(0,0,0,0); + mCarriedOutTopMargin = 0; + mCarriedOutBottomMargin = 0; + mBreakType = NS_STYLE_CLEAR_NONE; +} + +nsLineBox::~nsLineBox() +{ + if (nsnull != mFloaters) { + delete mFloaters; + } +} + +static void +ListFloaters(FILE* out, PRInt32 aIndent, nsVoidArray* aFloaters) +{ + PRInt32 j, i, n = aFloaters->Count(); + for (i = 0; i < n; i++) { + for (j = aIndent; --j >= 0; ) fputs(" ", out); + nsPlaceholderFrame* ph = (nsPlaceholderFrame*) aFloaters->ElementAt(i); + if (nsnull != ph) { + fprintf(out, "placeholder@%p\n", ph); + nsIFrame* frame = ph->GetAnchoredItem(); + if (nsnull != frame) { + frame->List(out, aIndent + 1, nsnull); + } + } + } +} + +char* +nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const +{ + PR_snprintf(aBuf, aBufSize, "%s,%s", + (mState & LINE_IS_DIRTY) ? "dirty" : "clean", + (mState & LINE_IS_BLOCK) ? "block" : "inline"); + return aBuf; +} + +void +nsLineBox::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter, + PRBool aOutputMe) const +{ + PRInt32 i; + + if (aOutputMe) { + for (i = aIndent; --i >= 0; ) fputs(" ", out); + char cbuf[100]; + fprintf(out, "line %p: count=%d state=%s ", + this, ChildCount(), StateToString(cbuf, sizeof(cbuf))); + if (0 != mCarriedOutTopMargin) { + fprintf(out, "tm=%d ", mCarriedOutTopMargin); + } + if (0 != mCarriedOutBottomMargin) { + fprintf(out, "bm=%d ", mCarriedOutBottomMargin); + } + out << mBounds; + fprintf(out, " ca="); + out << mCombinedArea; + fprintf(out, " <\n"); + } + + nsIFrame* frame = mFirstChild; + PRInt32 n = ChildCount(); + while (--n >= 0) { + frame->List(out, aIndent + 1, aFilter); + frame->GetNextSibling(frame); + } + + if (aOutputMe) { + for (i = aIndent; --i >= 0; ) fputs(" ", out); + if (nsnull != mFloaters) { + fputs("> floaters <\n", out); + ListFloaters(out, aIndent + 1, mFloaters); + for (i = aIndent; --i >= 0; ) fputs(" ", out); + } + fputs(">\n", out); + } +} + +nsIFrame* +nsLineBox::LastChild() const +{ + nsIFrame* frame = mFirstChild; + PRInt32 n = ChildCount() - 1; + while (--n >= 0) { + frame->GetNextSibling(frame); + } + return frame; +} + +PRBool +nsLineBox::IsLastChild(nsIFrame* aFrame) const +{ + nsIFrame* lastFrame = LastChild(); + return aFrame == lastFrame; +} + +PRBool +nsLineBox::Contains(nsIFrame* aFrame) const +{ + PRInt32 n = ChildCount(); + nsIFrame* frame = mFirstChild; + while (--n >= 0) { + if (frame == aFrame) { + return PR_TRUE; + } + frame->GetNextSibling(frame); + } + return PR_FALSE; +} + +#if 0 +static PRInt32 +LengthOf(nsIFrame* aFrame) +{ + PRInt32 result = 0; + while (nsnull != aFrame) { + result++; + aFrame->GetNextSibling(aFrame); + } + return result; +} + +void +nsLineBox::Verify() +{ + nsIFrame* lastFrame = LastChild(); + if (nsnull != lastFrame) { + nsIFrame* nextInFlow; + lastFrame->GetNextInFlow(nextInFlow); + if (nsnull != mNext) { + nsIFrame* nextSibling; + lastFrame->GetNextSibling(nextSibling); + NS_ASSERTION(mNext->mFirstChild == nextSibling, "bad line list"); + } + } + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(len >= ChildCount(), "bad mChildCount"); +} + +static void +VerifyLines(nsLineBox* aLine) +{ + while (nsnull != aLine) { + aLine->Verify(); + aLine = aLine->mNext; + } +} + +static void +VerifyChildCount(nsLineBox* aLines, PRBool aEmptyOK = PR_FALSE) +{ + if (nsnull != aLines) { + PRInt32 childCount = LengthOf(aLines->mFirstChild); + PRInt32 sum = 0; + nsLineBox* line = aLines; + while (nsnull != line) { + if (!aEmptyOK) { + NS_ASSERTION(0 != line->ChildCount(), "empty line left in line list"); + } + sum += line->ChildCount(); + line = line->mNext; + } + if (sum != childCount) { + printf("Bad sibling list/line mChildCount's\n"); + nsLineBox* line = aLines; + while (nsnull != line) { + line->List(stdout, 1); + if (nsnull != line->mNext) { + nsIFrame* lastFrame = line->LastChild(); + if (nsnull != lastFrame) { + nsIFrame* nextSibling; + lastFrame->GetNextSibling(nextSibling); + if (line->mNext->mFirstChild != nextSibling) { + printf(" [list broken: nextSibling=%p mNext->mFirstChild=%p]\n", + nextSibling, line->mNext->mFirstChild); + } + } + } + line = line->mNext; + } + NS_ASSERTION(sum == childCount, "bad sibling list/line mChildCount's"); + } + } +} +#endif + +void +nsLineBox::DeleteLineList(nsIPresContext& aPresContext, nsLineBox* aLine) +{ + if (nsnull != aLine) { + // Delete our child frames before doing anything else. In particular + // we do all of this before our base class releases it's hold on the + // view. + for (nsIFrame* child = aLine->mFirstChild; child; ) { + nsIFrame* nextChild; + child->GetNextSibling(nextChild); + child->DeleteFrame(aPresContext); + child = nextChild; + } + + while (nsnull != aLine) { + nsLineBox* next = aLine->mNext; + delete aLine; + aLine = next; + } + } +} + +nsLineBox* +nsLineBox::LastLine(nsLineBox* aLine) +{ + if (nsnull != aLine) { + while (nsnull != aLine->mNext) { + aLine = aLine->mNext; + } + } + return aLine; +} + +nsLineBox* +nsLineBox::FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame) +{ + while (nsnull != aLine) { + if (aLine->Contains(aFrame)) { + return aLine; + } + aLine = aLine->mNext; + } + return nsnull; +} + +void +nsLineBox::UnplaceFloaters(nsISpaceManager* aSpaceManager) +{ + if (nsnull != mFloaters) { + PRInt32 i, n = mFloaters->Count(); + for (i = 0; i < n; i++) { + nsPlaceholderFrame* pf = (nsPlaceholderFrame*) mFloaters->ElementAt(i); + nsIFrame* floater = pf->GetAnchoredItem(); + aSpaceManager->RemoveRegion(floater); + } + } +} diff --git a/mozilla/layout/html/base/src/nsLineBox.h b/mozilla/layout/html/base/src/nsLineBox.h new file mode 100644 index 00000000000..a39dbc065dc --- /dev/null +++ b/mozilla/layout/html/base/src/nsLineBox.h @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsLineBox_h___ +#define nsLineBox_h___ + +#include "nsVoidArray.h" +#include "nsPlaceholderFrame.h" + +// bits in nsLineBox.mFlags +#define LINE_IS_DIRTY 0x1 +#define LINE_IS_BLOCK 0x2 +#define LINE_NEED_DID_REFLOW 0x8 +#define LINE_TOP_MARGIN_IS_AUTO 0x10 +#define LINE_BOTTOM_MARGIN_IS_AUTO 0x20 +#define LINE_OUTSIDE_CHILDREN 0x40 + +class nsISpaceManager; +class nsLineBox; + +/** + * A list of block lines + */ +class nsLineBoxList { +public: + +protected: + nsLineBox* mLines; +}; + +/** + * The nsLineBox class represents a horizontal line of frames. It contains + * enough state to support incremental reflow of the frames, event handling + * for the frames, and rendering of the frames. + */ +class nsLineBox { +public: + nscoord GetCarriedOutTopMargin() const { + return mCarriedOutTopMargin; + } + + nscoord GetCarriedOutBottomMargin() const { + return mCarriedOutBottomMargin; + } + + nscoord GetCarriedOutMarginFlags() const { + return 0;/* XXX write me */ + } + + nscoord GetHeight() const { return mBounds.height; } + + //---------------------------------------------------------------------- + // XXX old junk + nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags); + + ~nsLineBox(); + + static void DeleteLineList(nsIPresContext& aPresContext, nsLineBox* aLine); + + static nsLineBox* LastLine(nsLineBox* aLine); + + static nsLineBox* FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame); + + void List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter, + PRBool aOutputMe) const; + + PRInt32 ChildCount() const { + return PRInt32(mChildCount); + } + + nsIFrame* LastChild() const; + + PRBool IsLastChild(nsIFrame* aFrame) const; + + PRBool IsBlock() const { + return 0 != (LINE_IS_BLOCK & mState); + } + + void SetIsBlock() { + mState |= LINE_IS_BLOCK; + } + + void ClearIsBlock() { + mState &= ~LINE_IS_BLOCK; + } + + void SetIsBlock(PRBool aValue) { + if (aValue) { + SetIsBlock(); + } + else { + ClearIsBlock(); + } + } + + void MarkDirty() { + mState |= LINE_IS_DIRTY; + } + + void ClearDirty() { + mState &= ~LINE_IS_DIRTY; + } + + void SetNeedDidReflow() { + mState |= LINE_NEED_DID_REFLOW; + } + + void ClearNeedDidReflow() { + mState &= ~LINE_NEED_DID_REFLOW; + } + + PRBool NeedsDidReflow() { + return 0 != (LINE_NEED_DID_REFLOW & mState); + } + + PRBool IsDirty() const { + return 0 != (LINE_IS_DIRTY & mState); + } + + void SetOutsideChildren() { + mState |= LINE_OUTSIDE_CHILDREN; + } + + void ClearOutsideChildren() { + mState &= ~LINE_OUTSIDE_CHILDREN; + } + + PRBool OutsideChildren() const { + return 0 != (LINE_OUTSIDE_CHILDREN & mState); + } + + PRUint16 GetState() const { return mState; } + + char* StateToString(char* aBuf, PRInt32 aBufSize) const; + + PRBool Contains(nsIFrame* aFrame) const; + + // XXX ick + void SetMarginFlags(PRUintn aFlags) { + mState &= ~(LINE_TOP_MARGIN_IS_AUTO|LINE_BOTTOM_MARGIN_IS_AUTO); + if (NS_CARRIED_TOP_MARGIN_IS_AUTO & aFlags) { + mState |= LINE_TOP_MARGIN_IS_AUTO; + } + if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aFlags) { + mState |= LINE_BOTTOM_MARGIN_IS_AUTO; + } + } + + PRUintn GetMarginFlags() { + return ((LINE_TOP_MARGIN_IS_AUTO & mState) + ? NS_CARRIED_TOP_MARGIN_IS_AUTO : 0) | + ((LINE_BOTTOM_MARGIN_IS_AUTO & mState) ? + NS_CARRIED_BOTTOM_MARGIN_IS_AUTO : 0); + } + + void UnplaceFloaters(nsISpaceManager* aSpaceManager); + +#ifdef NS_DEBUG + void Verify(); +#endif + +//XXX protected: + nsIFrame* mFirstChild; + PRUint16 mChildCount; + PRUint16 mState; + PRUint8 mBreakType; + nsRect mBounds; + nsRect mCombinedArea; + nscoord mCarriedOutTopMargin;/* XXX switch to 16 bits */ + nscoord mCarriedOutBottomMargin;/* XXX switch to 16 bits */ + nsVoidArray* mFloaters; + nsLineBox* mNext; + +protected: +}; + +#endif /* nsLineBox_h___ */