gecko-dev/layout/generic/nsBlockBandData.cpp

361 lines
11 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 "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 "nsBlockBandData.h"
#include "nsIFrame.h"
#include "nsIFrameReflow.h"
#include "nsIStyleContext.h"
#include "nsVoidArray.h"
nsBlockBandData::nsBlockBandData()
: mSpaceManager(nsnull),
mSpaceManagerX(0),
mSpaceManagerY(0),
mSpace(0, 0)
{
mSize = NS_BLOCK_BAND_DATA_TRAPS;
mTrapezoids = mData;
}
nsBlockBandData::~nsBlockBandData()
{
NS_IF_RELEASE(mSpaceManager);
if (mTrapezoids != mData) {
delete [] mTrapezoids;
}
}
nsresult
nsBlockBandData::Init(nsISpaceManager* aSpaceManager,
const nsSize& aSpace)
{
NS_PRECONDITION(nsnull != aSpaceManager, "null pointer");
if (nsnull == aSpaceManager) {
return NS_ERROR_NULL_POINTER;
}
NS_IF_RELEASE(mSpaceManager);
mSpaceManager = aSpaceManager;
NS_ADDREF(aSpaceManager);
aSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
mSpace = aSpace;
mFloaters = 0;
return NS_OK;
}
// Get the available reflow space for the current y coordinate. The
// available space is relative to our coordinate system (0,0) is our
// upper left corner.
nsresult
nsBlockBandData::GetAvailableSpace(nscoord aY, nsRect& aResult)
{
// Get the raw band data for the given Y coordinate
nsresult rv = mSpaceManager->GetBandData(aY, mSpace, *this);
while (NS_FAILED(rv)) {
// We need more space for our bands
if (mTrapezoids != mData) {
delete [] mTrapezoids;
}
PRInt32 newSize = mSize * 2;
mTrapezoids = new nsBandTrapezoid[newSize];
if (!mTrapezoids) {
return NS_ERROR_OUT_OF_MEMORY;
}
mSize = newSize;
rv = mSpaceManager->GetBandData(aY, mSpace, *this);
}
// Compute the bounding rect of the available space, i.e. space
// between any left and right floaters.
ComputeAvailSpaceRect();
aResult = mAvailSpace;
return NS_OK;
}
/**
* Computes the bounding rect of the available space, i.e. space
* between any left and right floaters. Uses the current trapezoid
* data, see nsISpaceManager::GetBandData(). Also updates member
* data "availSpace".
*/
void
nsBlockBandData::ComputeAvailSpaceRect()
{
if (0 == mCount) {
mAvailSpace.x = 0;
mAvailSpace.y = 0;
mAvailSpace.width = 0;
mAvailSpace.height = 0;
return;
}
nsBandTrapezoid* trapezoid = mTrapezoids;
nsBandTrapezoid* rightTrapezoid = nsnull;
PRInt32 floaters = 0;
if (mCount > 1) {
// If there's more than one trapezoid that means there are floaters
PRInt32 i;
// Examine each trapezoid in the band, counting up the number of
// left and right floaters. Use the right-most floater to
// determine where the right edge of the available space is.
for (i = 0; i < mCount; i++) {
trapezoid = &mTrapezoids[i];
if (trapezoid->mState != nsBandTrapezoid::Available) {
const nsStyleDisplay* display;
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->mState) {
PRInt32 j, numFrames = trapezoid->mFrames->Count();
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
for (j = 0; j < numFrames; j++) {
nsIFrame* f = (nsIFrame*) trapezoid->mFrames->ElementAt(j);
f->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)display);
if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
floaters++;
}
else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
floaters++;
if ((nsnull == rightTrapezoid) && (i > 0)) {
rightTrapezoid = &mTrapezoids[i - 1];
}
}
}
} else {
trapezoid->mFrame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)display);
if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
floaters++;
}
else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
floaters++;
if ((nsnull == rightTrapezoid) && (i > 0)) {
rightTrapezoid = &mTrapezoids[i - 1];
}
}
}
}
}
}
else if (mTrapezoids[0].mState != nsBandTrapezoid::Available) {
// We have a floater using up all the available space
floaters = 1;
}
mFloaters = floaters;
if (nsnull != rightTrapezoid) {
trapezoid = rightTrapezoid;
}
trapezoid->GetRect(mAvailSpace);
// When there is no available space, we still need a proper X
// coordinate to place objects that end up here anyway.
if (nsBandTrapezoid::Available != trapezoid->mState) {
const nsStyleDisplay* display;
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->mState) {
// It's not clear what coordinate to use when there is no
// available space and the space is multiply occupied...So: If
// any of the floaters that are a part of the trapezoid are left
// floaters then we move over to the right edge of the
// unavaliable space.
PRInt32 j, numFrames = trapezoid->mFrames->Count();
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
for (j = 0; j < numFrames; j++) {
nsIFrame* f = (nsIFrame*) trapezoid->mFrames->ElementAt(j);
f->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)display);
if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
mAvailSpace.x = mAvailSpace.XMost();
break;
}
}
}
else {
trapezoid->mFrame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)display);
if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
mAvailSpace.x = mAvailSpace.XMost();
}
}
mAvailSpace.width = 0;
}
// Fixup width
if (NS_UNCONSTRAINEDSIZE == mSpace.width) {
mAvailSpace.width = NS_UNCONSTRAINEDSIZE;
}
}
/**
* See if the given frame should be cleared
*/
PRBool
nsBlockBandData::ShouldClearFrame(nsIFrame* aFrame, PRUint8 aBreakType)
{
PRBool result = PR_FALSE;
const nsStyleDisplay* display;
nsresult rv = aFrame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)display);
if (NS_SUCCEEDED(rv) && (nsnull != display)) {
if (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType) {
result = PR_TRUE;
}
else if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
if (NS_STYLE_CLEAR_LEFT == aBreakType) {
result = PR_TRUE;
}
}
else if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
if (NS_STYLE_CLEAR_RIGHT == aBreakType) {
result = PR_TRUE;
}
}
}
return result;
}
// XXX doesn't work because of margins around floaters and because of
// % margins we can't really re-compute them here (nor would we want
// too...)
#if 0
/**
* Get the frames YMost, in the space managers "root" coordinate
* system. Because the floating frame may have been placed in a
* parent/child frame, we map the coordinates into the space-managers
* untranslated coordinate system.
*/
nscoord
nsBlockBandData::GetFrameYMost(nsIFrame* aFrame)
{
nsIFrame* spaceFrame;
mSpaceManager->GetFrame(spaceFrame);
nsRect r;
nsPoint p;
aFrame->GetRect(r);
nscoord y = r.y;
nsIFrame* parent;
aFrame->GetParent(&parent);
PRBool done = PR_FALSE;
while (!done && (parent != spaceFrame)) {
// If parent has a prev-in-flow, check there for equality first
// before looking upward.
nsIFrame* parentPrevInFlow;
parent->GetPrevInFlow(&parentPrevInFlow);
while (nsnull != parentPrevInFlow) {
if (parentPrevInFlow == spaceFrame) {
done = PR_TRUE;
break;
}
parentPrevInFlow->GetPrevInFlow(&parentPrevInFlow);
}
if (!done) {
parent->GetOrigin(p);
y += p.y;
parent->GetParent(&parent);
}
}
return y + r.height;
}
#endif
// XXX optimization? use mFloaters to avoid doing anything
nscoord
nsBlockBandData::ClearFloaters(nscoord aY, PRUint8 aBreakType)
{
for (;;) {
// Update band information based on target Y before clearing.
mSpaceManager->GetBandData(aY, mSpace, *this);
ComputeAvailSpaceRect();
// Compute aYS as aY in space-manager "root" coordinates.
nscoord aYS = aY + mSpaceManagerY;
// Find the largest frame YMost for the appropriate floaters in
// this band.
nscoord yMost = aYS;
PRInt32 i;
for (i = 0; i < mCount; i++) {
nsBandTrapezoid* trapezoid = &mTrapezoids[i];
if (nsBandTrapezoid::Available != trapezoid->mState) {
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->mState) {
PRInt32 fn, numFrames = trapezoid->mFrames->Count();
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
for (fn = 0; fn < numFrames; fn++) {
nsIFrame* frame = (nsIFrame*) trapezoid->mFrames->ElementAt(fn);
if (ShouldClearFrame(frame, aBreakType)) {
nscoord ym = trapezoid->mBottomY + mSpaceManagerY;
if (ym > yMost) yMost = ym;
}
}
}
else if (ShouldClearFrame(trapezoid->mFrame, aBreakType)) {
nscoord ym = trapezoid->mBottomY + mSpaceManagerY;
if (ym > yMost) yMost = ym;
}
}
}
// If yMost is unchanged (aYS) then there were no appropriate
// floaters in the band. Time to stop clearing.
if (yMost == aYS) {
break;
}
aY = aY + (yMost - aYS);
}
return aY;
}
void
nsBlockBandData::GetMaxElementSize(nscoord* aWidthResult,
nscoord* aHeightResult) const
{
nsRect r;
nscoord maxWidth = 0;
nscoord maxHeight = 0;
for (PRInt32 i = 0; i < mCount; i++) {
const nsBandTrapezoid* trap = &mTrapezoids[i];
if (trap->mState != nsBandTrapezoid::Available) {
// Get the width of the impacted area and update the maxWidth
trap->GetRect(r);
if (r.width > maxWidth) maxWidth = r.width;
// Get the total height of the frame to compute the maxHeight,
// not just the height that is part of this band.
// XXX vertical margins!
if (nsBandTrapezoid::OccupiedMultiple == trap->mState) {
PRInt32 j, numFrames = trap->mFrames->Count();
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
for (j = 0; j < numFrames; j++) {
nsIFrame* f = (nsIFrame*) trap->mFrames->ElementAt(j);
f->GetRect(r);
if (r.height > maxHeight) maxHeight = r.height;
}
} else {
trap->mFrame->GetRect(r);
if (r.height > maxHeight) maxHeight = r.height;
}
}
}
*aWidthResult = maxWidth;
*aHeightResult = maxHeight;
}