gecko-dev/layout/generic/nsInlineFrame.cpp

298 lines
8.0 KiB
C++

/* -*- 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 "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsBlockFrame.h"
#include "nsLineBox.h"
#include "nsLineLayout.h"
#include "nsHTMLIIDs.h"
#define nsInlineFrameSuper nsBaseIBFrame
class nsInlineFrame : public nsInlineFrameSuper
{
public:
friend nsresult NS_NewInlineFrame(nsIFrame*& aNewFrame);
// nsIFrame overrides
NS_IMETHOD CreateContinuingFrame(nsIPresContext& aCX,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame*& aContinuingFrame);
NS_IMETHOD GetFrameName(nsString& aResult) const;
// nsIHTMLReflow overrides
NS_IMETHOD FindTextRuns(nsLineLayout& aLineLayout);
NS_IMETHOD AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace);
NS_IMETHOD TrimTrailingWhiteSpace(nsIPresContext& aPresContext,
nsIRenderingContext& aRC,
nscoord& aDeltaWidth);
protected:
nsInlineFrame();
~nsInlineFrame();
virtual PRIntn GetSkipSides() const;
struct AdjustData {
nsIFrame* frame;
PRBool splittable;
nsRect bounds;
};
};
//----------------------------------------------------------------------
nsresult
NS_NewInlineFrame(nsIFrame*& aNewFrame)
{
nsInlineFrame* it = new nsInlineFrame;
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
aNewFrame = it;
return NS_OK;
}
nsInlineFrame::nsInlineFrame()
{
mFlags = BLOCK_IS_INLINE;
}
nsInlineFrame::~nsInlineFrame()
{
}
NS_IMETHODIMP
nsInlineFrame::FindTextRuns(nsLineLayout& aLineLayout)
{
nsresult rv = NS_OK;
// Gather up children from the overflow lists
DrainOverflowLines();
nsLineBox* line = mLines;
while (nsnull != line) {
if (!line->IsBlock()) {
// A frame that doesn't implement nsIHTMLReflow isn't text
// therefore it will end an open text run.
nsIFrame* frame = line->mFirstChild;
PRInt32 n = line->ChildCount();
while (--n >= 0) {
nsIHTMLReflow* hr;
if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&hr)) {
rv = hr->FindTextRuns(aLineLayout);
if (NS_OK != rv) {
return rv;
}
}
else {
aLineLayout.EndTextRun();
}
frame->GetNextSibling(frame);
}
}
else {
// Block lines terminate the text-runs
aLineLayout.EndTextRun();
}
line = line->mNext;
}
return rv;
}
NS_IMETHODIMP
nsInlineFrame::AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace)
{
// XXX Refactor this
if (0 >= aExtraSpace) {
aUsedSpace = 0;
return NS_OK;
}
const int NUM_AD = 50;
AdjustData adjustMem[NUM_AD];
AdjustData* ad0 = adjustMem;
AdjustData* end = ad0 + NUM_AD;
AdjustData* ad = ad0;
PRInt32 numAD = NUM_AD;
// Gather up raw data for justification
nsIFrame* frame = mLines ? mLines->mFirstChild : nsnull;
PRInt32 fixed = 0;
PRInt32 total = 0;
nscoord fixedWidth = 0;
for (; nsnull != frame; ad++, total++) {
// Grow temporary storage if we have to
if (ad == end) {
AdjustData* newAD = new AdjustData[numAD + numAD];
if (nsnull == newAD) {
if (ad0 != adjustMem) {
delete [] ad0;
}
aUsedSpace = 0;
return NS_OK;
}
nsCRT::memcpy(newAD, ad0, sizeof(AdjustData) * numAD);
ad = newAD + (ad - ad0);
if (ad0 != adjustMem) {
delete [] ad0;
}
ad0 = newAD;
end = ad0 + numAD;
numAD = numAD + numAD;
}
// Record info about the frame
ad->frame = frame;
frame->GetRect(ad->bounds);
nsSplittableType isSplittable = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(isSplittable);
if ((0 == ad->bounds.width) ||
NS_FRAME_IS_NOT_SPLITTABLE(isSplittable)) {
ad->splittable = PR_FALSE;
fixed++;
fixedWidth += ad->bounds.width;
}
else {
ad->splittable = PR_TRUE;
}
// Advance to the next frame
frame->GetNextSibling(frame);
}
nscoord totalUsed = 0;
nscoord variableWidth = mRect.width - fixedWidth;
if (variableWidth > 0) {
// Each variable width frame gets a portion of the available extra
// space that is proportional to the space it takes in the
// line. The extra space is given to the frame by updating its
// position and size. The frame is responsible for adjusting the
// position of its contents on its own (during rendering).
PRInt32 i, splittable = total - fixed;
nscoord extraSpace = aExtraSpace;
nscoord remainingExtra = extraSpace;
nscoord dx = 0;
float lineWidth = float(mRect.width);
ad = ad0;
for (i = 0; i < total; i++, ad++) {
nsIFrame* frame = ad->frame;
nsIHTMLReflow* ihr;
if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
nsRect r;
if (ad->splittable && (ad->bounds.width > 0)) {
float pctOfLine = float(ad->bounds.width) / lineWidth;
nscoord extra = nscoord(pctOfLine * extraSpace);
if (--splittable == 0) {
extra = remainingExtra;
}
if (0 != extra) {
nscoord used;
ihr->AdjustFrameSize(extra, used);
if (used < extra) {
// frame->ListTag(); printf(": extra=%d used=%d\n", extra, used);
}
totalUsed += used;
frame->GetRect(r);
r.x += dx;
frame->SetRect(r);
dx += used;
remainingExtra -= used;
}
else if (0 != dx) {
frame->GetRect(r);
r.x += dx;
frame->SetRect(r);
}
}
else if (0 != dx) {
frame->GetRect(r);
r.x += dx;
frame->SetRect(r);
}
}
}
}
mRect.width += totalUsed;
aUsedSpace = totalUsed;
if (ad0 != adjustMem) {
delete [] ad0;
}
return NS_OK;
}
NS_IMETHODIMP
nsInlineFrame::TrimTrailingWhiteSpace(nsIPresContext& aPresContext,
nsIRenderingContext& aRC,
nscoord& aDeltaWidth)
{
aDeltaWidth = 0;
nsLineBox* lastLine = nsLineBox::LastLine(mLines);
if (nsnull != lastLine) {
nsIFrame* lastFrame = lastLine->LastChild();
if (nsnull != lastFrame) {
nsIHTMLReflow* ihr;
if (NS_OK == lastFrame->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
ihr->TrimTrailingWhiteSpace(aPresContext, aRC, aDeltaWidth);
if (0 != aDeltaWidth) {
mRect.width -= aDeltaWidth;
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsInlineFrame::GetFrameName(nsString& aResult) const
{
return MakeFrameName("Inline", aResult);
}
NS_IMETHODIMP
nsInlineFrame::CreateContinuingFrame(nsIPresContext& aPresContext,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame*& aContinuingFrame)
{
nsInlineFrame* cf = new nsInlineFrame;
if (nsnull == cf) {
return NS_ERROR_OUT_OF_MEMORY;
}
cf->Init(aPresContext, mContent, aParent, aStyleContext);
cf->SetFlags(mFlags);
cf->AppendToFlow(this);
aContinuingFrame = cf;
return NS_OK;
}
PRIntn
nsInlineFrame::GetSkipSides() const
{
PRIntn skip = 0;
if (nsnull != mPrevInFlow) {
skip |= 1 << NS_SIDE_LEFT;
}
if (nsnull != mNextInFlow) {
skip |= 1 << NS_SIDE_RIGHT;
}
return skip;
}