From 2269c1d9620f1978ca4580210dadc895da6d8897 Mon Sep 17 00:00:00 2001 From: troy Date: Wed, 13 May 1998 16:22:44 +0000 Subject: [PATCH] Changes to space manager implementation. --- layout/base/public/nsISpaceManager.h | 13 +- layout/base/src/nsSpaceManager.cpp | 192 ++++++++++++++++++++++----- layout/base/src/nsSpaceManager.h | 43 +++--- layout/generic/nsSpaceManager.cpp | 192 ++++++++++++++++++++++----- layout/generic/nsSpaceManager.h | 43 +++--- 5 files changed, 383 insertions(+), 100 deletions(-) diff --git a/layout/base/public/nsISpaceManager.h b/layout/base/public/nsISpaceManager.h index 4bf4e92629d8..faaa9b21cadf 100644 --- a/layout/base/public/nsISpaceManager.h +++ b/layout/base/public/nsISpaceManager.h @@ -48,7 +48,7 @@ struct nsBandTrapezoid { nscoord xTopRight, xBottomRight; // right edge x-coordinates State state; // state of the space union { - nsIFrame* frame; // frame occupying the space + nsIFrame* frame; // single frame occupying the space const nsVoidArray* frames; // list of frames occupying the space }; @@ -142,13 +142,18 @@ public: virtual PRBool AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) = 0; /** - * Reshape the rectangular region associated with aFrame. The new space is - * relative to the local coordinate system. + * Resize the rectangular region associated with aFrame by the specified deltas. + * The height change always applies to the bottom edge or the existing rect. + * You specify whether the width change applies to the left or right edge * * Returns PR_TRUE if successful and PR_FALSE otherwise, e.g. there is no region * tagged with aFrame */ - virtual PRBool ReshapeRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) = 0; + enum AffectedEdge {LeftEdge, RightEdge}; + virtual PRBool ResizeRectRegion(nsIFrame* aFrame, + nscoord aDeltaWidth, + nscoord aDeltaHeight, + AffectedEdge aEdge = RightEdge) = 0; /** * Offset the region associated with aFrame by the specified amount. diff --git a/layout/base/src/nsSpaceManager.cpp b/layout/base/src/nsSpaceManager.cpp index fb50d54968b2..6a8549f4f19d 100644 --- a/layout/base/src/nsSpaceManager.cpp +++ b/layout/base/src/nsSpaceManager.cpp @@ -20,6 +20,7 @@ #include "nsRect.h" #include "nsSize.h" #include +#include "nsVoidArray.h" static NS_DEFINE_IID(kISpaceManagerIID, NS_ISPACEMANAGER_IID); @@ -71,11 +72,11 @@ nscoord SpaceManager::YMost() const * @param aMaxSize the size to use to constrain the band data * @param aAvailableBand */ -PRInt32 SpaceManager::GetBandAvailableSpace(const nsBandRect* aBand, - PRInt32 aIndex, - nscoord aY, - const nsSize& aMaxSize, - nsBandData& aBandData) const +PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, + PRInt32 aIndex, + nscoord aY, + const nsSize& aMaxSize, + nsBandData& aBandData) const { PRInt32 numRects = LengthOfBand(aBand, aIndex); nscoord localY = aY - mY; @@ -121,8 +122,14 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const nsBandRect* aBand, } // The rect represents unavailable space, so add another trapezoid - trapezoid->state = nsBandTrapezoid::smOccupied; - trapezoid->frame = aBand->frame; + if (1 == aBand->numFrames) { + trapezoid->state = nsBandTrapezoid::smOccupied; + trapezoid->frame = aBand->frame; + } else { + NS_ASSERTION(aBand->numFrames > 1, "unexpected frame count"); + trapezoid->state = nsBandTrapezoid::smOccupiedMultiple; + trapezoid->frames = aBand->frames; + } nscoord x = aBand->x; // The first band can straddle the clip rect @@ -179,7 +186,7 @@ PRInt32 SpaceManager::GetBandData(nscoord aYOffset, aBandData.trapezoids[0].frame = nsnull; } else { // Find the first band that contains the y-offset or is below the y-offset - nsBandRect* band = mRectArray.mRects; + BandRect* band = mRectArray.mRects; aBandData.count = 0; for (PRInt32 i = 0; i < mRectArray.mCount; ) { @@ -217,7 +224,7 @@ PRInt32 SpaceManager::GetBandData(nscoord aYOffset, * next band. If there is no next band then aRect is undefined * and aIndex is set to the number of rects in the rect array */ -PRBool SpaceManager::GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const +PRBool SpaceManager::GetNextBand(BandRect*& aRect, PRInt32& aIndex) const { nscoord topOfBand = aRect->y; @@ -241,7 +248,7 @@ PRBool SpaceManager::GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const * @param aBand the first rect in the band * @param aIndex aBand's index in the rect array */ -PRInt32 SpaceManager::LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) const +PRInt32 SpaceManager::LengthOfBand(const BandRect* aBand, PRInt32 aIndex) const { nscoord topOfBand = aBand->y; PRInt32 result = 1; @@ -268,13 +275,13 @@ PRInt32 SpaceManager::LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) cons * @param aRect the first rect in the band * @param aIndex aBand's index in the rect array */ -PRBool SpaceManager::CoalesceBand(nsBandRect* aBand, PRInt32 aIndex) +PRBool SpaceManager::CoalesceBand(BandRect* aBand, PRInt32 aIndex) { PRBool result = PR_FALSE; while ((aIndex + 1) < mRectArray.mCount) { // Is there another rect in this band? - nsBandRect* nextRect = aBand + 1; + BandRect* nextRect = aBand + 1; if (nextRect->y == aBand->y) { // The rects must not be overlapping @@ -313,7 +320,7 @@ PRBool SpaceManager::CoalesceBand(nsBandRect* aBand, PRInt32 aIndex) * @param aIndex aBand's index in the rect array * @param aB1Height the height of the new band to create */ -void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Height) +void SpaceManager::DivideBand(BandRect* aBand, PRInt32 aIndex, nscoord aB1Height) { NS_PRECONDITION(aB1Height < aBand->height, "bad height"); @@ -327,7 +334,11 @@ void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Heig // Insert a new bottom band nsRect r(aBand->x, aBand->y + aB1Height, aBand->width, aB2Height); - mRectArray.InsertAt(r, insertAt, aBand->frame); + if (aBand->numFrames > 1) { + mRectArray.InsertAt(r, insertAt, aBand->frames); + } else { + mRectArray.InsertAt(r, insertAt, aBand->frame); + } aBand = mRectArray.mRects + aIndex; // memory may have changed... // Adjust the height of the top band @@ -347,7 +358,7 @@ void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Heig * @param aRect the rect to add to the band. It's in world coordinates * @returns PR_TRUE if successful and PR_FALSE if this is the last band */ -void SpaceManager::AddRectToBand(nsBandRect* aBand, +void SpaceManager::AddRectToBand(BandRect* aBand, PRInt32 aIndex, const nsRect& aRect, nsIFrame* aFrame) @@ -426,8 +437,13 @@ void SpaceManager::AddRectToBand(nsBandRect* aBand, // Insert the new right half of the existing rect, and make it the current // rect aIndex++; - mRectArray.InsertAt(r1, aIndex, aBand->frame); + if (aBand->numFrames > 1) { + mRectArray.InsertAt(r1, aIndex, aBand->frames); + } else { + mRectArray.InsertAt(r1, aIndex, aBand->frame); + } aBand = mRectArray.mRects + aIndex; // memory may have changed... + aBand->AddFrame(aFrame); } // At this point the left edge of the new rect is the same as the left edge @@ -441,16 +457,16 @@ void SpaceManager::AddRectToBand(nsBandRect* aBand, // the new rect nsRect r1(rect.XMost(), aBand->y, aBand->XMost() - rect.XMost(), aBand->height); - // Modify the left half of the existing rect + // Modify the left half of the existing rect, and indicate the rect is + // shared aBand->width = r1.x - aBand->x; + aBand->AddFrame(aFrame); // Insert the new right half of the existing rect mRectArray.InsertAt(r1, aIndex, aBand->frame); aBand = mRectArray.mRects + aIndex; // memory may have changed... } - // XXX Mark the existing rect as being shared by the two frames... - if (rect.width == aBand->width) { // We're all done return; @@ -502,6 +518,13 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); + // Check if the rect is empty + if (aUnavailableSpace.IsEmpty()) { + // The rect doesn't consume any space so don't add the rect to the band data + mEmptyRects.Append(rect, aFrame); + return PR_TRUE; + } + // If there are no existing bands or this rect is below the bottommost band, // then add a new band if ((0 == mRectArray.mCount) || (rect.y >= mRectArray.YMost())) { @@ -511,7 +534,7 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS } // Examine each band looking for a band that intersects this rect - nsBandRect* band = mRectArray.mRects; + BandRect* band = mRectArray.mRects; for (PRInt32 i = 0; i < mRectArray.mCount; ) { // Compare the top edge of this rect with the top edge of the band @@ -597,9 +620,12 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS return PR_TRUE; } -PRBool SpaceManager::ReshapeRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) +PRBool SpaceManager::ResizeRectRegion(nsIFrame* aFrame, + nscoord aDeltaWidth, + nscoord aDeltaHeight, + AffectedEdge aEdge) { - NS_NOTYETIMPLEMENTED("offseting a region"); + NS_NOTYETIMPLEMENTED("resizing a region"); return PR_FALSE; } @@ -613,16 +639,23 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) { PRBool result = PR_FALSE; - // Walk the list of rects and remove those tagged with aFrame. - // XXX We need to properly handle overlapped rects - for (PRInt32 i = 0; i < mRectArray.mCount; i++) { - if (mRectArray.mRects[i].frame == aFrame) { - mRectArray.RemoveAt(i); + // Walk the list of rects and remove those rects tagged with aFrame. + for (PRInt32 i = 0; i < mRectArray.mCount;) { + BandRect* r = &mRectArray.mRects[i]; + + if (r->IsOccupiedBy(aFrame)) { + if (r->numFrames > 1) { + r->RemoveFrame(aFrame); + i++; + } else { + mRectArray.RemoveAt(i); + } result = PR_TRUE; } } - // XXX We should try and coalesce adjacent bands... + // XXX We should try and coalesce adjoining rects within a band, and + // adjacent bands as well... return result; } @@ -631,6 +664,59 @@ void SpaceManager::ClearRegions() mRectArray.Clear(); } +///////////////////////////////////////////////////////////////////////////// +// BandRect + +PRBool SpaceManager::BandRect::IsOccupiedBy(nsIFrame* aFrame) +{ + 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; +} + +void SpaceManager::BandRect::AddFrame(nsIFrame* aFrame) +{ + if (1 == numFrames) { + nsIFrame* f = frame; + frames = new nsVoidArray; + frames->AppendElement(f); + } + + numFrames++; + frames->AppendElement(aFrame); + NS_POSTCONDITION(frames->Count() == numFrames, "bad frame count"); +} + +void SpaceManager::BandRect::RemoveFrame(nsIFrame* aFrame) +{ + NS_PRECONDITION(numFrames > 1, "only one frame"); + frames->RemoveElement(aFrame); + numFrames--; + + if (1 == numFrames) { + nsIFrame* f = (nsIFrame*)frames->ElementAt(0); + + delete frames; + frame = f; + } +} + ///////////////////////////////////////////////////////////////////////////// // RectArray @@ -642,6 +728,16 @@ SpaceManager::RectArray::RectArray() SpaceManager::RectArray::~RectArray() { + // Delete any void arrays used for the list of frames occupying a rect + BandRect* r = mRects; + for (PRInt32 n = mCount; n > 0; n--) { + if (r->numFrames > 1) { + delete r->frames; + } + r++; + } + + // Free the space for the rect array if (nsnull != mRects) { free(mRects); } @@ -657,7 +753,7 @@ void SpaceManager::RectArray::Append(const nsRect& aRect, nsIFrame* aFrame) // Ensure there's enough capacity if (mCount >= mMax) { mMax += 8; - mRects = (nsBandRect*)realloc(mRects, mMax * sizeof(nsBandRect)); + mRects = (BandRect*)realloc(mRects, mMax * sizeof(BandRect)); } mRects[mCount].x = aRect.x; @@ -665,6 +761,7 @@ void SpaceManager::RectArray::Append(const nsRect& aRect, nsIFrame* aFrame) mRects[mCount].width = aRect.width; mRects[mCount].height = aRect.height; mRects[mCount].frame = aFrame; + mRects[mCount].numFrames = 1; mCount++; } @@ -677,29 +774,58 @@ void SpaceManager::RectArray::InsertAt(const nsRect& aRect, // Ensure there's enough capacity if (mCount >= mMax) { mMax += 8; - mRects = (nsBandRect*)realloc(mRects, mMax * sizeof(nsBandRect)); + mRects = (BandRect*)realloc(mRects, mMax * sizeof(BandRect)); } - memmove(&mRects[aIndex + 1], &mRects[aIndex], (mCount - aIndex) * sizeof(nsBandRect)); + memmove(&mRects[aIndex + 1], &mRects[aIndex], (mCount - aIndex) * sizeof(BandRect)); mRects[aIndex].x = aRect.x; mRects[aIndex].y = aRect.y; mRects[aIndex].width = aRect.width; mRects[aIndex].height = aRect.height; mRects[aIndex].frame = aFrame; + mRects[aIndex].numFrames = 1; mCount++; } +void SpaceManager::RectArray::InsertAt(const nsRect& aRect, + PRInt32 aIndex, + const nsVoidArray* aFramesList) +{ + NS_PRECONDITION(aIndex <= mCount, "bad index"); // no holes in the array + + InsertAt(aRect, aIndex, (nsIFrame*)nsnull); + mRects[aIndex].numFrames = aFramesList->Count(); + mRects[aIndex].frames->operator=(*aFramesList); +} + void SpaceManager::RectArray::RemoveAt(PRInt32 aIndex) { NS_PRECONDITION(aIndex < mCount, "bad index"); + BandRect* r = &mRects[aIndex]; + if (r->numFrames > 1) { + // Delete the void array of frames occupying the rect + delete r->frames; + } + + // Reduce the count and compact the array mCount--; if (aIndex < mCount) { - memmove(&mRects[aIndex], &mRects[aIndex + 1], (mCount - aIndex) * sizeof(nsBandRect)); + memmove(&mRects[aIndex], &mRects[aIndex + 1], (mCount - aIndex) * sizeof(BandRect)); } } void SpaceManager::RectArray::Clear() { - mCount = 0; + // Delete any void arrays used for the list of frames occupying a rect + BandRect* r = mRects; + while (mCount > 0) { + if (r->numFrames > 1) { + delete r->frames; + } + mCount--; + r++; + } + + NS_POSTCONDITION(0 == mCount, "bad count"); } diff --git a/layout/base/src/nsSpaceManager.h b/layout/base/src/nsSpaceManager.h index 0f7d80f33431..0ddb6dcae55e 100644 --- a/layout/base/src/nsSpaceManager.h +++ b/layout/base/src/nsSpaceManager.h @@ -42,15 +42,27 @@ public: nsBandData& aBandData) const; virtual PRBool AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); - virtual PRBool ReshapeRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); + virtual PRBool ResizeRectRegion(nsIFrame* aFrame, + nscoord aDeltaWidth, + nscoord aDeltaHeight, + AffectedEdge aEdge); virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); virtual PRBool RemoveRegion(nsIFrame* aFrame); virtual void ClearRegions(); protected: - struct nsBandRect : nsRect { - nsIFrame* frame; + struct BandRect : nsRect { + PRIntn numFrames; // number of frames occupying this rect + + union { + nsIFrame* frame; // single frame occupying the space + nsVoidArray* frames; // list of frames occupying the space + }; + + PRBool IsOccupiedBy(nsIFrame*); + void AddFrame(nsIFrame*); + void RemoveFrame(nsIFrame*); }; class RectArray { @@ -61,6 +73,7 @@ protected: // Functions to add rects void Append(const nsRect& aRect, nsIFrame* aFrame); void InsertAt(const nsRect& aRect, PRInt32 aIndex, nsIFrame* aFrame); + void InsertAt(const nsRect& aRect, PRInt32 aIndex, const nsVoidArray* aFramesList); void RemoveAt(PRInt32 aIndex); // Clear the list of rectangles @@ -71,7 +84,7 @@ protected: public: // Access to the underlying storage - nsBandRect* mRects; // y-x banded array of rectangles of unavailable space + BandRect* mRects; // y-x banded array of rectangles of unavailable space PRInt32 mCount; // current number of rects PRInt32 mMax; // capacity of rect array }; @@ -79,20 +92,20 @@ protected: nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space RectArray mRectArray; // y-x banded array of rectangles of unavailable space - RectArray mEmptyRects; // list of empty height rects + RectArray mEmptyRects; // list of empty rects protected: - PRBool GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const; - PRInt32 LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) const; - PRBool CoalesceBand(nsBandRect* aBand, PRInt32 aIndex); - void DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Height); - void AddRectToBand(nsBandRect* aBand, PRInt32 aIndex, + PRBool GetNextBand(BandRect*& aRect, PRInt32& aIndex) const; + PRInt32 LengthOfBand(const BandRect* aBand, PRInt32 aIndex) const; + PRBool CoalesceBand(BandRect* aBand, PRInt32 aIndex); + void DivideBand(BandRect* aBand, PRInt32 aIndex, nscoord aB1Height); + void AddRectToBand(BandRect* aBand, PRInt32 aIndex, const nsRect& aRect, nsIFrame* aFrame); - PRInt32 GetBandAvailableSpace(const nsBandRect* aBand, - PRInt32 aIndex, - nscoord aY, - const nsSize& aMaxSize, - nsBandData& aAvailableSpace) const; + PRInt32 GetBandAvailableSpace(const BandRect* aBand, + PRInt32 aIndex, + nscoord aY, + const nsSize& aMaxSize, + nsBandData& aAvailableSpace) const; private: SpaceManager(const SpaceManager&); // no implementation diff --git a/layout/generic/nsSpaceManager.cpp b/layout/generic/nsSpaceManager.cpp index fb50d54968b2..6a8549f4f19d 100644 --- a/layout/generic/nsSpaceManager.cpp +++ b/layout/generic/nsSpaceManager.cpp @@ -20,6 +20,7 @@ #include "nsRect.h" #include "nsSize.h" #include +#include "nsVoidArray.h" static NS_DEFINE_IID(kISpaceManagerIID, NS_ISPACEMANAGER_IID); @@ -71,11 +72,11 @@ nscoord SpaceManager::YMost() const * @param aMaxSize the size to use to constrain the band data * @param aAvailableBand */ -PRInt32 SpaceManager::GetBandAvailableSpace(const nsBandRect* aBand, - PRInt32 aIndex, - nscoord aY, - const nsSize& aMaxSize, - nsBandData& aBandData) const +PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, + PRInt32 aIndex, + nscoord aY, + const nsSize& aMaxSize, + nsBandData& aBandData) const { PRInt32 numRects = LengthOfBand(aBand, aIndex); nscoord localY = aY - mY; @@ -121,8 +122,14 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const nsBandRect* aBand, } // The rect represents unavailable space, so add another trapezoid - trapezoid->state = nsBandTrapezoid::smOccupied; - trapezoid->frame = aBand->frame; + if (1 == aBand->numFrames) { + trapezoid->state = nsBandTrapezoid::smOccupied; + trapezoid->frame = aBand->frame; + } else { + NS_ASSERTION(aBand->numFrames > 1, "unexpected frame count"); + trapezoid->state = nsBandTrapezoid::smOccupiedMultiple; + trapezoid->frames = aBand->frames; + } nscoord x = aBand->x; // The first band can straddle the clip rect @@ -179,7 +186,7 @@ PRInt32 SpaceManager::GetBandData(nscoord aYOffset, aBandData.trapezoids[0].frame = nsnull; } else { // Find the first band that contains the y-offset or is below the y-offset - nsBandRect* band = mRectArray.mRects; + BandRect* band = mRectArray.mRects; aBandData.count = 0; for (PRInt32 i = 0; i < mRectArray.mCount; ) { @@ -217,7 +224,7 @@ PRInt32 SpaceManager::GetBandData(nscoord aYOffset, * next band. If there is no next band then aRect is undefined * and aIndex is set to the number of rects in the rect array */ -PRBool SpaceManager::GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const +PRBool SpaceManager::GetNextBand(BandRect*& aRect, PRInt32& aIndex) const { nscoord topOfBand = aRect->y; @@ -241,7 +248,7 @@ PRBool SpaceManager::GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const * @param aBand the first rect in the band * @param aIndex aBand's index in the rect array */ -PRInt32 SpaceManager::LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) const +PRInt32 SpaceManager::LengthOfBand(const BandRect* aBand, PRInt32 aIndex) const { nscoord topOfBand = aBand->y; PRInt32 result = 1; @@ -268,13 +275,13 @@ PRInt32 SpaceManager::LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) cons * @param aRect the first rect in the band * @param aIndex aBand's index in the rect array */ -PRBool SpaceManager::CoalesceBand(nsBandRect* aBand, PRInt32 aIndex) +PRBool SpaceManager::CoalesceBand(BandRect* aBand, PRInt32 aIndex) { PRBool result = PR_FALSE; while ((aIndex + 1) < mRectArray.mCount) { // Is there another rect in this band? - nsBandRect* nextRect = aBand + 1; + BandRect* nextRect = aBand + 1; if (nextRect->y == aBand->y) { // The rects must not be overlapping @@ -313,7 +320,7 @@ PRBool SpaceManager::CoalesceBand(nsBandRect* aBand, PRInt32 aIndex) * @param aIndex aBand's index in the rect array * @param aB1Height the height of the new band to create */ -void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Height) +void SpaceManager::DivideBand(BandRect* aBand, PRInt32 aIndex, nscoord aB1Height) { NS_PRECONDITION(aB1Height < aBand->height, "bad height"); @@ -327,7 +334,11 @@ void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Heig // Insert a new bottom band nsRect r(aBand->x, aBand->y + aB1Height, aBand->width, aB2Height); - mRectArray.InsertAt(r, insertAt, aBand->frame); + if (aBand->numFrames > 1) { + mRectArray.InsertAt(r, insertAt, aBand->frames); + } else { + mRectArray.InsertAt(r, insertAt, aBand->frame); + } aBand = mRectArray.mRects + aIndex; // memory may have changed... // Adjust the height of the top band @@ -347,7 +358,7 @@ void SpaceManager::DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Heig * @param aRect the rect to add to the band. It's in world coordinates * @returns PR_TRUE if successful and PR_FALSE if this is the last band */ -void SpaceManager::AddRectToBand(nsBandRect* aBand, +void SpaceManager::AddRectToBand(BandRect* aBand, PRInt32 aIndex, const nsRect& aRect, nsIFrame* aFrame) @@ -426,8 +437,13 @@ void SpaceManager::AddRectToBand(nsBandRect* aBand, // Insert the new right half of the existing rect, and make it the current // rect aIndex++; - mRectArray.InsertAt(r1, aIndex, aBand->frame); + if (aBand->numFrames > 1) { + mRectArray.InsertAt(r1, aIndex, aBand->frames); + } else { + mRectArray.InsertAt(r1, aIndex, aBand->frame); + } aBand = mRectArray.mRects + aIndex; // memory may have changed... + aBand->AddFrame(aFrame); } // At this point the left edge of the new rect is the same as the left edge @@ -441,16 +457,16 @@ void SpaceManager::AddRectToBand(nsBandRect* aBand, // the new rect nsRect r1(rect.XMost(), aBand->y, aBand->XMost() - rect.XMost(), aBand->height); - // Modify the left half of the existing rect + // Modify the left half of the existing rect, and indicate the rect is + // shared aBand->width = r1.x - aBand->x; + aBand->AddFrame(aFrame); // Insert the new right half of the existing rect mRectArray.InsertAt(r1, aIndex, aBand->frame); aBand = mRectArray.mRects + aIndex; // memory may have changed... } - // XXX Mark the existing rect as being shared by the two frames... - if (rect.width == aBand->width) { // We're all done return; @@ -502,6 +518,13 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); + // Check if the rect is empty + if (aUnavailableSpace.IsEmpty()) { + // The rect doesn't consume any space so don't add the rect to the band data + mEmptyRects.Append(rect, aFrame); + return PR_TRUE; + } + // If there are no existing bands or this rect is below the bottommost band, // then add a new band if ((0 == mRectArray.mCount) || (rect.y >= mRectArray.YMost())) { @@ -511,7 +534,7 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS } // Examine each band looking for a band that intersects this rect - nsBandRect* band = mRectArray.mRects; + BandRect* band = mRectArray.mRects; for (PRInt32 i = 0; i < mRectArray.mCount; ) { // Compare the top edge of this rect with the top edge of the band @@ -597,9 +620,12 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS return PR_TRUE; } -PRBool SpaceManager::ReshapeRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) +PRBool SpaceManager::ResizeRectRegion(nsIFrame* aFrame, + nscoord aDeltaWidth, + nscoord aDeltaHeight, + AffectedEdge aEdge) { - NS_NOTYETIMPLEMENTED("offseting a region"); + NS_NOTYETIMPLEMENTED("resizing a region"); return PR_FALSE; } @@ -613,16 +639,23 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) { PRBool result = PR_FALSE; - // Walk the list of rects and remove those tagged with aFrame. - // XXX We need to properly handle overlapped rects - for (PRInt32 i = 0; i < mRectArray.mCount; i++) { - if (mRectArray.mRects[i].frame == aFrame) { - mRectArray.RemoveAt(i); + // Walk the list of rects and remove those rects tagged with aFrame. + for (PRInt32 i = 0; i < mRectArray.mCount;) { + BandRect* r = &mRectArray.mRects[i]; + + if (r->IsOccupiedBy(aFrame)) { + if (r->numFrames > 1) { + r->RemoveFrame(aFrame); + i++; + } else { + mRectArray.RemoveAt(i); + } result = PR_TRUE; } } - // XXX We should try and coalesce adjacent bands... + // XXX We should try and coalesce adjoining rects within a band, and + // adjacent bands as well... return result; } @@ -631,6 +664,59 @@ void SpaceManager::ClearRegions() mRectArray.Clear(); } +///////////////////////////////////////////////////////////////////////////// +// BandRect + +PRBool SpaceManager::BandRect::IsOccupiedBy(nsIFrame* aFrame) +{ + 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; +} + +void SpaceManager::BandRect::AddFrame(nsIFrame* aFrame) +{ + if (1 == numFrames) { + nsIFrame* f = frame; + frames = new nsVoidArray; + frames->AppendElement(f); + } + + numFrames++; + frames->AppendElement(aFrame); + NS_POSTCONDITION(frames->Count() == numFrames, "bad frame count"); +} + +void SpaceManager::BandRect::RemoveFrame(nsIFrame* aFrame) +{ + NS_PRECONDITION(numFrames > 1, "only one frame"); + frames->RemoveElement(aFrame); + numFrames--; + + if (1 == numFrames) { + nsIFrame* f = (nsIFrame*)frames->ElementAt(0); + + delete frames; + frame = f; + } +} + ///////////////////////////////////////////////////////////////////////////// // RectArray @@ -642,6 +728,16 @@ SpaceManager::RectArray::RectArray() SpaceManager::RectArray::~RectArray() { + // Delete any void arrays used for the list of frames occupying a rect + BandRect* r = mRects; + for (PRInt32 n = mCount; n > 0; n--) { + if (r->numFrames > 1) { + delete r->frames; + } + r++; + } + + // Free the space for the rect array if (nsnull != mRects) { free(mRects); } @@ -657,7 +753,7 @@ void SpaceManager::RectArray::Append(const nsRect& aRect, nsIFrame* aFrame) // Ensure there's enough capacity if (mCount >= mMax) { mMax += 8; - mRects = (nsBandRect*)realloc(mRects, mMax * sizeof(nsBandRect)); + mRects = (BandRect*)realloc(mRects, mMax * sizeof(BandRect)); } mRects[mCount].x = aRect.x; @@ -665,6 +761,7 @@ void SpaceManager::RectArray::Append(const nsRect& aRect, nsIFrame* aFrame) mRects[mCount].width = aRect.width; mRects[mCount].height = aRect.height; mRects[mCount].frame = aFrame; + mRects[mCount].numFrames = 1; mCount++; } @@ -677,29 +774,58 @@ void SpaceManager::RectArray::InsertAt(const nsRect& aRect, // Ensure there's enough capacity if (mCount >= mMax) { mMax += 8; - mRects = (nsBandRect*)realloc(mRects, mMax * sizeof(nsBandRect)); + mRects = (BandRect*)realloc(mRects, mMax * sizeof(BandRect)); } - memmove(&mRects[aIndex + 1], &mRects[aIndex], (mCount - aIndex) * sizeof(nsBandRect)); + memmove(&mRects[aIndex + 1], &mRects[aIndex], (mCount - aIndex) * sizeof(BandRect)); mRects[aIndex].x = aRect.x; mRects[aIndex].y = aRect.y; mRects[aIndex].width = aRect.width; mRects[aIndex].height = aRect.height; mRects[aIndex].frame = aFrame; + mRects[aIndex].numFrames = 1; mCount++; } +void SpaceManager::RectArray::InsertAt(const nsRect& aRect, + PRInt32 aIndex, + const nsVoidArray* aFramesList) +{ + NS_PRECONDITION(aIndex <= mCount, "bad index"); // no holes in the array + + InsertAt(aRect, aIndex, (nsIFrame*)nsnull); + mRects[aIndex].numFrames = aFramesList->Count(); + mRects[aIndex].frames->operator=(*aFramesList); +} + void SpaceManager::RectArray::RemoveAt(PRInt32 aIndex) { NS_PRECONDITION(aIndex < mCount, "bad index"); + BandRect* r = &mRects[aIndex]; + if (r->numFrames > 1) { + // Delete the void array of frames occupying the rect + delete r->frames; + } + + // Reduce the count and compact the array mCount--; if (aIndex < mCount) { - memmove(&mRects[aIndex], &mRects[aIndex + 1], (mCount - aIndex) * sizeof(nsBandRect)); + memmove(&mRects[aIndex], &mRects[aIndex + 1], (mCount - aIndex) * sizeof(BandRect)); } } void SpaceManager::RectArray::Clear() { - mCount = 0; + // Delete any void arrays used for the list of frames occupying a rect + BandRect* r = mRects; + while (mCount > 0) { + if (r->numFrames > 1) { + delete r->frames; + } + mCount--; + r++; + } + + NS_POSTCONDITION(0 == mCount, "bad count"); } diff --git a/layout/generic/nsSpaceManager.h b/layout/generic/nsSpaceManager.h index 0f7d80f33431..0ddb6dcae55e 100644 --- a/layout/generic/nsSpaceManager.h +++ b/layout/generic/nsSpaceManager.h @@ -42,15 +42,27 @@ public: nsBandData& aBandData) const; virtual PRBool AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); - virtual PRBool ReshapeRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); + virtual PRBool ResizeRectRegion(nsIFrame* aFrame, + nscoord aDeltaWidth, + nscoord aDeltaHeight, + AffectedEdge aEdge); virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); virtual PRBool RemoveRegion(nsIFrame* aFrame); virtual void ClearRegions(); protected: - struct nsBandRect : nsRect { - nsIFrame* frame; + struct BandRect : nsRect { + PRIntn numFrames; // number of frames occupying this rect + + union { + nsIFrame* frame; // single frame occupying the space + nsVoidArray* frames; // list of frames occupying the space + }; + + PRBool IsOccupiedBy(nsIFrame*); + void AddFrame(nsIFrame*); + void RemoveFrame(nsIFrame*); }; class RectArray { @@ -61,6 +73,7 @@ protected: // Functions to add rects void Append(const nsRect& aRect, nsIFrame* aFrame); void InsertAt(const nsRect& aRect, PRInt32 aIndex, nsIFrame* aFrame); + void InsertAt(const nsRect& aRect, PRInt32 aIndex, const nsVoidArray* aFramesList); void RemoveAt(PRInt32 aIndex); // Clear the list of rectangles @@ -71,7 +84,7 @@ protected: public: // Access to the underlying storage - nsBandRect* mRects; // y-x banded array of rectangles of unavailable space + BandRect* mRects; // y-x banded array of rectangles of unavailable space PRInt32 mCount; // current number of rects PRInt32 mMax; // capacity of rect array }; @@ -79,20 +92,20 @@ protected: nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space RectArray mRectArray; // y-x banded array of rectangles of unavailable space - RectArray mEmptyRects; // list of empty height rects + RectArray mEmptyRects; // list of empty rects protected: - PRBool GetNextBand(nsBandRect*& aRect, PRInt32& aIndex) const; - PRInt32 LengthOfBand(const nsBandRect* aBand, PRInt32 aIndex) const; - PRBool CoalesceBand(nsBandRect* aBand, PRInt32 aIndex); - void DivideBand(nsBandRect* aBand, PRInt32 aIndex, nscoord aB1Height); - void AddRectToBand(nsBandRect* aBand, PRInt32 aIndex, + PRBool GetNextBand(BandRect*& aRect, PRInt32& aIndex) const; + PRInt32 LengthOfBand(const BandRect* aBand, PRInt32 aIndex) const; + PRBool CoalesceBand(BandRect* aBand, PRInt32 aIndex); + void DivideBand(BandRect* aBand, PRInt32 aIndex, nscoord aB1Height); + void AddRectToBand(BandRect* aBand, PRInt32 aIndex, const nsRect& aRect, nsIFrame* aFrame); - PRInt32 GetBandAvailableSpace(const nsBandRect* aBand, - PRInt32 aIndex, - nscoord aY, - const nsSize& aMaxSize, - nsBandData& aAvailableSpace) const; + PRInt32 GetBandAvailableSpace(const BandRect* aBand, + PRInt32 aIndex, + nscoord aY, + const nsSize& aMaxSize, + nsBandData& aAvailableSpace) const; private: SpaceManager(const SpaceManager&); // no implementation