1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * class that manages regions of 2-D space, originally designed
41 * generally but actually specific to space occupied by floats
44 #ifndef nsSpaceManager_h___
45 #define nsSpaceManager_h___
48 #include "nsIntervalSet.h"
49 #include "nsISupports.h"
52 #include "nsVoidArray.h"
57 struct nsHTMLReflowState
;
60 #define NS_SPACE_MANAGER_CACHE_SIZE 4
63 * Information about a particular trapezoid within a band. The space described
64 * by the trapezoid is in one of three states:
67 * <li>occupied by one frame
68 * <li>occupied by more than one frame
71 struct nsBandTrapezoid
{
72 nscoord mTopY
, mBottomY
; // top and bottom y-coordinates
73 nscoord mTopLeftX
, mBottomLeftX
; // left edge x-coordinates
74 nscoord mTopRightX
, mBottomRightX
; // right edge x-coordinates
75 const nsSmallVoidArray
* mFrames
; // list of frames occupying the space
77 // Get the height of the trapezoid
78 nscoord
GetHeight() const {return mBottomY
- mTopY
;}
80 // Get the bounding rect of the trapezoid
81 inline void GetRect(nsRect
& aRect
) const;
83 // Set the trapezoid from a rectangle
84 inline void operator=(const nsRect
& aRect
);
86 // Do these trapezoids have the same geometry?
87 inline PRBool
EqualGeometry(const nsBandTrapezoid
& aTrap
) const;
101 inline void nsBandTrapezoid::GetRect(nsRect
& aRect
) const
103 aRect
.x
= PR_MIN(mTopLeftX
, mBottomLeftX
);
105 aRect
.width
= PR_MAX(mTopRightX
, mBottomRightX
);
106 if (NS_MAXSIZE
!= aRect
.width
) {
107 aRect
.width
-= aRect
.x
;
109 aRect
.height
= (NS_MAXSIZE
== mBottomY
) ? NS_MAXSIZE
: mBottomY
- mTopY
;
112 inline void nsBandTrapezoid::operator=(const nsRect
& aRect
)
114 mTopLeftX
= mBottomLeftX
= aRect
.x
;
115 mTopRightX
= mBottomRightX
= aRect
.XMost();
117 mBottomY
= aRect
.YMost();
120 inline PRBool
nsBandTrapezoid::EqualGeometry(const nsBandTrapezoid
& aTrap
) const
123 mTopLeftX
== aTrap
.mTopLeftX
&&
124 mBottomLeftX
== aTrap
.mBottomLeftX
&&
125 mTopRightX
== aTrap
.mTopRightX
&&
126 mBottomRightX
== aTrap
.mBottomRightX
&&
127 mTopY
== aTrap
.mTopY
&&
128 mBottomY
== aTrap
.mBottomY
133 * Structure used for describing the space within a band.
134 * @see #GetBandData()
137 PRInt32 mCount
; // [out] actual number of trapezoids in the band data
138 PRInt32 mSize
; // [in] the size of the array (number of trapezoids)
139 nsBandTrapezoid
* mTrapezoids
; // [out] array of length 'size'
143 * Class for dealing with bands of available space. The space manager
144 * defines a coordinate space with an origin at (0, 0) that grows down
147 class nsSpaceManager
{
149 nsSpaceManager(nsIPresShell
* aPresShell
, nsIFrame
* aFrame
);
152 void* operator new(size_t aSize
) CPP_THROW_NEW
;
153 void operator delete(void* aPtr
, size_t aSize
);
155 static void Shutdown();
158 * Get the frame that's associated with the space manager. This frame
159 * created the space manager, and the world coordinate space is
160 * relative to this frame.
162 * You can use QueryInterface() on this frame to get any additional
165 nsIFrame
* GetFrame() const { return mFrame
; }
168 * Translate the current origin by the specified (dx, dy). This
169 * creates a new local coordinate space relative to the current
172 void Translate(nscoord aDx
, nscoord aDy
) { mX
+= aDx
; mY
+= aDy
; }
175 * Returns the current translation from local coordinate space to
176 * world coordinate space. This represents the accumulated calls to
179 void GetTranslation(nscoord
& aX
, nscoord
& aY
) const { aX
= mX
; aY
= mY
; }
182 * Returns the x-most rect in the space manager, or 0 if there are no
185 * @return PR_TRUE if there are bands and PR_FALSE if there are no bands
187 PRBool
XMost(nscoord
& aXMost
) const;
190 * Returns the y-most of the bottommost band or 0 if there are no bands.
192 * @return PR_TRUE if there are bands and PR_FALSE if there are no bands
194 PRBool
YMost(nscoord
& aYMost
) const;
197 * Returns a band starting at the specified y-offset. The band data
198 * indicates which parts of the band are available, and which parts
201 * The band data that is returned is in the coordinate space of the
202 * local coordinate system.
204 * The local coordinate space origin, the y-offset, and the max size
205 * describe a rectangle that's used to clip the underlying band of
206 * available space, i.e.
207 * {0, aYOffset, aMaxSize.width, aMaxSize.height} in the local
210 * @param aYOffset the y-offset of where the band begins. The coordinate is
211 * relative to the upper-left corner of the local coordinate space
212 * @param aMaxSize the size to use to constrain the band data
213 * @param aBandData [in,out] used to return the list of trapezoids that
214 * describe the available space and the unavailable space
215 * @return NS_OK if successful and NS_ERROR_FAILURE if the band data is not
216 * not large enough. The 'count' member of the band data struct
217 * indicates how large the array of trapezoids needs to be
219 nsresult
GetBandData(nscoord aYOffset
,
220 const nsSize
& aMaxSize
,
221 nsBandData
& aBandData
) const;
224 * Add a rectangular region of unavailable space. The space is
225 * relative to the local coordinate system.
227 * The region is tagged with a frame
229 * @param aFrame the frame used to identify the region. Must not be NULL
230 * @param aUnavailableSpace the bounding rect of the unavailable space
231 * @return NS_OK if successful
232 * NS_ERROR_FAILURE if there is already a region tagged with aFrame
234 nsresult
AddRectRegion(nsIFrame
* aFrame
,
235 const nsRect
& aUnavailableSpace
);
238 * Remove the regions associated with this floating frame and its
239 * next-sibling list. Some of the frames may never have been added;
240 * we just skip those. This is not fully general; it only works as
241 * long as the N frames to be removed are the last N frames to have
242 * been added; if there's a frame in the middle of them that should
243 * not be removed, YOU LOSE.
245 * This can only be done at the end of the life of this space manager. The only
246 * methods it is safe to call after this are XMost() and YMost().
248 nsresult
RemoveTrailingRegions(nsIFrame
* aFrameList
);
252 * Remove the region associated with aFrane.
254 * doesn't work in the general case!
256 * Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region
259 nsresult
RemoveRegion(nsIFrame
* aFrame
);
262 // Structure that stores the current state of a frame manager for
263 // Save/Restore purposes.
266 nsIFrame
*mLastFrame
;
269 nscoord mMaximalLeftYMost
;
270 nscoord mMaximalRightYMost
;
271 PRPackedBool mHaveCachedLeftYMost
;
272 PRPackedBool mHaveCachedRightYMost
;
274 friend class nsSpaceManager
;
278 * Clears the list of regions representing the unavailable space.
282 PRBool
HasAnyFloats() { return mFrameInfoMap
!= nsnull
; }
285 * Methods for dealing with the propagation of float damage during
288 PRBool
HasFloatDamage()
290 return !mFloatDamage
.IsEmpty();
293 void IncludeInDamage(nscoord aIntervalBegin
, nscoord aIntervalEnd
)
295 mFloatDamage
.IncludeInterval(aIntervalBegin
+ mY
, aIntervalEnd
+ mY
);
298 PRBool
IntersectsDamage(nscoord aIntervalBegin
, nscoord aIntervalEnd
)
300 return mFloatDamage
.Intersects(aIntervalBegin
+ mY
, aIntervalEnd
+ mY
);
304 * Saves the current state of the space manager into aState.
306 void PushState(SavedState
* aState
);
309 * Restores the space manager to the saved state.
311 * These states must be managed using stack discipline. PopState can only
312 * be used after PushState has been used to save the state, and it can only
313 * be used once --- although it can be omitted; saved states can be ignored.
314 * States must be popped in the reverse order they were pushed.
316 void PopState(SavedState
* aState
);
319 * Get the top of the last region placed into the space manager, to
320 * enforce the rule that a float can't be above an earlier float.
321 * Returns the minimum nscoord value if there are no regions.
323 nscoord
GetLowestRegionTop();
326 * Return the coordinate of the lowest float matching aBreakType in this
327 * space manager. Returns aY if there are no matching floats.
329 nscoord
ClearFloats(nscoord aY
, PRUint8 aBreakType
);
333 * Dump the state of the spacemanager out to a file
335 nsresult
List(FILE* out
);
339 // Structure that maintains information about the region associated
340 // with a particular frame
342 nsIFrame
* const mFrame
;
343 nsRect mRect
; // rectangular region
346 FrameInfo(nsIFrame
* aFrame
, const nsRect
& aRect
);
347 #ifdef NS_BUILD_REFCNT_LOGGING
353 // Doubly linked list of band rects
354 struct BandRect
: PRCListStr
{
356 nscoord mRight
, mBottom
;
357 nsSmallVoidArray mFrames
; // list of frames occupying the space
359 BandRect(nscoord aLeft
, nscoord aTop
,
360 nscoord aRight
, nscoord aBottom
,
362 BandRect(nscoord aLeft
, nscoord aTop
,
363 nscoord aRight
, nscoord aBottom
,
364 nsSmallVoidArray
& frames
);
368 BandRect
* Next() const {return (BandRect
*)PR_NEXT_LINK(this);}
369 BandRect
* Prev() const {return (BandRect
*)PR_PREV_LINK(this);}
370 void InsertBefore(BandRect
* aBandRect
) {PR_INSERT_BEFORE(aBandRect
, this);}
371 void InsertAfter(BandRect
* aBandRect
) {PR_INSERT_AFTER(aBandRect
, this);}
372 void Remove() {PR_REMOVE_LINK(this);}
374 // Split the band rect into two vertically, with this band rect becoming
375 // the top part, and a new band rect being allocated and returned for the
378 // Does not insert the new band rect into the linked list
379 BandRect
* SplitVertically(nscoord aBottom
);
381 // Split the band rect into two horizontally, with this band rect becoming
382 // the left part, and a new band rect being allocated and returned for the
385 // Does not insert the new band rect into the linked list
386 BandRect
* SplitHorizontally(nscoord aRight
);
388 // Accessor functions
389 PRBool
IsOccupiedBy(const nsIFrame
* aFrame
) const {
390 return (mFrames
.IndexOf((void*)aFrame
) != -1);
392 void AddFrame(const nsIFrame
* aFrame
) {
393 mFrames
.AppendElement((void*)aFrame
);
395 void RemoveFrame(const nsIFrame
* aFrame
) {
396 mFrames
.RemoveElement((void*)aFrame
);
398 nsIFrame
* FrameAt(PRInt32 index
) {
399 return static_cast<nsIFrame
*>(mFrames
.FastElementAt(index
));
401 PRBool
HasSameFrameList(const BandRect
* aBandRect
) const;
402 PRInt32
Length() const;
405 // Circular linked list of band rects
406 struct BandList
: BandRect
{
410 PRBool
IsEmpty() const {return PR_CLIST_IS_EMPTY((PRCListStr
*)this);}
411 BandRect
* Head() const {return (BandRect
*)PR_LIST_HEAD(this);}
412 BandRect
* Tail() const {return (BandRect
*)PR_LIST_TAIL(this);}
415 void Append(BandRect
* aBandRect
) {PR_APPEND_LINK(aBandRect
, this);}
417 // Remove and delete all the band rects in the list
422 nsIFrame
* const mFrame
; // frame associated with the space manager
423 nscoord mX
, mY
; // translation from local to global coordinate space
424 BandList mBandList
; // header/sentinel for circular linked list of band rects
425 nscoord mLowestTop
; // the lowest *top*
426 FrameInfo
* mFrameInfoMap
;
427 nsIntervalSet mFloatDamage
;
428 PRPackedBool mHaveCachedLeftYMost
; // If true, mMaximalLeftYMost is set
429 PRPackedBool mHaveCachedRightYMost
; // If true, mMaximalRightYMost is set
430 nscoord mMaximalLeftYMost
; // The maximal YMost of our FrameInfo
431 // rects for left floats. Only makes
432 // sense when mHaveCachedLeftYMost is
434 nscoord mMaximalRightYMost
; // The maximal YMost of our FrameInfo
435 // rects for right floats. Only makes
436 // sense when mHaveCachedLeftYMost is
438 // We keep track of the last BandRect* we worked with so that we can
439 // make use of locality of reference in situations where people want
440 // to do a bunch of operations in a row.
441 BandRect
* mCachedBandPosition
;
444 FrameInfo
* GetFrameInfoFor(nsIFrame
* aFrame
);
445 FrameInfo
* CreateFrameInfo(nsIFrame
* aFrame
, const nsRect
& aRect
);
446 void DestroyFrameInfo(FrameInfo
*);
448 void ClearFrameInfo();
449 void ClearBandRects();
451 BandRect
* GetNextBand(const BandRect
* aBandRect
) const;
452 BandRect
* GetPrevBand(const BandRect
* aBandRect
) const;
453 void DivideBand(BandRect
* aBand
, nscoord aBottom
);
454 PRBool
CanJoinBands(BandRect
* aBand
, BandRect
* aPrevBand
);
455 PRBool
JoinBands(BandRect
* aBand
, BandRect
* aPrevBand
);
456 void AddRectToBand(BandRect
* aBand
, BandRect
* aBandRect
);
457 void InsertBandRect(BandRect
* aBandRect
);
459 nsresult
GetBandAvailableSpace(const BandRect
* aBand
,
461 const nsSize
& aMaxSize
,
462 nsBandData
& aAvailableSpace
) const;
464 // Return a band guaranteed to have its top at or above aYOffset or the first
465 // band if there is no band with its top above aYOffset. This method will
466 // use mCachedBandPosition to maybe get such a band that's not too far up.
467 // This function should not be called if there are no bands.
468 // This function never returns null.
469 BandRect
* GuessBandWithTopAbove(nscoord aYOffset
) const;
471 void SetCachedBandPosition(BandRect
* aBandRect
) {
472 NS_ASSERTION(!aBandRect
||
473 aBandRect
== mBandList
.Head() ||
474 aBandRect
->Prev()->mBottom
!= aBandRect
->mBottom
,
475 "aBandRect should be first rect within its band");
476 mCachedBandPosition
= aBandRect
;
481 static PRInt32 sCachedSpaceManagerCount
;
482 static void* sCachedSpaceManagers
[NS_SPACE_MANAGER_CACHE_SIZE
];
484 nsSpaceManager(const nsSpaceManager
&); // no implementation
485 void operator=(const nsSpaceManager
&); // no implementation
489 * A helper class to manage maintenance of the space manager during
490 * nsBlockFrame::Reflow. It automatically restores the old space
491 * manager in the reflow state when the object goes out of scope.
493 class nsAutoSpaceManager
{
495 nsAutoSpaceManager(nsHTMLReflowState
& aReflowState
)
496 : mReflowState(aReflowState
),
503 ~nsAutoSpaceManager();
506 * Create a new space manager for the specified frame. This will
507 * `remember' the old space manager, and install the new space
508 * manager in the reflow state.
511 CreateSpaceManagerFor(nsPresContext
*aPresContext
,
516 * `Orphan' any space manager that the nsAutoSpaceManager created;
517 * i.e., make it so that we don't destroy the space manager when we
520 void DebugOrphanSpaceManager() { mOwns
= PR_FALSE
; }
524 nsHTMLReflowState
&mReflowState
;
528 nsSpaceManager
*mNew
;
529 nsSpaceManager
*mOld
;
532 #endif /* nsSpaceManager_h___ */