gecko-dev/layout/generic/nsSpaceManager.cpp

1071 lines
32 KiB
C++
Raw Normal View History

1998-04-13 20:24:54 +00:00
/* -*- 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 "nsSpaceManager.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "nsSize.h"
#include <stdlib.h>
#include "nsVoidArray.h"
1998-04-13 20:24:54 +00:00
static NS_DEFINE_IID(kISpaceManagerIID, NS_ISPACEMANAGER_IID);
1998-05-19 03:00:56 +00:00
PR_CALLBACK PLHashNumber
NS_HashNumber(const void* key)
{
return (PLHashNumber)key;
}
PR_CALLBACK PRIntn
NS_RemoveFrameInfoEntries(PLHashEntry* he, PRIntn i, void* arg)
{
1998-05-28 02:37:37 +00:00
nsSpaceManager::FrameInfo* frameInfo = (nsSpaceManager::FrameInfo*)he->value;
1998-05-19 03:00:56 +00:00
NS_ASSERTION(nsnull != frameInfo, "null frameInfo");
delete frameInfo;
1998-05-19 03:00:56 +00:00
return HT_ENUMERATE_REMOVE;
}
1998-05-19 22:55:40 +00:00
/////////////////////////////////////////////////////////////////////////////
// BandList
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandList::BandList()
1998-05-19 22:55:40 +00:00
: BandRect(-1, -1, -1, -1, (nsIFrame*)nsnull)
{
PR_INIT_CLIST(this);
numFrames = 0;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::BandList::Clear()
1998-05-19 22:55:40 +00:00
{
if (!IsEmpty()) {
BandRect* bandRect = Head();
while (bandRect != this) {
BandRect* next = bandRect->Next();
delete bandRect;
bandRect = next;
}
PR_INIT_CLIST(this);
}
}
1998-04-13 20:24:54 +00:00
/////////////////////////////////////////////////////////////////////////////
// nsSpaceManager
1998-05-28 02:37:37 +00:00
nsSpaceManager::nsSpaceManager(nsIFrame* aFrame)
1998-04-13 20:24:54 +00:00
: mFrame(aFrame)
{
NS_INIT_REFCNT();
mX = mY = 0;
1998-05-19 03:00:56 +00:00
mFrameInfoMap = nsnull;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::ClearFrameInfo()
1998-05-19 03:00:56 +00:00
{
if (nsnull != mFrameInfoMap) {
PL_HashTableEnumerateEntries(mFrameInfoMap, NS_RemoveFrameInfoEntries, 0);
PL_HashTableDestroy(mFrameInfoMap);
mFrameInfoMap = nsnull;
}
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::~nsSpaceManager()
1998-05-19 03:00:56 +00:00
{
1998-05-19 22:55:40 +00:00
mBandList.Clear();
1998-05-19 03:00:56 +00:00
ClearFrameInfo();
1998-04-13 20:24:54 +00:00
}
1998-05-28 02:37:37 +00:00
NS_IMPL_ISUPPORTS(nsSpaceManager, kISpaceManagerIID);
1998-04-13 20:24:54 +00:00
1998-05-28 02:37:37 +00:00
nsIFrame* nsSpaceManager::GetFrame() const
1998-04-13 20:24:54 +00:00
{
return mFrame;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::Translate(nscoord aDx, nscoord aDy)
1998-04-13 20:24:54 +00:00
{
mX += aDx;
mY += aDy;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::GetTranslation(nscoord& aX, nscoord& aY) const
1998-04-13 20:24:54 +00:00
{
aX = mX;
aY = mY;
}
1998-05-28 02:37:37 +00:00
nscoord nsSpaceManager::YMost() const
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
nscoord yMost;
1998-05-19 22:55:40 +00:00
if (mBandList.IsEmpty()) {
1998-05-19 03:00:56 +00:00
yMost = 0;
1998-04-13 20:24:54 +00:00
} else {
1998-05-19 22:55:40 +00:00
BandRect* lastRect = mBandList.Tail();
1998-05-19 03:00:56 +00:00
yMost = lastRect->bottom;
1998-04-13 20:24:54 +00:00
}
1998-05-19 03:00:56 +00:00
return yMost;
1998-04-13 20:24:54 +00:00
}
/**
* Internal function that returns the list of available and unavailable space
* within the band
1998-04-13 20:24:54 +00:00
*
* @param aBand the first rect in the band
* @param aY the y-offset in world coordinates
* @param aMaxSize the size to use to constrain the band data
* @param aAvailableBand
*/
1998-05-28 02:37:37 +00:00
PRInt32 nsSpaceManager::GetBandAvailableSpace(const BandRect* aBand,
nscoord aY,
const nsSize& aMaxSize,
nsBandData& aBandData) const
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
nscoord topOfBand = aBand->top;
nscoord localY = aY - mY;
1998-05-19 03:00:56 +00:00
nscoord height = PR_MIN(aBand->bottom - aY, aMaxSize.height);
nsBandTrapezoid* trapezoid = aBandData.trapezoids;
nscoord rightEdge = mX + aMaxSize.width;
1998-04-13 20:24:54 +00:00
// Initialize the band data
aBandData.count = 0;
1998-04-13 20:24:54 +00:00
// Skip any rectangles that are to the left of the local coordinate space
1998-05-19 03:00:56 +00:00
while (aBand->top == topOfBand) {
if (aBand->right > mX) {
1998-04-13 20:24:54 +00:00
break;
}
// Get the next rect in the band
1998-05-19 22:55:40 +00:00
aBand = aBand->Next();
1998-04-13 20:24:54 +00:00
}
// This is used to track the current x-location within the band. This is in
// world coordinates
nscoord left = mX;
// Process the remaining rectangles that are within the clip width
1998-05-19 03:00:56 +00:00
while ((aBand->top == topOfBand) && (aBand->left < rightEdge)) {
// Compare the left edge of the occupied space with the current left
// coordinate
1998-05-19 03:00:56 +00:00
if (aBand->left > left) {
// The rect is to the right of our current left coordinate, so we've
// found some available space
trapezoid->state = nsBandTrapezoid::Available;
trapezoid->frame = nsnull;
// Assign the trapezoid a rectangular shape. The trapezoid must be in the
// local coordinate space, so convert the current left coordinate
1998-05-19 03:00:56 +00:00
*trapezoid = nsRect(left - mX, localY, aBand->left - left, height);
1998-04-13 20:24:54 +00:00
// Move to the next output rect
trapezoid++;
aBandData.count++;
1998-04-13 20:24:54 +00:00
}
// The rect represents unavailable space, so add another trapezoid
if (1 == aBand->numFrames) {
trapezoid->state = nsBandTrapezoid::Occupied;
trapezoid->frame = aBand->frame;
} else {
NS_ASSERTION(aBand->numFrames > 1, "unexpected frame count");
trapezoid->state = nsBandTrapezoid::OccupiedMultiple;
trapezoid->frames = aBand->frames;
}
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
nscoord x = aBand->left;
// The first band can straddle the clip rect
if (x < mX) {
// Clip the left edge
x = mX;
}
// Assign the trapezoid a rectangular shape. The trapezoid must be in the
// local coordinate space, so convert the rects's left coordinate
1998-05-19 03:00:56 +00:00
*trapezoid = nsRect(x - mX, localY, aBand->right - x, height);
// Move to the next output rect
trapezoid++;
aBandData.count++;
// Adjust our current x-location within the band
1998-05-19 03:00:56 +00:00
left = aBand->right;
// Move to the next rect within the band
1998-05-19 22:55:40 +00:00
aBand = aBand->Next();
1998-04-13 20:24:54 +00:00
}
// No more rects left in the band. If we haven't yet reached the right edge,
1998-04-13 20:24:54 +00:00
// then all the remaining space is available
if (left < rightEdge) {
trapezoid->state = nsBandTrapezoid::Available;
trapezoid->frame = nsnull;
// Assign the trapezoid a rectangular shape. The trapezoid must be in the
// local coordinate space, so convert the current left coordinate
*trapezoid = nsRect(left - mX, localY, rightEdge - left, height);
aBandData.count++;
1998-04-13 20:24:54 +00:00
}
return aBandData.count;
1998-04-13 20:24:54 +00:00
}
1998-05-28 02:37:37 +00:00
PRInt32 nsSpaceManager::GetBandData(nscoord aYOffset,
const nsSize& aMaxSize,
nsBandData& aBandData) const
1998-04-13 20:24:54 +00:00
{
// Convert the y-offset to world coordinates
1998-05-19 03:00:56 +00:00
nscoord y = mY + aYOffset;
1998-04-13 20:24:54 +00:00
// If there are no unavailable rects or the offset is below the bottommost
1998-05-19 03:00:56 +00:00
// band, then all the space is available
if (y >= YMost()) {
// All the requested space is available
aBandData.count = 1;
aBandData.trapezoids[0] = nsRect(0, aYOffset, aMaxSize.width, aMaxSize.height);
aBandData.trapezoids[0].state = nsBandTrapezoid::Available;
aBandData.trapezoids[0].frame = nsnull;
1998-04-13 20:24:54 +00:00
} else {
// Find the first band that contains the y-offset or is below the y-offset
1998-05-19 22:55:40 +00:00
NS_ASSERTION(!mBandList.IsEmpty(), "no bands");
BandRect* band = mBandList.Head();
1998-04-13 20:24:54 +00:00
aBandData.count = 0;
1998-05-19 03:00:56 +00:00
while (nsnull != band) {
if (band->top > y) {
// The band is below the y-offset. The area between the y-offset and
// the top of the band is available
aBandData.count = 1;
aBandData.trapezoids[0] =
1998-05-19 03:00:56 +00:00
nsRect(0, aYOffset, aMaxSize.width, PR_MIN(band->top - y, aMaxSize.height));
aBandData.trapezoids[0].state = nsBandTrapezoid::Available;
aBandData.trapezoids[0].frame = nsnull;
1998-04-13 20:24:54 +00:00
break;
1998-05-19 03:00:56 +00:00
} else if (y < band->bottom) {
// The band contains the y-offset. Return a list of available and
// unavailable rects within the band
1998-05-19 03:00:56 +00:00
return GetBandAvailableSpace(band, y, aMaxSize, aBandData);
1998-04-13 20:24:54 +00:00
} else {
// Skip to the next band
1998-05-19 03:00:56 +00:00
band = GetNextBand(band);
1998-04-13 20:24:54 +00:00
}
}
}
1998-05-12 23:02:02 +00:00
NS_POSTCONDITION(aBandData.count > 0, "unexpected band data count");
return aBandData.count;
1998-04-13 20:24:54 +00:00
}
/**
* Skips to the start of the next band.
*
1998-05-19 03:00:56 +00:00
* @param aBandRect A rect within the band
* @returns The start of the next band, or nsnull of this is the last band.
1998-04-13 20:24:54 +00:00
*/
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect* nsSpaceManager::GetNextBand(const BandRect* aBandRect) const
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
nscoord topOfBand = aBandRect->top;
1998-04-13 20:24:54 +00:00
1998-05-19 22:55:40 +00:00
aBandRect = aBandRect->Next();
1998-05-19 03:00:56 +00:00
while (aBandRect != &mBandList) {
// Check whether this rect is part of the same band
if (aBandRect->top != topOfBand) {
1998-04-13 20:24:54 +00:00
// We found the start of the next band
1998-05-19 03:00:56 +00:00
return (BandRect*)aBandRect;
1998-04-13 20:24:54 +00:00
}
1998-05-19 22:55:40 +00:00
aBandRect = aBandRect->Next();
1998-04-13 20:24:54 +00:00
}
1998-05-19 03:00:56 +00:00
// No bands left
return nsnull;
1998-04-13 20:24:54 +00:00
}
/**
* Divides the current band into two vertically
*
1998-05-19 03:00:56 +00:00
* @param aBandRect the first rect in the band
* @param aBottom where to split the band. This becomes the bottom of the top
* part
1998-04-13 20:24:54 +00:00
*/
1998-05-28 02:37:37 +00:00
void nsSpaceManager::DivideBand(BandRect* aBandRect, nscoord aBottom)
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
NS_PRECONDITION(aBottom < aBandRect->bottom, "bad height");
nscoord topOfBand = aBandRect->top;
BandRect* nextBand = GetNextBand(aBandRect);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
if (nsnull == nextBand) {
nextBand = (BandRect*)&mBandList;
}
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
while (topOfBand == aBandRect->top) {
// Split the band rect into two vertically
BandRect* bottomBandRect = aBandRect->SplitVertically(aBottom);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Insert the new bottom part
1998-05-19 22:55:40 +00:00
nextBand->InsertBefore(bottomBandRect);
1998-04-13 20:24:54 +00:00
// Move to the next rect in the band
1998-05-19 22:55:40 +00:00
aBandRect = aBandRect->Next();
1998-04-13 20:24:54 +00:00
}
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::CanJoinBands(BandRect* aBand, BandRect* aPrevBand)
1998-05-19 22:55:40 +00:00
{
PRBool result;
nscoord topOfBand = aBand->top;
nscoord topOfPrevBand = aPrevBand->top;
// The bands can be joined if:
// - they're adjacent
// - they have the same number of rects
// - each rect has the same left and right edge as its corresponding rect, and
// the rects are occupied by the same frames
if (aPrevBand->bottom == aBand->top) {
// Compare each of the rects in the two bands
while (PR_TRUE) {
if ((aBand->left != aPrevBand->left) || (aBand->right != aPrevBand->right)) {
// The rects have different edges
result = PR_FALSE;
break;
}
if (!aBand->HasSameFrameList(aPrevBand)) {
// The rects are occupied by different frames
result = PR_FALSE;
break;
}
// Move to the next rects within the bands
aBand = aBand->Next();
aPrevBand = aPrevBand->Next();
// Have we reached the end of either band?
PRBool endOfBand = aBand->top != topOfBand;
PRBool endOfPrevBand = aPrevBand->top != topOfPrevBand;
if (endOfBand || endOfPrevBand) {
result = endOfBand & endOfPrevBand;
break; // all done
}
}
} else {
// The bands aren't adjacent
result = PR_FALSE;
}
return result;
}
1998-05-19 15:30:10 +00:00
/**
* Tries to join the two adjacent bands. Returns PR_TRUE if successful and
* PR_FALSE otherwise
*
1998-05-19 22:55:40 +00:00
* If the two bands are joined, the previous band is the the band that's deleted
1998-05-19 15:30:10 +00:00
*/
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::JoinBands(BandRect* aBand, BandRect* aPrevBand)
1998-05-19 15:30:10 +00:00
{
1998-05-19 22:55:40 +00:00
if (CanJoinBands(aBand, aPrevBand)) {
1998-05-20 05:14:14 +00:00
BandRect* startOfNextBand = aBand;
1998-05-19 22:55:40 +00:00
1998-05-20 05:14:14 +00:00
while (aPrevBand != startOfNextBand) {
1998-05-19 22:55:40 +00:00
// Adjust the top of the band we're keeping, and then move to the next
// rect within the band
aBand->top = aPrevBand->top;
aBand = aBand->Next();
// Delete the rect from the previous band
BandRect* next = aPrevBand->Next();
aPrevBand->Remove();
delete aPrevBand;
aPrevBand = next;
}
return PR_TRUE;
}
1998-05-19 15:30:10 +00:00
return PR_FALSE;
}
1998-04-13 20:24:54 +00:00
/**
* Adds a new rect to a band.
*
* @param aBand the first rect in the band
1998-05-19 03:00:56 +00:00
* @param aBandRect the band rect to add to the band
1998-04-13 20:24:54 +00:00
*/
1998-05-28 02:37:37 +00:00
void nsSpaceManager::AddRectToBand(BandRect* aBand,
BandRect* aBandRect)
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
NS_PRECONDITION((aBand->top == aBandRect->top) &&
(aBand->bottom == aBandRect->bottom), "bad band");
NS_PRECONDITION(1 == aBandRect->numFrames, "shared band rect");
nscoord topOfBand = aBand->top;
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Figure out where in the band horizontally to insert the rect
1998-04-13 20:24:54 +00:00
do {
// Compare the left edge of the new rect with the left edge of the existing
// rect
1998-05-19 03:00:56 +00:00
if (aBandRect->left < aBand->left) {
// The new rect's left edge is to the left of the existing rect's left edge.
// Could be any of these cases (N is new rect, E is existing rect):
//
// Case 1: left-of Case 2: overlaps Case 3: N.contains(E)
// --------------- ---------------- ---------------------
// +-----+ +-----+ +-----+ +---------+
// | N | | E | | N | | N |
// +-----+ +-----+ +-----+ +---------+
// +-----+ +---+
// | E | | E |
// +-----+ +---+
//
// Do the two rectangles overlap?
1998-05-19 03:00:56 +00:00
if (aBandRect->right <= aBand->left) {
// No, the new rect is completely to the left of the existing rect
// (case #1). Insert a new rect
1998-05-19 22:55:40 +00:00
aBand->InsertBefore(aBandRect);
return;
1998-04-13 20:24:54 +00:00
}
1998-05-19 03:00:56 +00:00
// Yes, they overlap. Compare the right edges.
if (aBandRect->right > aBand->right) {
// The new rect's right edge is to the right of the existing rect's
// right edge (case #3). Split the new rect
BandRect* r1 = aBandRect->SplitHorizontally(aBand->left);
1998-05-19 03:00:56 +00:00
// Insert the part of the new rect that's to the left of the existing
// rect as a new band rect
1998-05-19 22:55:40 +00:00
aBand->InsertBefore(aBandRect);
// Continue below with the part that overlaps the existing rect
1998-05-19 03:00:56 +00:00
aBandRect = r1;
1998-05-19 03:00:56 +00:00
} else {
if (aBand->right > aBandRect->right) {
// The existing rect extends past the new rect (case #2). Split the
// existing rect
BandRect* r1 = aBand->SplitHorizontally(aBandRect->right);
// Insert the new right half of the existing rect
1998-05-19 22:55:40 +00:00
aBand->InsertAfter(r1);
1998-05-19 03:00:56 +00:00
}
// Insert the part of the new rect that's to the left of the existing
// rect
aBandRect->right = aBand->left;
1998-05-19 22:55:40 +00:00
aBand->InsertBefore(aBandRect);
1998-05-19 03:00:56 +00:00
1998-05-19 22:55:40 +00:00
// Mark the existing rect as shared
1998-05-19 03:00:56 +00:00
aBand->AddFrame(aBandRect->frame);
return;
}
}
1998-05-19 03:00:56 +00:00
if (aBandRect->left > aBand->left) {
// The new rect's left edge is to the right of the existing rect's left
// edge. Could be any one of these cases:
//
// Case 4: right-of Case 5: overlaps Case 6: E.Contains(N)
// --------------- ---------------- ---------------------
// +-----+ +-----+ +-----+ +------------+
// | E | | N | | E | | E |
// +-----+ +-----+ +-----+ +------------+
// +-----+ +-----+
// | N | | N |
// +-----+ +-----+
//
1998-05-19 03:00:56 +00:00
if (aBandRect->left >= aBand->right) {
// The new rect is to the right of the existing rect (case #4), so move
// to the next rect in the band
1998-05-19 22:55:40 +00:00
aBand = aBand->Next();
continue;
}
// The rects overlap, so divide the existing rect into two rects: the
// part to the left of the new rect, and the part that overlaps
1998-05-19 03:00:56 +00:00
BandRect* r1 = aBand->SplitHorizontally(aBandRect->left);
// Insert the new right half of the existing rect, and make it the current
// rect
1998-05-19 22:55:40 +00:00
aBand->InsertAfter(r1);
1998-05-19 03:00:56 +00:00
aBand = r1;
}
// At this point the left edge of the new rect is the same as the left edge
// of the existing rect
1998-05-19 03:00:56 +00:00
NS_ASSERTION(aBandRect->left == aBand->left, "unexpected rect");
// Compare which rect is wider, the new rect or the existing rect
1998-05-19 03:00:56 +00:00
if (aBand->right > aBandRect->right) {
// The existing rect is wider (case #6). Divide the existing rect into
// two rects: the part that overlaps, and the part to the right of the
// new rect
BandRect* r1 = aBand->SplitHorizontally(aBandRect->right);
// Insert the new right half of the existing rect
1998-05-19 22:55:40 +00:00
aBand->InsertAfter(r1);
1998-05-19 03:00:56 +00:00
// Mark the overlap as being shared
aBand->AddFrame(aBandRect->frame);
1998-04-13 20:24:54 +00:00
return;
1998-05-19 03:00:56 +00:00
} else {
// Indicate the frames share the existing rect
aBand->AddFrame(aBandRect->frame);
if (aBand->right == aBandRect->right) {
// The new and existing rect have the same right edge. We're all done,
// and the new band rect is no longer needed
delete aBandRect;
return;
} else {
// The new rect is wider than the existing rect (cases #5). Set the
// new rect to be the overhang, and move to the next rect within the band
aBandRect->left = aBand->right;
1998-05-19 22:55:40 +00:00
aBand = aBand->Next();
1998-05-19 03:00:56 +00:00
continue;
}
}
1998-05-19 22:55:40 +00:00
} while (aBand->top == topOfBand);
1998-04-13 20:24:54 +00:00
// Insert a new rect
1998-05-19 22:55:40 +00:00
aBand->InsertBefore(aBandRect);
1998-04-13 20:24:54 +00:00
}
// When comparing a rect to a band there are seven cases to consider.
// 'R' is the rect and 'B' is the band.
//
// Case 1 Case 2 Case 3 Case 4
// ------ ------ ------ ------
// +-----+ +-----+ +-----+ +-----+
// | R | | R | +-----+ +-----+ | | | |
// +-----+ +-----+ | | | R | | B | | B |
// +-----+ | B | +-----+ | | +-----+ | |
// | | | | +-----+ | R | +-----+
// | B | +-----+ +-----+
// | |
// +-----+
//
//
//
// Case 5 Case 6 Case 7
// ------ ------ ------
// +-----+ +-----+ +-----+ +-----+
// | | | R | | B | | | +-----+
// | B | +-----+ +-----+ | R | | B |
// | | | | +-----+
// +-----+ +-----+
// +-----+
// | R |
// +-----+
//
1998-05-28 02:37:37 +00:00
void nsSpaceManager::InsertBandRect(BandRect* aBandRect)
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
// If there are no existing bands or this rect is below the bottommost
// band, then add a new band
if (aBandRect->top >= YMost()) {
1998-05-19 22:55:40 +00:00
mBandList.Append(aBandRect);
1998-05-19 03:00:56 +00:00
return;
1998-04-13 20:24:54 +00:00
}
// Examine each band looking for a band that intersects this rect
1998-05-19 22:55:40 +00:00
NS_ASSERTION(!mBandList.IsEmpty(), "no bands");
BandRect* band = mBandList.Head();
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
while (nsnull != band) {
1998-04-13 20:24:54 +00:00
// Compare the top edge of this rect with the top edge of the band
1998-05-19 03:00:56 +00:00
if (aBandRect->top < band->top) {
1998-04-13 20:24:54 +00:00
// The top edge of the rect is above the top edge of the band.
// Is there any overlap?
1998-05-19 03:00:56 +00:00
if (aBandRect->bottom <= band->top) {
1998-04-13 20:24:54 +00:00
// Case #1. This rect is completely above the band, so insert a
1998-05-19 03:00:56 +00:00
// new band before the current band
1998-05-19 22:55:40 +00:00
band->InsertBefore(aBandRect);
1998-04-13 20:24:54 +00:00
break; // we're all done
}
1998-05-19 03:00:56 +00:00
// Case #2 and case #7. Divide this rect, creating a new rect for
// the part that's above the band
BandRect* bandRect1 = new BandRect(aBandRect->left, aBandRect->top,
aBandRect->right, band->top,
aBandRect->frame);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Insert bandRect1 as a new band
1998-05-19 22:55:40 +00:00
band->InsertBefore(bandRect1);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Modify this rect to exclude the part above the band
aBandRect->top = band->top;
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
} else if (aBandRect->top > band->top) {
1998-04-13 20:24:54 +00:00
// The top edge of the rect is below the top edge of the band. Is there
// any overlap?
1998-05-19 03:00:56 +00:00
if (aBandRect->top >= band->bottom) {
1998-04-13 20:24:54 +00:00
// Case #5. This rect is below the current band. Skip to the next band
1998-05-19 03:00:56 +00:00
band = GetNextBand(band);
1998-04-13 20:24:54 +00:00
continue;
}
// Case #3 and case #4. Divide the current band into two bands with the
// top band being the part that's above the rect
1998-05-19 03:00:56 +00:00
DivideBand(band, aBandRect->top);
1998-04-13 20:24:54 +00:00
// Skip to the bottom band that we just created
1998-05-19 03:00:56 +00:00
band = GetNextBand(band);
1998-04-13 20:24:54 +00:00
}
// At this point the rect and the band should have the same y-offset
1998-05-19 03:00:56 +00:00
NS_ASSERTION(aBandRect->top == band->top, "unexpected band");
1998-04-13 20:24:54 +00:00
// Is the band higher than the rect?
1998-05-19 03:00:56 +00:00
if (band->bottom > aBandRect->bottom) {
1998-04-13 20:24:54 +00:00
// Divide the band into two bands with the top band the same height
// as the rect
1998-05-19 03:00:56 +00:00
DivideBand(band, aBandRect->bottom);
1998-04-13 20:24:54 +00:00
}
1998-05-19 03:00:56 +00:00
if (aBandRect->bottom == band->bottom) {
1998-04-13 20:24:54 +00:00
// Add the rect to the band
1998-05-19 03:00:56 +00:00
AddRectToBand(band, aBandRect);
1998-04-13 20:24:54 +00:00
break;
} else {
// Case #4 and case #7. The rect contains the band vertically. Divide
// the rect, creating a new rect for the part that overlaps the band
1998-05-19 03:00:56 +00:00
BandRect* bandRect1 = new BandRect(aBandRect->left, aBandRect->top,
aBandRect->right, band->bottom,
aBandRect->frame);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Add bandRect1 to the band
AddRectToBand(band, bandRect1);
1998-04-13 20:24:54 +00:00
1998-05-19 03:00:56 +00:00
// Modify aBandRect to be the part below the band
aBandRect->top = band->bottom;
1998-04-13 20:24:54 +00:00
// Continue with the next band
1998-05-19 03:00:56 +00:00
band = GetNextBand(band);
if (nsnull == band) {
1998-04-13 20:24:54 +00:00
// Append a new bottommost band
1998-05-19 22:55:40 +00:00
mBandList.Append(aBandRect);
1998-04-13 20:24:54 +00:00
break;
}
}
}
1998-05-19 03:00:56 +00:00
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace)
1998-05-19 03:00:56 +00:00
{
NS_PRECONDITION(nsnull != aFrame, "null frame");
// See if there is already a region associated with aFrame
FrameInfo* frameInfo = GetFrameInfoFor(aFrame);
if (nsnull != frameInfo) {
NS_WARNING("aFrame is already associated with a region");
return PR_FALSE;
}
// Convert the frame to world coordinates
nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY,
aUnavailableSpace.width, aUnavailableSpace.height);
1998-05-19 22:55:40 +00:00
// Verify that the offset is within the defined coordinate space
if ((rect.x < 0) || (rect.y < 0)) {
NS_WARNING("invalid offset for rect region");
return PR_FALSE;
}
1998-05-19 03:00:56 +00:00
// Create a frame info structure
frameInfo = CreateFrameInfo(aFrame, rect);
// Is the rect empty?
if (aUnavailableSpace.IsEmpty()) {
// The rect doesn't consume any space, so don't add any band data
return PR_TRUE;
}
// Allocate a band rect
BandRect* bandRect = new BandRect(rect.x, rect.y, rect.XMost(), rect.YMost(), aFrame);
1998-05-12 23:02:02 +00:00
1998-05-19 03:00:56 +00:00
// Insert the band rect
InsertBandRect(bandRect);
1998-05-12 23:02:02 +00:00
return PR_TRUE;
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::ResizeRectRegion(nsIFrame* aFrame,
nscoord aDeltaWidth,
nscoord aDeltaHeight,
AffectedEdge aEdge)
1998-05-12 23:02:02 +00:00
{
1998-05-19 22:55:40 +00:00
// Get the frame info associated with with aFrame
FrameInfo* frameInfo = GetFrameInfoFor(aFrame);
if (nsnull == frameInfo) {
NS_WARNING("no region associated with aFrame");
return PR_FALSE;
}
nsRect rect(frameInfo->rect);
rect.SizeBy(aDeltaWidth, aDeltaHeight);
if (aEdge == LeftEdge) {
rect.x += aDeltaWidth;
}
// Verify that the offset is within the defined coordinate space
if ((rect.x < 0) || (rect.y < 0)) {
NS_WARNING("invalid offset when resizing rect region");
return PR_FALSE;
}
// For the time being just remove it and add it back in
RemoveRegion(aFrame);
return AddRectRegion(aFrame, rect);
1998-05-12 23:02:02 +00:00
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::OffsetRegion(nsIFrame* aFrame, nscoord aDx, nscoord aDy)
1998-05-12 23:02:02 +00:00
{
1998-05-19 22:55:40 +00:00
// Get the frame info associated with with aFrame
FrameInfo* frameInfo = GetFrameInfoFor(aFrame);
if (nsnull == frameInfo) {
NS_WARNING("no region associated with aFrame");
return PR_FALSE;
}
nsRect rect(frameInfo->rect);
rect.MoveBy(aDx, aDy);
// Verify that the offset is within the defined coordinate space
if ((rect.x < 0) || (rect.y < 0)) {
NS_WARNING("invalid offset when offseting rect region");
return PR_FALSE;
}
// For the time being just remove it and add it back in
RemoveRegion(aFrame);
return AddRectRegion(aFrame, rect);
1998-05-12 23:02:02 +00:00
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::RemoveRegion(nsIFrame* aFrame)
1998-05-12 23:02:02 +00:00
{
1998-05-19 22:55:40 +00:00
// Get the frame info associated with aFrame
1998-05-19 15:30:10 +00:00
FrameInfo* frameInfo = GetFrameInfoFor(aFrame);
if (nsnull == frameInfo) {
NS_WARNING("no region associated with aFrame");
return PR_FALSE;
}
1998-05-12 23:02:02 +00:00
1998-05-19 15:30:10 +00:00
if (!frameInfo->rect.IsEmpty()) {
1998-05-20 05:14:14 +00:00
NS_ASSERTION(!mBandList.IsEmpty(), "no bands");
1998-05-19 22:55:40 +00:00
BandRect* band = mBandList.Head();
1998-05-19 15:30:10 +00:00
BandRect* prevBand = nsnull;
PRBool prevFoundMatchingRect = PR_FALSE;
1998-05-19 15:30:10 +00:00
// Iterate each band looking for rects tagged with aFrame
while (nsnull != band) {
BandRect* rect = band;
BandRect* prevRect = nsnull;
nscoord topOfBand = band->top;
PRBool foundMatchingRect = PR_FALSE;
PRBool prevIsSharedRect = PR_FALSE;
// Iterate each rect in the band
do {
PRBool isSharedRect = PR_FALSE;
if (rect->IsOccupiedBy(aFrame)) {
1998-05-20 05:14:14 +00:00
// Remember that we found a matching rect in this band
foundMatchingRect = PR_TRUE;
1998-05-19 15:30:10 +00:00
if (rect->numFrames > 1) {
// The band rect is occupied by more than one frame
rect->RemoveFrame(aFrame);
// Remember that this rect was being shared by more than one frame
// including aFrame
isSharedRect = PR_TRUE;
} else {
// The rect isn't shared so just delete it
1998-05-20 05:14:14 +00:00
BandRect* next = rect->Next();
1998-05-19 22:55:40 +00:00
rect->Remove();
1998-05-20 05:14:14 +00:00
if (rect == band) {
// The rect we're deleting is the start of the band
if (topOfBand == next->top) {
band = next;
} else {
band = nsnull;
}
}
delete rect;
rect = next;
// We don't need to try and coalesce adjacent rects in this case
prevRect = nsnull;
prevIsSharedRect = PR_FALSE;
continue;
1998-05-19 15:30:10 +00:00
}
}
1998-05-20 05:14:14 +00:00
// If we found a shared rect occupied by aFrame, then we need to try
// and coalesce adjacent rects
1998-05-19 15:30:10 +00:00
if (prevIsSharedRect || (isSharedRect && (nsnull != prevRect))) {
NS_ASSERTION(nsnull != prevRect, "no previous rect");
if ((prevRect->right == rect->left) && (prevRect->HasSameFrameList(rect))) {
// Modify the current rect's left edge, and delete the previous rect
rect->left = prevRect->left;
1998-05-19 22:55:40 +00:00
prevRect->Remove();
1998-05-20 05:14:14 +00:00
if (prevRect == band) {
// the rect we're deleting is the start of the band
band = rect;
}
1998-05-19 15:30:10 +00:00
delete prevRect;
}
}
// Get the next rect in the band
prevRect = rect;
prevIsSharedRect = isSharedRect;
1998-05-19 22:55:40 +00:00
rect = rect->Next();
1998-05-19 15:30:10 +00:00
} while (rect->top == topOfBand);
1998-05-20 05:14:14 +00:00
if (nsnull != band) {
// If we found a rect occupied by aFrame in this band or the previous band
// then try to join the two bands
1998-05-30 23:56:28 +00:00
if ((nsnull != prevBand) && (foundMatchingRect || prevFoundMatchingRect)) {
1998-05-20 05:14:14 +00:00
// Try and join this band with the previous band
JoinBands(band, prevBand);
}
}
1998-05-19 15:30:10 +00:00
// Move to the next band
prevFoundMatchingRect = foundMatchingRect;
1998-05-20 05:14:14 +00:00
prevBand = band;
band = (rect == &mBandList) ? nsnull : rect;
1998-05-12 23:02:02 +00:00
}
}
1998-05-19 15:30:10 +00:00
DestroyFrameInfo(frameInfo);
return PR_TRUE;
1998-04-13 20:24:54 +00:00
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::ClearRegions()
1998-04-13 20:24:54 +00:00
{
1998-05-19 03:00:56 +00:00
ClearFrameInfo();
1998-05-19 22:55:40 +00:00
mBandList.Clear();
1998-05-19 03:00:56 +00:00
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::FrameInfo* nsSpaceManager::GetFrameInfoFor(nsIFrame* aFrame)
1998-05-19 03:00:56 +00:00
{
FrameInfo* result = nsnull;
if (nsnull != mFrameInfoMap) {
result = (FrameInfo*)PL_HashTableLookup(mFrameInfoMap, (const void*)aFrame);
}
return result;
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::FrameInfo* nsSpaceManager::CreateFrameInfo(nsIFrame* aFrame,
const nsRect& aRect)
1998-05-19 03:00:56 +00:00
{
if (nsnull == mFrameInfoMap) {
mFrameInfoMap = PL_NewHashTable(17, NS_HashNumber, PL_CompareValues,
PL_CompareValues, nsnull, nsnull);
}
FrameInfo* frameInfo = new FrameInfo(aFrame, aRect);
PL_HashTableAdd(mFrameInfoMap, (const void*)aFrame, frameInfo);
return frameInfo;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::DestroyFrameInfo(FrameInfo* aFrameInfo)
1998-05-19 03:00:56 +00:00
{
1998-05-20 05:14:14 +00:00
PL_HashTableRemove(mFrameInfoMap, (const void*)aFrameInfo->frame);
1998-05-19 03:00:56 +00:00
delete aFrameInfo;
}
/////////////////////////////////////////////////////////////////////////////
// FrameInfo
1998-05-28 02:37:37 +00:00
nsSpaceManager::FrameInfo::FrameInfo(nsIFrame* aFrame, const nsRect& aRect)
1998-05-19 03:00:56 +00:00
: frame(aFrame), rect(aRect)
{
1998-04-13 20:24:54 +00:00
}
/////////////////////////////////////////////////////////////////////////////
// BandRect
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect::BandRect(nscoord aLeft,
nscoord aTop,
nscoord aRight,
nscoord aBottom,
nsIFrame* aFrame)
1998-05-19 03:00:56 +00:00
{
left = aLeft;
top = aTop;
right = aRight;
bottom = aBottom;
frame = aFrame;
numFrames = 1;
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect::BandRect(nscoord aLeft,
nscoord aTop,
nscoord aRight,
nscoord aBottom,
nsVoidArray* aFrames)
1998-05-19 03:00:56 +00:00
{
left = aLeft;
top = aTop;
right = aRight;
bottom = aBottom;
frames = new nsVoidArray;
frames->operator=(*aFrames);
numFrames = frames->Count();
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect::~BandRect()
1998-05-19 03:00:56 +00:00
{
if (numFrames > 1) {
delete frames;
}
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect* nsSpaceManager::BandRect::SplitVertically(nscoord aBottom)
1998-05-19 03:00:56 +00:00
{
NS_PRECONDITION((aBottom > top) && (aBottom < bottom), "bad argument");
// Create a new band rect for the bottom part
BandRect* bottomBandRect;
if (numFrames > 1) {
bottomBandRect = new BandRect(left, aBottom, right, bottom, frames);
} else {
bottomBandRect = new BandRect(left, aBottom, right, bottom, frame);
}
// This band rect becomes the top part, so adjust the bottom edge
bottom = aBottom;
return bottomBandRect;
}
1998-05-28 02:37:37 +00:00
nsSpaceManager::BandRect* nsSpaceManager::BandRect::SplitHorizontally(nscoord aRight)
1998-05-19 03:00:56 +00:00
{
NS_PRECONDITION((aRight > left) && (aRight < right), "bad argument");
// Create a new band rect for the right part
BandRect* rightBandRect;
if (numFrames > 1) {
rightBandRect = new BandRect(aRight, top, right, bottom, frames);
} else {
rightBandRect = new BandRect(aRight, top, right, bottom, frame);
}
// This band rect becomes the left part, so adjust the right edge
right = aRight;
return rightBandRect;
}
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::BandRect::IsOccupiedBy(const nsIFrame* aFrame) const
{
PRBool result;
if (1 == numFrames) {
result = (frame == aFrame);
} else {
PRInt32 count = frames->Count();
result = PR_FALSE;
for (PRInt32 i = 0; i < count; i++) {
nsIFrame* f = (nsIFrame*)frames->ElementAt(i);
if (f == aFrame) {
result = PR_TRUE;
break;
}
}
}
return result;
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::BandRect::AddFrame(const nsIFrame* aFrame)
{
if (1 == numFrames) {
nsIFrame* f = frame;
frames = new nsVoidArray;
frames->AppendElement(f);
}
numFrames++;
1998-05-19 15:30:10 +00:00
frames->AppendElement((void*)aFrame);
NS_POSTCONDITION(frames->Count() == numFrames, "bad frame count");
}
1998-05-28 02:37:37 +00:00
void nsSpaceManager::BandRect::RemoveFrame(const nsIFrame* aFrame)
{
NS_PRECONDITION(numFrames > 1, "only one frame");
1998-05-19 15:30:10 +00:00
frames->RemoveElement((void*)aFrame);
numFrames--;
if (1 == numFrames) {
nsIFrame* f = (nsIFrame*)frames->ElementAt(0);
delete frames;
frame = f;
}
}
1998-05-19 15:30:10 +00:00
1998-05-28 02:37:37 +00:00
PRBool nsSpaceManager::BandRect::HasSameFrameList(const BandRect* aBandRect) const
1998-05-19 15:30:10 +00:00
{
1998-05-19 22:55:40 +00:00
PRBool result;
1998-05-19 15:30:10 +00:00
// Check whether they're occupied by the same number of frames
if (numFrames != aBandRect->numFrames) {
1998-05-19 22:55:40 +00:00
result = PR_FALSE;
} else if (1 == numFrames) {
result = frame == aBandRect->frame;
1998-05-19 15:30:10 +00:00
} else {
1998-05-19 22:55:40 +00:00
result = PR_TRUE;
1998-05-19 15:30:10 +00:00
// For each frame occupying this band rect check whether it also occupies
// aBandRect
PRInt32 count = frames->Count();
for (PRInt32 i = 0; i < count; i++) {
nsIFrame* f = (nsIFrame*)frames->ElementAt(i);
if (-1 == aBandRect->frames->IndexOf(f)) {
1998-05-19 22:55:40 +00:00
result = PR_FALSE;
break;
1998-05-19 15:30:10 +00:00
}
}
}
1998-05-19 22:55:40 +00:00
return result;
1998-05-19 15:30:10 +00:00
}