diff --git a/layout/base/public/nsISpaceManager.h b/layout/base/public/nsISpaceManager.h index faaa9b21cadf..597b89026e0f 100644 --- a/layout/base/public/nsISpaceManager.h +++ b/layout/base/public/nsISpaceManager.h @@ -73,7 +73,8 @@ struct nsBandData { }; /** - * Interface for dealing with bands of available space. + * Interface for dealing with bands of available space. The space manager defines a coordinate + * space with an origin at (0, 0) that grows down and to the right. * * @see nsIRunaround */ @@ -138,6 +139,9 @@ public: * * Returns PR_TRUE if successful and PR_FALSE otherwise, e.g. there is already * a region tagged with aFrame + * + * When translated to world coordinates the origin MUST be within the defined + * coordinate space, i.e. the x-offset and y-offset must be >= 0 */ virtual PRBool AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) = 0; @@ -147,7 +151,8 @@ public: * 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 + * tagged with aFrame, or the new offset when translated to world coordinates is + * outside the defined coordinate space */ enum AffectedEdge {LeftEdge, RightEdge}; virtual PRBool ResizeRectRegion(nsIFrame* aFrame, @@ -159,7 +164,8 @@ public: * Offset the region associated with aFrame by the specified amount. * * Returns PR_TRUE if successful and PR_FALSE otherwise, e.g. there is no region - * tagged with aFrame + * tagged with aFrame, or the new offset when translated to world coordinates + * is outside the defined coordinate space */ virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy) = 0; diff --git a/layout/base/src/nsSpaceManager.cpp b/layout/base/src/nsSpaceManager.cpp index e93811738048..2d39ec665a3c 100644 --- a/layout/base/src/nsSpaceManager.cpp +++ b/layout/base/src/nsSpaceManager.cpp @@ -40,6 +40,33 @@ NS_RemoveFrameInfoEntries(PLHashEntry* he, PRIntn i, void* arg) return HT_ENUMERATE_REMOVE; } +///////////////////////////////////////////////////////////////////////////// +// BandList + +SpaceManager::BandList::BandList() + : BandRect(-1, -1, -1, -1, (nsIFrame*)nsnull) +{ + PR_INIT_CLIST(this); + numFrames = 0; +} + +void SpaceManager::BandList::Clear() +{ + if (!IsEmpty()) { + BandRect* bandRect = Head(); + + while (bandRect != this) { + BandRect* next = bandRect->Next(); + + delete bandRect; + bandRect = next; + } + + PR_INIT_CLIST(this); + } +} + + ///////////////////////////////////////////////////////////////////////////// // nsSpaceManager @@ -47,7 +74,6 @@ SpaceManager::SpaceManager(nsIFrame* aFrame) : mFrame(aFrame) { NS_INIT_REFCNT(); - PR_INIT_CLIST(&mBandList); mX = mY = 0; mFrameInfoMap = nsnull; } @@ -61,26 +87,10 @@ void SpaceManager::ClearFrameInfo() } } -void SpaceManager::ClearBandRects() -{ - if (!PR_CLIST_IS_EMPTY(&mBandList)) { - BandRect* bandRect = (BandRect*)PR_LIST_HEAD(&mBandList); - - while (bandRect != &mBandList) { - BandRect* next = (BandRect*)PR_NEXT_LINK(bandRect); - - delete bandRect; - bandRect = next; - } - - PR_INIT_CLIST(&mBandList); - } -} - SpaceManager::~SpaceManager() { + mBandList.Clear(); ClearFrameInfo(); - ClearBandRects(); } NS_IMPL_ISUPPORTS(SpaceManager, kISpaceManagerIID); @@ -106,10 +116,10 @@ nscoord SpaceManager::YMost() const { nscoord yMost; - if (PR_CLIST_IS_EMPTY(&mBandList)) { + if (mBandList.IsEmpty()) { yMost = 0; } else { - BandRect* lastRect = (BandRect*)PR_LIST_TAIL(&mBandList); + BandRect* lastRect = mBandList.Tail(); yMost = lastRect->bottom; } @@ -147,7 +157,7 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, } // Get the next rect in the band - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); } // This is used to track the current x-location within the band. This is in @@ -202,7 +212,7 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, left = aBand->right; // Move to the next rect within the band - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); } // No more rects left in the band. If we haven't yet reached the right edge, @@ -237,8 +247,8 @@ 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 - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); - NS_ASSERTION(band != &mBandList, "no bands"); + NS_ASSERTION(!mBandList.IsEmpty(), "no bands"); + BandRect* band = mBandList.Head(); aBandData.count = 0; while (nsnull != band) { @@ -276,7 +286,7 @@ SpaceManager::BandRect* SpaceManager::GetNextBand(const BandRect* aBandRect) con { nscoord topOfBand = aBandRect->top; - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); while (aBandRect != &mBandList) { // Check whether this rect is part of the same band if (aBandRect->top != topOfBand) { @@ -284,7 +294,7 @@ SpaceManager::BandRect* SpaceManager::GetNextBand(const BandRect* aBandRect) con return (BandRect*)aBandRect; } - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); } // No bands left @@ -313,22 +323,89 @@ void SpaceManager::DivideBand(BandRect* aBandRect, nscoord aBottom) BandRect* bottomBandRect = aBandRect->SplitVertically(aBottom); // Insert the new bottom part - PR_INSERT_BEFORE(bottomBandRect, nextBand); + nextBand->InsertBefore(bottomBandRect); // Move to the next rect in the band - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); } } +PRBool SpaceManager::CanJoinBands(BandRect* aBand, BandRect* aPrevBand) +{ + 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; +} + /** * Tries to join the two adjacent bands. Returns PR_TRUE if successful and * PR_FALSE otherwise * - * If the two bands are joined the previous band is the the band that's deleted + * If the two bands are joined, the previous band is the the band that's deleted */ -PRBool SpaceManager::JoinBands(BandRect* aBandRect, BandRect* aPrevBand) +PRBool SpaceManager::JoinBands(BandRect* aBand, BandRect* aPrevBand) { - NS_NOTYETIMPLEMENTED("joining bands"); + if (CanJoinBands(aBand, aPrevBand)) { + nscoord topOfPrevBand = aPrevBand->top; + + while (topOfPrevBand == aPrevBand->top) { + // 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; + } + return PR_FALSE; } @@ -367,7 +444,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, if (aBandRect->right <= aBand->left) { // No, the new rect is completely to the left of the existing rect // (case #1). Insert a new rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); return; } @@ -379,7 +456,9 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // Insert the part of the new rect that's to the left of the existing // rect as a new band rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); + + // Continue below with the part that overlaps the existing rect aBandRect = r1; } else { @@ -389,15 +468,15 @@ void SpaceManager::AddRectToBand(BandRect* aBand, BandRect* r1 = aBand->SplitHorizontally(aBandRect->right); // Insert the new right half of the existing rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); } // Insert the part of the new rect that's to the left of the existing // rect aBandRect->right = aBand->left; - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); - // Mark the part of the existing rect that overlaps as being shared + // Mark the existing rect as shared aBand->AddFrame(aBandRect->frame); return; } @@ -419,7 +498,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, 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 - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); continue; } @@ -429,7 +508,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // Insert the new right half of the existing rect, and make it the current // rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); aBand = r1; } @@ -445,7 +524,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, BandRect* r1 = aBand->SplitHorizontally(aBandRect->right); // Insert the new right half of the existing rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); // Mark the overlap as being shared aBand->AddFrame(aBandRect->frame); @@ -464,14 +543,14 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // 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; - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); continue; } } - } while ((aBand != &mBandList) && (aBand->top == topOfBand)); + } while (aBand->top == topOfBand); // Insert a new rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); } // When comparing a rect to a band there are seven cases to consider. @@ -506,13 +585,13 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) // If there are no existing bands or this rect is below the bottommost // band, then add a new band if (aBandRect->top >= YMost()) { - PR_APPEND_LINK(aBandRect, &mBandList); + mBandList.Append(aBandRect); return; } // Examine each band looking for a band that intersects this rect - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); - NS_ASSERTION(nsnull != band, "no bands"); + NS_ASSERTION(!mBandList.IsEmpty(), "no bands"); + BandRect* band = mBandList.Head(); while (nsnull != band) { // Compare the top edge of this rect with the top edge of the band @@ -522,7 +601,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) if (aBandRect->bottom <= band->top) { // Case #1. This rect is completely above the band, so insert a // new band before the current band - PR_INSERT_BEFORE(aBandRect, band); + band->InsertBefore(aBandRect); break; // we're all done } @@ -533,7 +612,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) aBandRect->frame); // Insert bandRect1 as a new band - PR_INSERT_BEFORE(bandRect1, band); + band->InsertBefore(bandRect1); // Modify this rect to exclude the part above the band aBandRect->top = band->top; @@ -587,7 +666,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) band = GetNextBand(band); if (nsnull == band) { // Append a new bottommost band - PR_APPEND_LINK(aBandRect, &mBandList); + mBandList.Append(aBandRect); break; } } @@ -610,6 +689,12 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); + // 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; + } + // Create a frame info structure frameInfo = CreateFrameInfo(aFrame, rect); @@ -632,17 +717,32 @@ PRBool SpaceManager::ResizeRectRegion(nsIFrame* aFrame, nscoord aDeltaHeight, AffectedEdge aEdge) { - NS_NOTYETIMPLEMENTED("resizing a region"); - return PR_FALSE; + // 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); } -PRBool SpaceManager::OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy) -{ - NS_NOTYETIMPLEMENTED("offseting a region"); - return PR_FALSE; -} - -PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) +PRBool SpaceManager::OffsetRegion(nsIFrame* aFrame, nscoord aDx, nscoord aDy) { // Get the frame info associated with with aFrame FrameInfo* frameInfo = GetFrameInfoFor(aFrame); @@ -652,11 +752,35 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* 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); +} + +PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) +{ + // Get the frame info associated with aFrame + FrameInfo* frameInfo = GetFrameInfoFor(aFrame); + + if (nsnull == frameInfo) { + NS_WARNING("no region associated with aFrame"); + return PR_FALSE; + } + if (!frameInfo->rect.IsEmpty()) { - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); + BandRect* band = mBandList.Head(); + NS_ASSERTION(band != &mBandList, "no band rects"); BandRect* prevBand = nsnull; PRBool prevFoundMatchingRect = PR_FALSE; - NS_ASSERTION(band != &mBandList, "no band rects"); // Iterate each band looking for rects tagged with aFrame while (nsnull != band) { @@ -680,7 +804,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) isSharedRect = PR_TRUE; } else { // The rect isn't shared so just delete it - PR_REMOVE_LINK(rect); + rect->Remove(); } // Remember that we found a matching rect in this band @@ -696,7 +820,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) if ((prevRect->right == rect->left) && (prevRect->HasSameFrameList(rect))) { // Modify the current rect's left edge, and delete the previous rect rect->left = prevRect->left; - PR_REMOVE_LINK(prevRect); + prevRect->Remove(); delete prevRect; } } @@ -704,7 +828,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) // Get the next rect in the band prevRect = rect; prevIsSharedRect = isSharedRect; - rect = (BandRect*)PR_NEXT_LINK(rect); + rect = rect->Next(); if (rect == &mBandList) { // No bands left @@ -734,7 +858,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) void SpaceManager::ClearRegions() { ClearFrameInfo(); - ClearBandRects(); + mBandList.Clear(); } SpaceManager::FrameInfo* SpaceManager::GetFrameInfoFor(nsIFrame* aFrame) @@ -905,15 +1029,16 @@ void SpaceManager::BandRect::RemoveFrame(const nsIFrame* aFrame) PRBool SpaceManager::BandRect::HasSameFrameList(const BandRect* aBandRect) const { + PRBool result; + // Check whether they're occupied by the same number of frames if (numFrames != aBandRect->numFrames) { - return PR_FALSE; - } - - // Check that the list of frames matches - if (1 == numFrames) { - return frame == aBandRect->frame; + result = PR_FALSE; + } else if (1 == numFrames) { + result = frame == aBandRect->frame; } else { + result = PR_TRUE; + // For each frame occupying this band rect check whether it also occupies // aBandRect PRInt32 count = frames->Count(); @@ -921,10 +1046,11 @@ PRBool SpaceManager::BandRect::HasSameFrameList(const BandRect* aBandRect) const nsIFrame* f = (nsIFrame*)frames->ElementAt(i); if (-1 == aBandRect->frames->IndexOf(f)) { - return PR_FALSE; + result = PR_FALSE; + break; } } - - return PR_TRUE; } + + return result; } diff --git a/layout/base/src/nsSpaceManager.h b/layout/base/src/nsSpaceManager.h index 00135f5b842b..600752cc1db5 100644 --- a/layout/base/src/nsSpaceManager.h +++ b/layout/base/src/nsSpaceManager.h @@ -50,7 +50,7 @@ public: nscoord aDeltaWidth, nscoord aDeltaHeight, AffectedEdge aEdge); - virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); + virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord aDx, nscoord aDy); virtual PRBool RemoveRegion(nsIFrame* aFrame); virtual void ClearRegions(); @@ -83,14 +83,25 @@ protected: nsVoidArray*); ~BandRect(); + // List operations + BandRect* Next() const {return (BandRect*)PR_NEXT_LINK(this);} + BandRect* Prev() const {return (BandRect*)PR_PREV_LINK(this);} + void InsertBefore(BandRect* aBandRect) {PR_INSERT_BEFORE(aBandRect, this);} + void InsertAfter(BandRect* aBandRect) {PR_INSERT_AFTER(aBandRect, this);} + void Remove() {PR_REMOVE_LINK(this);} + // Split the band rect into two vertically, with this band rect becoming // the top part, and a new band rect being allocated and returned for the // bottom part + // + // Does not insert the new band rect into the linked list BandRect* SplitVertically(nscoord aBottom); // Split the band rect into two horizontally, with this band rect becoming // the left part, and a new band rect being allocated and returned for the // right part + // + // Does not insert the new band rect into the linked list BandRect* SplitHorizontally(nscoord aRight); // Accessor functions @@ -100,9 +111,25 @@ protected: PRBool HasSameFrameList(const BandRect* aBandRect) const; }; + // Circular linked list of band rects + struct BandList : BandRect { + BandList(); + + // Accessors + PRBool IsEmpty() const {return PR_CLIST_IS_EMPTY((PRCListStr*)this);} + BandRect* Head() const {return (BandRect*)PR_LIST_HEAD(this);} + BandRect* Tail() const {return (BandRect*)PR_LIST_TAIL(this);} + + // Operations + void Append(BandRect* aBandRect) {PR_APPEND_LINK(aBandRect, this);} + + // Remove and delete all the band rects in the list + void Clear(); + }; + nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space - PRCListStr mBandList; + BandList mBandList; // header for circular linked list of band rects PLHashTable* mFrameInfoMap; protected: @@ -115,6 +142,7 @@ protected: BandRect* GetNextBand(const BandRect* aBandRect) const; void DivideBand(BandRect* aBand, nscoord aBottom); + PRBool CanJoinBands(BandRect* aBand, BandRect* aPrevBand); PRBool JoinBands(BandRect* aBand, BandRect* aPrevBand); void AddRectToBand(BandRect* aBand, BandRect* aBandRect); void InsertBandRect(BandRect* aBandRect); diff --git a/layout/generic/nsSpaceManager.cpp b/layout/generic/nsSpaceManager.cpp index e93811738048..2d39ec665a3c 100644 --- a/layout/generic/nsSpaceManager.cpp +++ b/layout/generic/nsSpaceManager.cpp @@ -40,6 +40,33 @@ NS_RemoveFrameInfoEntries(PLHashEntry* he, PRIntn i, void* arg) return HT_ENUMERATE_REMOVE; } +///////////////////////////////////////////////////////////////////////////// +// BandList + +SpaceManager::BandList::BandList() + : BandRect(-1, -1, -1, -1, (nsIFrame*)nsnull) +{ + PR_INIT_CLIST(this); + numFrames = 0; +} + +void SpaceManager::BandList::Clear() +{ + if (!IsEmpty()) { + BandRect* bandRect = Head(); + + while (bandRect != this) { + BandRect* next = bandRect->Next(); + + delete bandRect; + bandRect = next; + } + + PR_INIT_CLIST(this); + } +} + + ///////////////////////////////////////////////////////////////////////////// // nsSpaceManager @@ -47,7 +74,6 @@ SpaceManager::SpaceManager(nsIFrame* aFrame) : mFrame(aFrame) { NS_INIT_REFCNT(); - PR_INIT_CLIST(&mBandList); mX = mY = 0; mFrameInfoMap = nsnull; } @@ -61,26 +87,10 @@ void SpaceManager::ClearFrameInfo() } } -void SpaceManager::ClearBandRects() -{ - if (!PR_CLIST_IS_EMPTY(&mBandList)) { - BandRect* bandRect = (BandRect*)PR_LIST_HEAD(&mBandList); - - while (bandRect != &mBandList) { - BandRect* next = (BandRect*)PR_NEXT_LINK(bandRect); - - delete bandRect; - bandRect = next; - } - - PR_INIT_CLIST(&mBandList); - } -} - SpaceManager::~SpaceManager() { + mBandList.Clear(); ClearFrameInfo(); - ClearBandRects(); } NS_IMPL_ISUPPORTS(SpaceManager, kISpaceManagerIID); @@ -106,10 +116,10 @@ nscoord SpaceManager::YMost() const { nscoord yMost; - if (PR_CLIST_IS_EMPTY(&mBandList)) { + if (mBandList.IsEmpty()) { yMost = 0; } else { - BandRect* lastRect = (BandRect*)PR_LIST_TAIL(&mBandList); + BandRect* lastRect = mBandList.Tail(); yMost = lastRect->bottom; } @@ -147,7 +157,7 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, } // Get the next rect in the band - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); } // This is used to track the current x-location within the band. This is in @@ -202,7 +212,7 @@ PRInt32 SpaceManager::GetBandAvailableSpace(const BandRect* aBand, left = aBand->right; // Move to the next rect within the band - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); } // No more rects left in the band. If we haven't yet reached the right edge, @@ -237,8 +247,8 @@ 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 - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); - NS_ASSERTION(band != &mBandList, "no bands"); + NS_ASSERTION(!mBandList.IsEmpty(), "no bands"); + BandRect* band = mBandList.Head(); aBandData.count = 0; while (nsnull != band) { @@ -276,7 +286,7 @@ SpaceManager::BandRect* SpaceManager::GetNextBand(const BandRect* aBandRect) con { nscoord topOfBand = aBandRect->top; - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); while (aBandRect != &mBandList) { // Check whether this rect is part of the same band if (aBandRect->top != topOfBand) { @@ -284,7 +294,7 @@ SpaceManager::BandRect* SpaceManager::GetNextBand(const BandRect* aBandRect) con return (BandRect*)aBandRect; } - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); } // No bands left @@ -313,22 +323,89 @@ void SpaceManager::DivideBand(BandRect* aBandRect, nscoord aBottom) BandRect* bottomBandRect = aBandRect->SplitVertically(aBottom); // Insert the new bottom part - PR_INSERT_BEFORE(bottomBandRect, nextBand); + nextBand->InsertBefore(bottomBandRect); // Move to the next rect in the band - aBandRect = (BandRect*)PR_NEXT_LINK(aBandRect); + aBandRect = aBandRect->Next(); } } +PRBool SpaceManager::CanJoinBands(BandRect* aBand, BandRect* aPrevBand) +{ + 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; +} + /** * Tries to join the two adjacent bands. Returns PR_TRUE if successful and * PR_FALSE otherwise * - * If the two bands are joined the previous band is the the band that's deleted + * If the two bands are joined, the previous band is the the band that's deleted */ -PRBool SpaceManager::JoinBands(BandRect* aBandRect, BandRect* aPrevBand) +PRBool SpaceManager::JoinBands(BandRect* aBand, BandRect* aPrevBand) { - NS_NOTYETIMPLEMENTED("joining bands"); + if (CanJoinBands(aBand, aPrevBand)) { + nscoord topOfPrevBand = aPrevBand->top; + + while (topOfPrevBand == aPrevBand->top) { + // 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; + } + return PR_FALSE; } @@ -367,7 +444,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, if (aBandRect->right <= aBand->left) { // No, the new rect is completely to the left of the existing rect // (case #1). Insert a new rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); return; } @@ -379,7 +456,9 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // Insert the part of the new rect that's to the left of the existing // rect as a new band rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); + + // Continue below with the part that overlaps the existing rect aBandRect = r1; } else { @@ -389,15 +468,15 @@ void SpaceManager::AddRectToBand(BandRect* aBand, BandRect* r1 = aBand->SplitHorizontally(aBandRect->right); // Insert the new right half of the existing rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); } // Insert the part of the new rect that's to the left of the existing // rect aBandRect->right = aBand->left; - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); - // Mark the part of the existing rect that overlaps as being shared + // Mark the existing rect as shared aBand->AddFrame(aBandRect->frame); return; } @@ -419,7 +498,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, 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 - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); continue; } @@ -429,7 +508,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // Insert the new right half of the existing rect, and make it the current // rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); aBand = r1; } @@ -445,7 +524,7 @@ void SpaceManager::AddRectToBand(BandRect* aBand, BandRect* r1 = aBand->SplitHorizontally(aBandRect->right); // Insert the new right half of the existing rect - PR_INSERT_AFTER(r1, aBand); + aBand->InsertAfter(r1); // Mark the overlap as being shared aBand->AddFrame(aBandRect->frame); @@ -464,14 +543,14 @@ void SpaceManager::AddRectToBand(BandRect* aBand, // 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; - aBand = (BandRect*)PR_NEXT_LINK(aBand); + aBand = aBand->Next(); continue; } } - } while ((aBand != &mBandList) && (aBand->top == topOfBand)); + } while (aBand->top == topOfBand); // Insert a new rect - PR_INSERT_BEFORE(aBandRect, aBand); + aBand->InsertBefore(aBandRect); } // When comparing a rect to a band there are seven cases to consider. @@ -506,13 +585,13 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) // If there are no existing bands or this rect is below the bottommost // band, then add a new band if (aBandRect->top >= YMost()) { - PR_APPEND_LINK(aBandRect, &mBandList); + mBandList.Append(aBandRect); return; } // Examine each band looking for a band that intersects this rect - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); - NS_ASSERTION(nsnull != band, "no bands"); + NS_ASSERTION(!mBandList.IsEmpty(), "no bands"); + BandRect* band = mBandList.Head(); while (nsnull != band) { // Compare the top edge of this rect with the top edge of the band @@ -522,7 +601,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) if (aBandRect->bottom <= band->top) { // Case #1. This rect is completely above the band, so insert a // new band before the current band - PR_INSERT_BEFORE(aBandRect, band); + band->InsertBefore(aBandRect); break; // we're all done } @@ -533,7 +612,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) aBandRect->frame); // Insert bandRect1 as a new band - PR_INSERT_BEFORE(bandRect1, band); + band->InsertBefore(bandRect1); // Modify this rect to exclude the part above the band aBandRect->top = band->top; @@ -587,7 +666,7 @@ void SpaceManager::InsertBandRect(BandRect* aBandRect) band = GetNextBand(band); if (nsnull == band) { // Append a new bottommost band - PR_APPEND_LINK(aBandRect, &mBandList); + mBandList.Append(aBandRect); break; } } @@ -610,6 +689,12 @@ PRBool SpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableS nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); + // 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; + } + // Create a frame info structure frameInfo = CreateFrameInfo(aFrame, rect); @@ -632,17 +717,32 @@ PRBool SpaceManager::ResizeRectRegion(nsIFrame* aFrame, nscoord aDeltaHeight, AffectedEdge aEdge) { - NS_NOTYETIMPLEMENTED("resizing a region"); - return PR_FALSE; + // 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); } -PRBool SpaceManager::OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy) -{ - NS_NOTYETIMPLEMENTED("offseting a region"); - return PR_FALSE; -} - -PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) +PRBool SpaceManager::OffsetRegion(nsIFrame* aFrame, nscoord aDx, nscoord aDy) { // Get the frame info associated with with aFrame FrameInfo* frameInfo = GetFrameInfoFor(aFrame); @@ -652,11 +752,35 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* 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); +} + +PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) +{ + // Get the frame info associated with aFrame + FrameInfo* frameInfo = GetFrameInfoFor(aFrame); + + if (nsnull == frameInfo) { + NS_WARNING("no region associated with aFrame"); + return PR_FALSE; + } + if (!frameInfo->rect.IsEmpty()) { - BandRect* band = (BandRect*)PR_LIST_HEAD(&mBandList); + BandRect* band = mBandList.Head(); + NS_ASSERTION(band != &mBandList, "no band rects"); BandRect* prevBand = nsnull; PRBool prevFoundMatchingRect = PR_FALSE; - NS_ASSERTION(band != &mBandList, "no band rects"); // Iterate each band looking for rects tagged with aFrame while (nsnull != band) { @@ -680,7 +804,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) isSharedRect = PR_TRUE; } else { // The rect isn't shared so just delete it - PR_REMOVE_LINK(rect); + rect->Remove(); } // Remember that we found a matching rect in this band @@ -696,7 +820,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) if ((prevRect->right == rect->left) && (prevRect->HasSameFrameList(rect))) { // Modify the current rect's left edge, and delete the previous rect rect->left = prevRect->left; - PR_REMOVE_LINK(prevRect); + prevRect->Remove(); delete prevRect; } } @@ -704,7 +828,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) // Get the next rect in the band prevRect = rect; prevIsSharedRect = isSharedRect; - rect = (BandRect*)PR_NEXT_LINK(rect); + rect = rect->Next(); if (rect == &mBandList) { // No bands left @@ -734,7 +858,7 @@ PRBool SpaceManager::RemoveRegion(nsIFrame* aFrame) void SpaceManager::ClearRegions() { ClearFrameInfo(); - ClearBandRects(); + mBandList.Clear(); } SpaceManager::FrameInfo* SpaceManager::GetFrameInfoFor(nsIFrame* aFrame) @@ -905,15 +1029,16 @@ void SpaceManager::BandRect::RemoveFrame(const nsIFrame* aFrame) PRBool SpaceManager::BandRect::HasSameFrameList(const BandRect* aBandRect) const { + PRBool result; + // Check whether they're occupied by the same number of frames if (numFrames != aBandRect->numFrames) { - return PR_FALSE; - } - - // Check that the list of frames matches - if (1 == numFrames) { - return frame == aBandRect->frame; + result = PR_FALSE; + } else if (1 == numFrames) { + result = frame == aBandRect->frame; } else { + result = PR_TRUE; + // For each frame occupying this band rect check whether it also occupies // aBandRect PRInt32 count = frames->Count(); @@ -921,10 +1046,11 @@ PRBool SpaceManager::BandRect::HasSameFrameList(const BandRect* aBandRect) const nsIFrame* f = (nsIFrame*)frames->ElementAt(i); if (-1 == aBandRect->frames->IndexOf(f)) { - return PR_FALSE; + result = PR_FALSE; + break; } } - - return PR_TRUE; } + + return result; } diff --git a/layout/generic/nsSpaceManager.h b/layout/generic/nsSpaceManager.h index 00135f5b842b..600752cc1db5 100644 --- a/layout/generic/nsSpaceManager.h +++ b/layout/generic/nsSpaceManager.h @@ -50,7 +50,7 @@ public: nscoord aDeltaWidth, nscoord aDeltaHeight, AffectedEdge aEdge); - virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); + virtual PRBool OffsetRegion(nsIFrame* aFrame, nscoord aDx, nscoord aDy); virtual PRBool RemoveRegion(nsIFrame* aFrame); virtual void ClearRegions(); @@ -83,14 +83,25 @@ protected: nsVoidArray*); ~BandRect(); + // List operations + BandRect* Next() const {return (BandRect*)PR_NEXT_LINK(this);} + BandRect* Prev() const {return (BandRect*)PR_PREV_LINK(this);} + void InsertBefore(BandRect* aBandRect) {PR_INSERT_BEFORE(aBandRect, this);} + void InsertAfter(BandRect* aBandRect) {PR_INSERT_AFTER(aBandRect, this);} + void Remove() {PR_REMOVE_LINK(this);} + // Split the band rect into two vertically, with this band rect becoming // the top part, and a new band rect being allocated and returned for the // bottom part + // + // Does not insert the new band rect into the linked list BandRect* SplitVertically(nscoord aBottom); // Split the band rect into two horizontally, with this band rect becoming // the left part, and a new band rect being allocated and returned for the // right part + // + // Does not insert the new band rect into the linked list BandRect* SplitHorizontally(nscoord aRight); // Accessor functions @@ -100,9 +111,25 @@ protected: PRBool HasSameFrameList(const BandRect* aBandRect) const; }; + // Circular linked list of band rects + struct BandList : BandRect { + BandList(); + + // Accessors + PRBool IsEmpty() const {return PR_CLIST_IS_EMPTY((PRCListStr*)this);} + BandRect* Head() const {return (BandRect*)PR_LIST_HEAD(this);} + BandRect* Tail() const {return (BandRect*)PR_LIST_TAIL(this);} + + // Operations + void Append(BandRect* aBandRect) {PR_APPEND_LINK(aBandRect, this);} + + // Remove and delete all the band rects in the list + void Clear(); + }; + nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space - PRCListStr mBandList; + BandList mBandList; // header for circular linked list of band rects PLHashTable* mFrameInfoMap; protected: @@ -115,6 +142,7 @@ protected: BandRect* GetNextBand(const BandRect* aBandRect) const; void DivideBand(BandRect* aBand, nscoord aBottom); + PRBool CanJoinBands(BandRect* aBand, BandRect* aPrevBand); PRBool JoinBands(BandRect* aBand, BandRect* aPrevBand); void AddRectToBand(BandRect* aBand, BandRect* aBandRect); void InsertBandRect(BandRect* aBandRect);