new
git-svn-id: svn://10.0.0.236/trunk@15521 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
407
mozilla/layout/generic/nsBlockReflowContext.cpp
Normal file
407
mozilla/layout/generic/nsBlockReflowContext.cpp
Normal file
@@ -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 <b>inside</b> 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 <b>inside</b> 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;
|
||||
}
|
||||
129
mozilla/layout/generic/nsBlockReflowContext.h
Normal file
129
mozilla/layout/generic/nsBlockReflowContext.h
Normal file
@@ -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___ */
|
||||
433
mozilla/layout/generic/nsBulletFrame.cpp
Normal file
433
mozilla/layout/generic/nsBulletFrame.cpp
Normal file
@@ -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;
|
||||
}
|
||||
65
mozilla/layout/generic/nsBulletFrame.h
Normal file
65
mozilla/layout/generic/nsBulletFrame.h
Normal file
@@ -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___ */
|
||||
275
mozilla/layout/generic/nsLineBox.cpp
Normal file
275
mozilla/layout/generic/nsLineBox.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
192
mozilla/layout/generic/nsLineBox.h
Normal file
192
mozilla/layout/generic/nsLineBox.h
Normal file
@@ -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___ */
|
||||
407
mozilla/layout/html/base/src/nsBlockReflowContext.cpp
Normal file
407
mozilla/layout/html/base/src/nsBlockReflowContext.cpp
Normal file
@@ -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 <b>inside</b> 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 <b>inside</b> 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;
|
||||
}
|
||||
129
mozilla/layout/html/base/src/nsBlockReflowContext.h
Normal file
129
mozilla/layout/html/base/src/nsBlockReflowContext.h
Normal file
@@ -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___ */
|
||||
433
mozilla/layout/html/base/src/nsBulletFrame.cpp
Normal file
433
mozilla/layout/html/base/src/nsBulletFrame.cpp
Normal file
@@ -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;
|
||||
}
|
||||
65
mozilla/layout/html/base/src/nsBulletFrame.h
Normal file
65
mozilla/layout/html/base/src/nsBulletFrame.h
Normal file
@@ -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___ */
|
||||
275
mozilla/layout/html/base/src/nsLineBox.cpp
Normal file
275
mozilla/layout/html/base/src/nsLineBox.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
192
mozilla/layout/html/base/src/nsLineBox.h
Normal file
192
mozilla/layout/html/base/src/nsLineBox.h
Normal file
@@ -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___ */
|
||||
Reference in New Issue
Block a user