1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * David Hyatt <hyatt@netscape.com>
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : //
41 : // Eric Vaughan
42 : // Netscape Communications
43 : //
44 : // See documentation in associated header file
45 : //
46 :
47 : #include "nsBoxLayoutState.h"
48 : #include "nsSprocketLayout.h"
49 : #include "nsPresContext.h"
50 : #include "nsCOMPtr.h"
51 : #include "nsIContent.h"
52 : #include "nsIPresShell.h"
53 : #include "nsContainerFrame.h"
54 : #include "nsBoxFrame.h"
55 :
56 : nsBoxLayout* nsSprocketLayout::gInstance = nsnull;
57 :
58 : //#define DEBUG_GROW
59 :
60 : #define DEBUG_SPRING_SIZE 8
61 : #define DEBUG_BORDER_SIZE 2
62 : #define COIL_SIZE 8
63 :
64 :
65 : nsresult
66 0 : NS_NewSprocketLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout)
67 : {
68 0 : if (!nsSprocketLayout::gInstance) {
69 0 : nsSprocketLayout::gInstance = new nsSprocketLayout();
70 0 : NS_IF_ADDREF(nsSprocketLayout::gInstance);
71 : }
72 : // we have not instance variables so just return our static one.
73 0 : aNewLayout = nsSprocketLayout::gInstance;
74 0 : return NS_OK;
75 : }
76 :
77 : /*static*/ void
78 1403 : nsSprocketLayout::Shutdown()
79 : {
80 1403 : NS_IF_RELEASE(gInstance);
81 1403 : }
82 :
83 0 : nsSprocketLayout::nsSprocketLayout()
84 : {
85 0 : }
86 :
87 : bool
88 0 : nsSprocketLayout::IsHorizontal(nsIBox* aBox)
89 : {
90 0 : return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
91 : }
92 :
93 : void
94 0 : nsSprocketLayout::GetFrameState(nsIBox* aBox, nsFrameState& aState)
95 : {
96 0 : aState = aBox->GetStateBits();
97 0 : }
98 :
99 : static PRUint8
100 0 : GetFrameDirection(nsIBox* aBox)
101 : {
102 0 : return aBox->GetStyleVisibility()->mDirection;
103 : }
104 :
105 : static void
106 0 : HandleBoxPack(nsIBox* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY,
107 : const nsRect& aOriginalRect, const nsRect& aClientRect)
108 : {
109 : // In the normal direction we lay out our kids in the positive direction (e.g., |x| will get
110 : // bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse
111 : // direction, the opposite is true. We'll be laying out each child at a smaller |x| or
112 : // |y|.
113 0 : PRUint8 frameDirection = GetFrameDirection(aBox);
114 :
115 0 : if (aFrameState & NS_STATE_IS_HORIZONTAL) {
116 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
117 : // The normal direction. |x| increases as we move through our children.
118 0 : aX = aClientRect.x;
119 : }
120 : else {
121 : // The reverse direction. |x| decreases as we move through our children.
122 0 : aX = aClientRect.x + aOriginalRect.width;
123 : }
124 : // |y| is always in the normal direction in horizontal boxes
125 0 : aY = aClientRect.y;
126 : }
127 : else {
128 : // take direction property into account for |x| in vertical boxes
129 0 : if (frameDirection == NS_STYLE_DIRECTION_LTR) {
130 : // The normal direction. |x| increases as we move through our children.
131 0 : aX = aClientRect.x;
132 : }
133 : else {
134 : // The reverse direction. |x| decreases as we move through our children.
135 0 : aX = aClientRect.x + aOriginalRect.width;
136 : }
137 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
138 : // The normal direction. |y| increases as we move through our children.
139 0 : aY = aClientRect.y;
140 : }
141 : else {
142 : // The reverse direction. |y| decreases as we move through our children.
143 0 : aY = aClientRect.y + aOriginalRect.height;
144 : }
145 : }
146 :
147 : // Get our pack/alignment information.
148 0 : nsIBox::Halignment halign = aBox->GetHAlign();
149 0 : nsIBox::Valignment valign = aBox->GetVAlign();
150 :
151 : // The following code handles box PACKING. Packing comes into play in the case where the computed size for
152 : // all of our children (now stored in our client rect) is smaller than the size available for
153 : // the box (stored in |aOriginalRect|).
154 : //
155 : // Here we adjust our |x| and |y| variables accordingly so that we start at the beginning,
156 : // middle, or end of the box.
157 : //
158 : // XXXdwh JUSTIFY needs to be implemented!
159 0 : if (aFrameState & NS_STATE_IS_HORIZONTAL) {
160 0 : switch(halign) {
161 : case nsBoxFrame::hAlign_Left:
162 0 : break; // Nothing to do. The default initialized us properly.
163 :
164 : case nsBoxFrame::hAlign_Center:
165 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
166 0 : aX += (aOriginalRect.width - aClientRect.width)/2;
167 : else
168 0 : aX -= (aOriginalRect.width - aClientRect.width)/2;
169 0 : break;
170 :
171 : case nsBoxFrame::hAlign_Right:
172 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
173 0 : aX += (aOriginalRect.width - aClientRect.width);
174 : else
175 0 : aX -= (aOriginalRect.width - aClientRect.width);
176 0 : break; // Nothing to do for the reverse dir. The default initialized us properly.
177 : }
178 : } else {
179 0 : switch(valign) {
180 : case nsBoxFrame::vAlign_Top:
181 : case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack.
182 0 : break; // Don't do anything. We were initialized correctly.
183 :
184 : case nsBoxFrame::vAlign_Middle:
185 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
186 0 : aY += (aOriginalRect.height - aClientRect.height)/2;
187 : else
188 0 : aY -= (aOriginalRect.height - aClientRect.height)/2;
189 0 : break;
190 :
191 : case nsBoxFrame::vAlign_Bottom:
192 0 : if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
193 0 : aY += (aOriginalRect.height - aClientRect.height);
194 : else
195 0 : aY -= (aOriginalRect.height - aClientRect.height);
196 0 : break;
197 : }
198 : }
199 0 : }
200 :
201 : NS_IMETHODIMP
202 0 : nsSprocketLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
203 : {
204 : // See if we are collapsed. If we are, then simply iterate over all our
205 : // children and give them a rect of 0 width and height.
206 0 : if (aBox->IsCollapsed()) {
207 0 : nsIBox* child = aBox->GetChildBox();
208 0 : while(child)
209 : {
210 0 : nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0));
211 0 : child = child->GetNextBox();
212 : }
213 0 : return NS_OK;
214 : }
215 :
216 0 : aState.PushStackMemory();
217 :
218 : // ----- figure out our size ----------
219 0 : const nsSize originalSize = aBox->GetSize();
220 :
221 : // -- make sure we remove our border and padding ----
222 0 : nsRect clientRect;
223 0 : aBox->GetClientRect(clientRect);
224 :
225 : // |originalClientRect| represents the rect of the entire box (excluding borders
226 : // and padding). We store it here because we're going to use |clientRect| to hold
227 : // the required size for all our kids. As an example, consider an hbox with a
228 : // specified width of 300. If the kids total only 150 pixels of width, then
229 : // we have 150 pixels left over. |clientRect| is going to hold a width of 150 and
230 : // is going to be adjusted based off the value of the PACK property. If flexible
231 : // objects are in the box, then the two rects will match.
232 0 : nsRect originalClientRect(clientRect);
233 :
234 : // The frame state contains cached knowledge about our box, such as our orientation
235 : // and direction.
236 0 : nsFrameState frameState = 0;
237 0 : GetFrameState(aBox, frameState);
238 :
239 : // Build a list of our children's desired sizes and computed sizes
240 0 : nsBoxSize* boxSizes = nsnull;
241 0 : nsComputedBoxSize* computedBoxSizes = nsnull;
242 :
243 0 : nscoord min = 0;
244 0 : nscoord max = 0;
245 0 : PRInt32 flexes = 0;
246 0 : PopulateBoxSizes(aBox, aState, boxSizes, min, max, flexes);
247 :
248 : // The |size| variable will hold the total size of children along the axis of
249 : // the box. Continuing with the example begun in the comment above, size would
250 : // be 150 pixels.
251 0 : nscoord size = clientRect.width;
252 0 : if (!IsHorizontal(aBox))
253 0 : size = clientRect.height;
254 0 : ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes);
255 :
256 : // After the call to ComputeChildSizes, the |size| variable contains the
257 : // total required size of all the children. We adjust our clientRect in the
258 : // appropriate dimension to match this size. In our example, we now assign
259 : // 150 pixels into the clientRect.width.
260 : //
261 : // The variables |min| and |max| hold the minimum required size box must be
262 : // in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum
263 : // height we require to enclose our children, and |max| is the maximum height
264 : // required to enclose our children.
265 0 : if (IsHorizontal(aBox)) {
266 0 : clientRect.width = size;
267 0 : if (clientRect.height < min)
268 0 : clientRect.height = min;
269 :
270 0 : if (frameState & NS_STATE_AUTO_STRETCH) {
271 0 : if (clientRect.height > max)
272 0 : clientRect.height = max;
273 : }
274 : } else {
275 0 : clientRect.height = size;
276 0 : if (clientRect.width < min)
277 0 : clientRect.width = min;
278 :
279 0 : if (frameState & NS_STATE_AUTO_STRETCH) {
280 0 : if (clientRect.width > max)
281 0 : clientRect.width = max;
282 : }
283 : }
284 :
285 : // With the sizes computed, now it's time to lay out our children.
286 0 : bool needsRedraw = false;
287 : bool finished;
288 0 : nscoord passes = 0;
289 :
290 : // We flow children at their preferred locations (along with the appropriate computed flex).
291 : // After we flow a child, it is possible that the child will change its size. If/when this happens,
292 : // we have to do another pass. Typically only 2 passes are required, but the code is prepared to
293 : // do as many passes as are necessary to achieve equilibrium.
294 0 : nscoord x = 0;
295 0 : nscoord y = 0;
296 0 : nscoord origX = 0;
297 0 : nscoord origY = 0;
298 :
299 : // |childResized| lets us know if a child changed its size after we attempted to lay it out at
300 : // the specified size. If this happens, we usually have to do another pass.
301 0 : bool childResized = false;
302 :
303 : // |passes| stores our number of passes. If for any reason we end up doing more than, say, 10
304 : // passes, we assert to indicate that something is seriously screwed up.
305 0 : passes = 0;
306 0 : do
307 : {
308 : #ifdef DEBUG_REFLOW
309 : if (passes > 0) {
310 : AddIndents();
311 : printf("ChildResized doing pass: %d\n", passes);
312 : }
313 : #endif
314 :
315 : // Always assume that we're done. This will change if, for example, children don't stay
316 : // the same size after being flowed.
317 0 : finished = true;
318 :
319 : // Handle box packing.
320 0 : HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
321 :
322 : // Now that packing is taken care of we set up a few additional
323 : // tracking variables.
324 0 : origX = x;
325 0 : origY = y;
326 :
327 0 : nscoord nextX = x;
328 0 : nscoord nextY = y;
329 :
330 : // Now we iterate over our box children and our box size lists in
331 : // parallel. For each child, we look at its sizes and figure out
332 : // where to place it.
333 0 : nsComputedBoxSize* childComputedBoxSize = computedBoxSizes;
334 0 : nsBoxSize* childBoxSize = boxSizes;
335 :
336 0 : nsIBox* child = aBox->GetChildBox();
337 :
338 0 : PRInt32 count = 0;
339 0 : while (child || (childBoxSize && childBoxSize->bogus))
340 : {
341 : // If for some reason, our lists are not the same length, we guard
342 : // by bailing out of the loop.
343 0 : if (childBoxSize == nsnull) {
344 0 : NS_NOTREACHED("Lists not the same length.");
345 0 : break;
346 : }
347 :
348 0 : nscoord width = clientRect.width;
349 0 : nscoord height = clientRect.height;
350 :
351 0 : if (!childBoxSize->bogus) {
352 : // We have a valid box size entry. This entry already contains information about our
353 : // sizes along the axis of the box (e.g., widths in a horizontal box). If our default
354 : // ALIGN is not stretch, however, then we also need to know the child's size along the
355 : // opposite axis.
356 0 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
357 0 : nsSize prefSize = child->GetPrefSize(aState);
358 0 : nsSize minSize = child->GetMinSize(aState);
359 0 : nsSize maxSize = child->GetMaxSize(aState);
360 0 : prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
361 :
362 0 : AddMargin(child, prefSize);
363 0 : width = NS_MIN(prefSize.width, originalClientRect.width);
364 0 : height = NS_MIN(prefSize.height, originalClientRect.height);
365 : }
366 : }
367 :
368 : // Obtain the computed size along the axis of the box for this child from the computedBoxSize entry.
369 : // We store the result in |width| for horizontal boxes and |height| for vertical boxes.
370 0 : if (frameState & NS_STATE_IS_HORIZONTAL)
371 0 : width = childComputedBoxSize->size;
372 : else
373 0 : height = childComputedBoxSize->size;
374 :
375 : // Adjust our x/y for the left/right spacing.
376 0 : if (frameState & NS_STATE_IS_HORIZONTAL) {
377 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
378 0 : x += (childBoxSize->left);
379 : else
380 0 : x -= (childBoxSize->right);
381 : } else {
382 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
383 0 : y += (childBoxSize->left);
384 : else
385 0 : y -= (childBoxSize->right);
386 : }
387 :
388 0 : nextX = x;
389 0 : nextY = y;
390 :
391 : // Now we build a child rect.
392 0 : nscoord rectX = x;
393 0 : nscoord rectY = y;
394 0 : if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
395 0 : if (frameState & NS_STATE_IS_HORIZONTAL)
396 0 : rectX -= width;
397 : else
398 0 : rectY -= height;
399 : }
400 :
401 : // We now create an accurate child rect based off our computed size information.
402 0 : nsRect childRect(rectX, rectY, width, height);
403 :
404 : // Sanity check against our clientRect. It is possible that a child specified
405 : // a size that is too large to fit. If that happens, then we have to grow
406 : // our client rect. Remember, clientRect is not the total rect of the enclosing
407 : // box. It currently holds our perception of how big the children needed to
408 : // be.
409 0 : if (childRect.width > clientRect.width)
410 0 : clientRect.width = childRect.width;
411 :
412 0 : if (childRect.height > clientRect.height)
413 0 : clientRect.height = childRect.height;
414 :
415 : // Either |nextX| or |nextY| is updated by this function call, according
416 : // to our axis.
417 0 : ComputeChildsNextPosition(aBox, x, y, nextX, nextY, childRect);
418 :
419 : // Now we further update our nextX/Y along our axis.
420 : // We also set childRect.y/x along the opposite axis appropriately for a
421 : // stretch alignment. (Non-stretch alignment is handled below.)
422 0 : if (frameState & NS_STATE_IS_HORIZONTAL) {
423 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
424 0 : nextX += (childBoxSize->right);
425 : else
426 0 : nextX -= (childBoxSize->left);
427 0 : childRect.y = originalClientRect.y;
428 : }
429 : else {
430 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
431 0 : nextY += (childBoxSize->right);
432 : else
433 0 : nextY -= (childBoxSize->left);
434 0 : childRect.x = originalClientRect.x;
435 : }
436 :
437 : // If we encounter a completely bogus box size, we just leave this child completely
438 : // alone and continue through the loop to the next child.
439 0 : if (childBoxSize->bogus)
440 : {
441 0 : childComputedBoxSize = childComputedBoxSize->next;
442 0 : childBoxSize = childBoxSize->next;
443 0 : count++;
444 0 : x = nextX;
445 0 : y = nextY;
446 0 : continue;
447 : }
448 :
449 0 : nsMargin margin(0,0,0,0);
450 :
451 0 : bool layout = true;
452 :
453 : // Deflate the rect of our child by its margin.
454 0 : child->GetMargin(margin);
455 0 : childRect.Deflate(margin);
456 0 : if (childRect.width < 0)
457 0 : childRect.width = 0;
458 0 : if (childRect.height < 0)
459 0 : childRect.height = 0;
460 :
461 : // Now we're trying to figure out if we have to lay out this child, i.e., to call
462 : // the child's Layout method.
463 0 : if (passes > 0) {
464 0 : layout = false;
465 : } else {
466 : // Always perform layout if we are dirty or have dirty children
467 0 : if (!NS_SUBTREE_DIRTY(child))
468 0 : layout = false;
469 : }
470 :
471 0 : nsRect oldRect(child->GetRect());
472 :
473 : // Non-stretch alignment will be handled in AlignChildren(), so don't
474 : // change child out-of-axis positions yet.
475 0 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
476 0 : if (frameState & NS_STATE_IS_HORIZONTAL) {
477 0 : childRect.y = oldRect.y;
478 : } else {
479 0 : childRect.x = oldRect.x;
480 : }
481 : }
482 :
483 : // We computed a childRect. Now we want to set the bounds of the child to be that rect.
484 : // If our old rect is different, then we know our size changed and we cache that fact
485 : // in the |sizeChanged| variable.
486 :
487 0 : child->SetBounds(aState, childRect);
488 : bool sizeChanged = (childRect.width != oldRect.width ||
489 0 : childRect.height != oldRect.height);
490 :
491 0 : if (sizeChanged) {
492 : // Our size is different. Sanity check against our maximum allowed size to ensure
493 : // we didn't exceed it.
494 0 : nsSize minSize = child->GetMinSize(aState);
495 0 : nsSize maxSize = child->GetMaxSize(aState);
496 0 : maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize);
497 :
498 : // make sure the size is in our max size.
499 0 : if (childRect.width > maxSize.width)
500 0 : childRect.width = maxSize.width;
501 :
502 0 : if (childRect.height > maxSize.height)
503 0 : childRect.height = maxSize.height;
504 :
505 : // set it again
506 0 : child->SetBounds(aState, childRect);
507 :
508 : // Since the child changed size, we know a redraw is probably going to be required.
509 0 : needsRedraw = true;
510 : }
511 :
512 : // if something moved then we might need to redraw
513 0 : if (oldRect.x != childRect.x || oldRect.y != childRect.y)
514 0 : needsRedraw = true;
515 :
516 : // If we already determined that layout was required or if our size has changed, then
517 : // we make sure to call layout on the child, since its children may need to be shifted
518 : // around as a result of the size change.
519 0 : if (layout || sizeChanged)
520 0 : child->Layout(aState);
521 :
522 : // If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout.
523 : // We have to check for this.
524 0 : nsRect newChildRect(child->GetRect());
525 :
526 0 : if (!newChildRect.IsEqualInterior(childRect)) {
527 : #ifdef DEBUG_GROW
528 : child->DumpBox(stdout);
529 : printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height);
530 : #endif
531 0 : newChildRect.Inflate(margin);
532 0 : childRect.Inflate(margin);
533 :
534 : // The child changed size during layout. The ChildResized method handles this
535 : // scenario.
536 : ChildResized(aBox,
537 : aState,
538 : child,
539 : childBoxSize,
540 : childComputedBoxSize,
541 : boxSizes,
542 : computedBoxSizes,
543 : childRect,
544 : newChildRect,
545 : clientRect,
546 : flexes,
547 0 : finished);
548 :
549 : // We note that a child changed size, which means that another pass will be required.
550 0 : childResized = true;
551 :
552 : // Now that a child resized, it's entirely possible that OUR rect is too small. Now we
553 : // ensure that |originalClientRect| is grown to accommodate the size of |clientRect|.
554 0 : if (clientRect.width > originalClientRect.width)
555 0 : originalClientRect.width = clientRect.width;
556 :
557 0 : if (clientRect.height > originalClientRect.height)
558 0 : originalClientRect.height = clientRect.height;
559 :
560 0 : if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
561 : // Our childRect had its XMost() or YMost() (depending on our layout
562 : // direction), positioned at a certain point. Ensure that the
563 : // newChildRect satisfies the same constraint. Note that this is
564 : // just equivalent to adjusting the x/y by the difference in
565 : // width/height between childRect and newChildRect. So we don't need
566 : // to reaccount for the left and right of the box layout state again.
567 0 : if (frameState & NS_STATE_IS_HORIZONTAL)
568 0 : newChildRect.x = childRect.XMost() - newChildRect.width;
569 : else
570 0 : newChildRect.y = childRect.YMost() - newChildRect.height;
571 : }
572 :
573 : // If the child resized then recompute its position.
574 0 : ComputeChildsNextPosition(aBox, x, y, nextX, nextY, newChildRect);
575 :
576 0 : if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom)
577 0 : newChildRect.Deflate(margin);
578 :
579 0 : if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom)
580 0 : childRect.Deflate(margin);
581 :
582 0 : child->SetBounds(aState, newChildRect);
583 :
584 : // If we are the first box that changed size, then we don't need to do a second pass
585 0 : if (count == 0)
586 0 : finished = true;
587 : }
588 :
589 : // Now update our x/y finally.
590 0 : x = nextX;
591 0 : y = nextY;
592 :
593 : // Move to the next child.
594 0 : childComputedBoxSize = childComputedBoxSize->next;
595 0 : childBoxSize = childBoxSize->next;
596 :
597 0 : child = child->GetNextBox();
598 0 : count++;
599 : }
600 :
601 : // Sanity-checking code to ensure we don't do an infinite # of passes.
602 0 : passes++;
603 0 : NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!");
604 0 : if (passes > 10)
605 0 : break;
606 : } while (false == finished);
607 :
608 : // Get rid of our size lists.
609 0 : while(boxSizes)
610 : {
611 0 : nsBoxSize* toDelete = boxSizes;
612 0 : boxSizes = boxSizes->next;
613 0 : delete toDelete;
614 : }
615 :
616 0 : while(computedBoxSizes)
617 : {
618 0 : nsComputedBoxSize* toDelete = computedBoxSizes;
619 0 : computedBoxSizes = computedBoxSizes->next;
620 0 : delete toDelete;
621 : }
622 :
623 0 : if (childResized) {
624 : // See if one of our children forced us to get bigger
625 0 : nsRect tmpClientRect(originalClientRect);
626 0 : nsMargin bp(0,0,0,0);
627 0 : aBox->GetBorderAndPadding(bp);
628 0 : tmpClientRect.Inflate(bp);
629 :
630 0 : if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height)
631 : {
632 : // if it did reset our bounds.
633 0 : nsRect bounds(aBox->GetRect());
634 0 : if (tmpClientRect.width > originalSize.width)
635 0 : bounds.width = tmpClientRect.width;
636 :
637 0 : if (tmpClientRect.height > originalSize.height)
638 0 : bounds.height = tmpClientRect.height;
639 :
640 0 : aBox->SetBounds(aState, bounds);
641 : }
642 : }
643 :
644 : // Because our size grew, we now have to readjust because of box packing. Repack
645 : // in order to update our x and y to the correct values.
646 0 : HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
647 :
648 : // Compare against our original x and y and only worry about adjusting the children if
649 : // we really did have to change the positions because of packing (typically for 'center'
650 : // or 'end' pack values).
651 0 : if (x != origX || y != origY) {
652 0 : nsIBox* child = aBox->GetChildBox();
653 :
654 : // reposition all our children
655 0 : while (child)
656 : {
657 0 : nsRect childRect(child->GetRect());
658 0 : childRect.x += (x - origX);
659 0 : childRect.y += (y - origY);
660 0 : child->SetBounds(aState, childRect);
661 0 : child = child->GetNextBox();
662 : }
663 : }
664 :
665 : // Perform out-of-axis alignment for non-stretch alignments
666 0 : if (!(frameState & NS_STATE_AUTO_STRETCH)) {
667 0 : AlignChildren(aBox, aState, &needsRedraw);
668 : }
669 :
670 : // Now do our redraw.
671 0 : if (needsRedraw)
672 0 : aBox->Redraw(aState);
673 :
674 0 : aState.PopStackMemory();
675 :
676 : // That's it! If you made it this far without having a nervous breakdown,
677 : // congratulations! Go get yourself a beer.
678 0 : return NS_OK;
679 : }
680 :
681 : void
682 0 : nsSprocketLayout::PopulateBoxSizes(nsIBox* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, PRInt32& aFlexes)
683 : {
684 : // used for the equal size flag
685 0 : nscoord biggestPrefWidth = 0;
686 0 : nscoord biggestMinWidth = 0;
687 0 : nscoord smallestMaxWidth = NS_INTRINSICSIZE;
688 :
689 0 : nsFrameState frameState = 0;
690 0 : GetFrameState(aBox, frameState);
691 :
692 : //if (frameState & NS_STATE_CURRENTLY_IN_DEBUG)
693 : // printf("In debug\n");
694 :
695 0 : aMinSize = 0;
696 0 : aMaxSize = NS_INTRINSICSIZE;
697 :
698 : bool isHorizontal;
699 :
700 0 : if (IsHorizontal(aBox))
701 0 : isHorizontal = true;
702 : else
703 0 : isHorizontal = false;
704 :
705 : // this is a nice little optimization
706 : // it turns out that if we only have 1 flexable child
707 : // then it does not matter what its preferred size is
708 : // there is nothing to flex it relative. This is great
709 : // because we can avoid asking for a preferred size in this
710 : // case. Why is this good? Well you might have html inside it
711 : // and asking html for its preferred size is rather expensive.
712 : // so we can just optimize it out this way.
713 :
714 : // set flexes
715 0 : nsIBox* child = aBox->GetChildBox();
716 :
717 0 : aFlexes = 0;
718 0 : nsBoxSize* currentBox = nsnull;
719 :
720 : #if 0
721 : nsBoxSize* start = aBoxSizes;
722 :
723 : while(child)
724 : {
725 : // ok if we started with a list move down the list
726 : // until we reach the end. Then start looking at childen.
727 : // This feature is used extensively for Grid.
728 : nscoord flex = 0;
729 :
730 : if (!start) {
731 : if (!currentBox) {
732 : aBoxSizes = new (aState) nsBoxSize();
733 : currentBox = aBoxSizes;
734 : } else {
735 : currentBox->next = new (aState) nsBoxSize();
736 : currentBox = currentBox->next;
737 : }
738 :
739 :
740 : flex = child->GetFlex(aState);
741 :
742 : currentBox->flex = flex;
743 : currentBox->collapsed = child->IsCollapsed();
744 : } else {
745 : flex = start->flex;
746 : start = start->next;
747 : }
748 :
749 : if (flex > 0)
750 : aFlexes++;
751 :
752 : child = child->GetNextBox();
753 : }
754 : #endif
755 :
756 : // get pref, min, max
757 0 : child = aBox->GetChildBox();
758 0 : currentBox = aBoxSizes;
759 0 : nsBoxSize* last = nsnull;
760 :
761 0 : nscoord maxFlex = 0;
762 0 : PRInt32 childCount = 0;
763 :
764 0 : while(child)
765 : {
766 0 : while (currentBox && currentBox->bogus) {
767 0 : last = currentBox;
768 0 : currentBox = currentBox->next;
769 : }
770 0 : ++childCount;
771 0 : nsSize pref(0,0);
772 0 : nsSize minSize(0,0);
773 0 : nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
774 0 : nscoord ascent = 0;
775 0 : bool collapsed = child->IsCollapsed();
776 :
777 0 : if (!collapsed) {
778 : // only one flexible child? Cool we will just make its preferred size
779 : // 0 then and not even have to ask for it.
780 : //if (flexes != 1) {
781 :
782 0 : pref = child->GetPrefSize(aState);
783 0 : minSize = child->GetMinSize(aState);
784 0 : maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetMaxSize(aState));
785 0 : ascent = child->GetBoxAscent(aState);
786 0 : nsMargin margin;
787 0 : child->GetMargin(margin);
788 0 : ascent += margin.top;
789 : //}
790 :
791 0 : pref = nsBox::BoundsCheck(minSize, pref, maxSize);
792 :
793 0 : AddMargin(child, pref);
794 0 : AddMargin(child, minSize);
795 0 : AddMargin(child, maxSize);
796 : }
797 :
798 0 : if (!currentBox) {
799 : // create one.
800 0 : currentBox = new (aState) nsBoxSize();
801 0 : if (!aBoxSizes) {
802 0 : aBoxSizes = currentBox;
803 0 : last = aBoxSizes;
804 : } else {
805 0 : last->next = currentBox;
806 0 : last = currentBox;
807 : }
808 :
809 : nscoord minWidth;
810 : nscoord maxWidth;
811 : nscoord prefWidth;
812 :
813 : // get sizes from child
814 0 : if (isHorizontal) {
815 0 : minWidth = minSize.width;
816 0 : maxWidth = maxSize.width;
817 0 : prefWidth = pref.width;
818 : } else {
819 0 : minWidth = minSize.height;
820 0 : maxWidth = maxSize.height;
821 0 : prefWidth = pref.height;
822 : }
823 :
824 0 : nscoord flex = child->GetFlex(aState);
825 :
826 : // set them if you collapsed you are not flexible.
827 0 : if (collapsed) {
828 0 : currentBox->flex = 0;
829 : }
830 : else {
831 0 : if (flex > maxFlex) {
832 0 : maxFlex = flex;
833 : }
834 0 : currentBox->flex = flex;
835 : }
836 :
837 : // we specified all our children are equal size;
838 0 : if (frameState & NS_STATE_EQUAL_SIZE) {
839 :
840 0 : if (prefWidth > biggestPrefWidth)
841 0 : biggestPrefWidth = prefWidth;
842 :
843 0 : if (minWidth > biggestMinWidth)
844 0 : biggestMinWidth = minWidth;
845 :
846 0 : if (maxWidth < smallestMaxWidth)
847 0 : smallestMaxWidth = maxWidth;
848 : } else { // not we can set our children right now.
849 0 : currentBox->pref = prefWidth;
850 0 : currentBox->min = minWidth;
851 0 : currentBox->max = maxWidth;
852 : }
853 :
854 0 : NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!");
855 :
856 : }
857 :
858 0 : if (!isHorizontal) {
859 0 : if (minSize.width > aMinSize)
860 0 : aMinSize = minSize.width;
861 :
862 0 : if (maxSize.width < aMaxSize)
863 0 : aMaxSize = maxSize.width;
864 :
865 : } else {
866 0 : if (minSize.height > aMinSize)
867 0 : aMinSize = minSize.height;
868 :
869 0 : if (maxSize.height < aMaxSize)
870 0 : aMaxSize = maxSize.height;
871 : }
872 :
873 0 : currentBox->collapsed = collapsed;
874 0 : aFlexes += currentBox->flex;
875 :
876 0 : child = child->GetNextBox();
877 :
878 0 : last = currentBox;
879 0 : currentBox = currentBox->next;
880 :
881 : }
882 :
883 0 : if (childCount > 0) {
884 0 : nscoord maxAllowedFlex = nscoord_MAX / childCount;
885 :
886 0 : if (NS_UNLIKELY(maxFlex > maxAllowedFlex)) {
887 : // clamp all the flexes
888 0 : currentBox = aBoxSizes;
889 0 : while (currentBox) {
890 0 : currentBox->flex = NS_MIN(currentBox->flex, maxAllowedFlex);
891 0 : currentBox = currentBox->next;
892 : }
893 : }
894 : }
895 : #ifdef DEBUG
896 : else {
897 0 : NS_ASSERTION(maxFlex == 0, "How did that happen?");
898 : }
899 : #endif
900 :
901 : // we specified all our children are equal size;
902 0 : if (frameState & NS_STATE_EQUAL_SIZE) {
903 0 : smallestMaxWidth = NS_MAX(smallestMaxWidth, biggestMinWidth);
904 0 : biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth);
905 :
906 0 : currentBox = aBoxSizes;
907 :
908 0 : while(currentBox)
909 : {
910 0 : if (!currentBox->collapsed) {
911 0 : currentBox->pref = biggestPrefWidth;
912 0 : currentBox->min = biggestMinWidth;
913 0 : currentBox->max = smallestMaxWidth;
914 : } else {
915 0 : currentBox->pref = 0;
916 0 : currentBox->min = 0;
917 0 : currentBox->max = 0;
918 : }
919 0 : currentBox = currentBox->next;
920 : }
921 : }
922 :
923 0 : }
924 :
925 : void
926 0 : nsSprocketLayout::ComputeChildsNextPosition(nsIBox* aBox,
927 : const nscoord& aCurX,
928 : const nscoord& aCurY,
929 : nscoord& aNextX,
930 : nscoord& aNextY,
931 : const nsRect& aCurrentChildSize)
932 : {
933 : // Get the position along the box axis for the child.
934 : // The out-of-axis position is not set.
935 0 : nsFrameState frameState = 0;
936 0 : GetFrameState(aBox, frameState);
937 :
938 0 : if (IsHorizontal(aBox)) {
939 : // horizontal box's children.
940 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
941 0 : aNextX = aCurX + aCurrentChildSize.width;
942 : else
943 0 : aNextX = aCurX - aCurrentChildSize.width;
944 :
945 : } else {
946 : // vertical box's children.
947 0 : if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
948 0 : aNextY = aCurY + aCurrentChildSize.height;
949 : else
950 0 : aNextY = aCurY - aCurrentChildSize.height;
951 : }
952 0 : }
953 :
954 : void
955 0 : nsSprocketLayout::AlignChildren(nsIBox* aBox,
956 : nsBoxLayoutState& aState,
957 : bool* aNeedsRedraw)
958 : {
959 0 : nsFrameState frameState = 0;
960 0 : GetFrameState(aBox, frameState);
961 0 : bool isHorizontal = (frameState & NS_STATE_IS_HORIZONTAL) != 0;
962 0 : nsRect clientRect;
963 0 : aBox->GetClientRect(clientRect);
964 :
965 0 : NS_PRECONDITION(!(frameState & NS_STATE_AUTO_STRETCH),
966 : "Only AlignChildren() with non-stretch alignment");
967 :
968 : // These are only calculated if needed
969 : nsIBox::Halignment halign;
970 : nsIBox::Valignment valign;
971 : nscoord maxAscent;
972 : bool isLTR;
973 :
974 0 : if (isHorizontal) {
975 0 : valign = aBox->GetVAlign();
976 0 : if (valign == nsBoxFrame::vAlign_BaseLine) {
977 0 : maxAscent = aBox->GetBoxAscent(aState);
978 : }
979 : } else {
980 0 : isLTR = GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR;
981 0 : halign = aBox->GetHAlign();
982 : }
983 :
984 0 : nsIBox* child = aBox->GetChildBox();
985 0 : while (child) {
986 :
987 0 : nsMargin margin;
988 0 : child->GetMargin(margin);
989 0 : nsRect childRect = child->GetRect();
990 :
991 0 : if (isHorizontal) {
992 0 : const nscoord startAlign = clientRect.y + margin.top;
993 : const nscoord endAlign =
994 0 : clientRect.YMost() - margin.bottom - childRect.height;
995 :
996 : nscoord y;
997 0 : switch (valign) {
998 : case nsBoxFrame::vAlign_Top:
999 0 : y = startAlign;
1000 0 : break;
1001 : case nsBoxFrame::vAlign_Middle:
1002 : // Should this center the border box?
1003 : // This centers the margin box, the historical behavior.
1004 0 : y = (startAlign + endAlign) / 2;
1005 0 : break;
1006 : case nsBoxFrame::vAlign_Bottom:
1007 0 : y = endAlign;
1008 0 : break;
1009 : case nsBoxFrame::vAlign_BaseLine:
1010 : // Alignments don't force the box to grow (only sizes do),
1011 : // so keep the children within the box.
1012 0 : y = maxAscent - child->GetBoxAscent(aState);
1013 0 : y = NS_MAX(startAlign, y);
1014 0 : y = NS_MIN(y, endAlign);
1015 0 : break;
1016 : }
1017 :
1018 0 : childRect.y = y;
1019 :
1020 : } else { // vertical box
1021 0 : const nscoord leftAlign = clientRect.x + margin.left;
1022 : const nscoord rightAlign =
1023 0 : clientRect.XMost() - margin.right - childRect.width;
1024 :
1025 : nscoord x;
1026 0 : switch (halign) {
1027 : case nsBoxFrame::hAlign_Left: // start
1028 0 : x = isLTR ? leftAlign : rightAlign;
1029 0 : break;
1030 : case nsBoxFrame::hAlign_Center:
1031 0 : x = (leftAlign + rightAlign) / 2;
1032 0 : break;
1033 : case nsBoxFrame::hAlign_Right: // end
1034 0 : x = isLTR ? rightAlign : leftAlign;
1035 0 : break;
1036 : }
1037 :
1038 0 : childRect.x = x;
1039 : }
1040 :
1041 0 : if (childRect.TopLeft() != child->GetPosition()) {
1042 0 : *aNeedsRedraw = true;
1043 0 : child->SetBounds(aState, childRect);
1044 : }
1045 :
1046 0 : child = child->GetNextBox();
1047 : }
1048 0 : }
1049 :
1050 : void
1051 0 : nsSprocketLayout::ChildResized(nsIBox* aBox,
1052 : nsBoxLayoutState& aState,
1053 : nsIBox* aChild,
1054 : nsBoxSize* aChildBoxSize,
1055 : nsComputedBoxSize* aChildComputedSize,
1056 : nsBoxSize* aBoxSizes,
1057 : nsComputedBoxSize* aComputedBoxSizes,
1058 : const nsRect& aChildLayoutRect,
1059 : nsRect& aChildActualRect,
1060 : nsRect& aContainingRect,
1061 : PRInt32 aFlexes,
1062 : bool& aFinished)
1063 :
1064 : {
1065 0 : nsRect childCurrentRect(aChildLayoutRect);
1066 :
1067 0 : bool isHorizontal = IsHorizontal(aBox);
1068 0 : nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal);
1069 0 : nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal);
1070 0 : nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal);
1071 :
1072 : //nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal);
1073 0 : nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal);
1074 0 : nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal);
1075 :
1076 0 : bool recompute = false;
1077 :
1078 : // if we are a horizontal box see if the child will fit inside us.
1079 0 : if ( childActualHeight > containingHeight) {
1080 : // if we are a horizontal box and the child is bigger than our height
1081 :
1082 : // ok if the height changed then we need to reflow everyone but us at the new height
1083 : // so we will set the changed index to be us. And signal that we need a new pass.
1084 :
1085 0 : nsSize min = aChild->GetMinSize(aState);
1086 0 : nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState));
1087 0 : AddMargin(aChild, max);
1088 :
1089 0 : if (isHorizontal)
1090 0 : childActualHeight = max.height < childActualHeight ? max.height : childActualHeight;
1091 : else
1092 0 : childActualHeight = max.width < childActualHeight ? max.width : childActualHeight;
1093 :
1094 : // only set if it changes
1095 0 : if (childActualHeight > containingHeight) {
1096 0 : containingHeight = childActualHeight;
1097 :
1098 : // remember we do not need to clear the resized list because changing the height of a horizontal box
1099 : // will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me
1100 : // on this one.
1101 0 : aFinished = false;
1102 :
1103 : // only recompute if there are flexes.
1104 0 : if (aFlexes > 0) {
1105 : // relayout everything
1106 0 : recompute = true;
1107 0 : InvalidateComputedSizes(aComputedBoxSizes);
1108 0 : nsComputedBoxSize* node = aComputedBoxSizes;
1109 :
1110 0 : while(node) {
1111 0 : node->resized = false;
1112 0 : node = node->next;
1113 : }
1114 :
1115 : }
1116 : }
1117 : }
1118 :
1119 0 : if (childActualWidth > childLayoutWidth) {
1120 0 : nsSize min = aChild->GetMinSize(aState);
1121 0 : nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState));
1122 :
1123 0 : AddMargin(aChild, max);
1124 :
1125 : // our width now becomes the new size
1126 :
1127 0 : if (isHorizontal)
1128 0 : childActualWidth = max.width < childActualWidth ? max.width : childActualWidth;
1129 : else
1130 0 : childActualWidth = max.height < childActualWidth ? max.height : childActualWidth;
1131 :
1132 0 : if (childActualWidth > childLayoutWidth) {
1133 0 : aChildComputedSize->size = childActualWidth;
1134 0 : aChildBoxSize->min = childActualWidth;
1135 0 : if (aChildBoxSize->pref < childActualWidth)
1136 0 : aChildBoxSize->pref = childActualWidth;
1137 0 : if (aChildBoxSize->max < childActualWidth)
1138 0 : aChildBoxSize->max = childActualWidth;
1139 :
1140 : // if we have flexible elements with us then reflex things. Otherwise we can skip doing it.
1141 0 : if (aFlexes > 0) {
1142 0 : InvalidateComputedSizes(aComputedBoxSizes);
1143 :
1144 0 : nsComputedBoxSize* node = aComputedBoxSizes;
1145 0 : aChildComputedSize->resized = true;
1146 :
1147 0 : while(node) {
1148 0 : if (node->resized)
1149 0 : node->valid = true;
1150 :
1151 0 : node = node->next;
1152 : }
1153 :
1154 0 : recompute = true;
1155 0 : aFinished = false;
1156 : } else {
1157 0 : containingWidth += aChildComputedSize->size - childLayoutWidth;
1158 : }
1159 : }
1160 : }
1161 :
1162 0 : if (recompute)
1163 0 : ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes);
1164 :
1165 0 : if (!childCurrentRect.IsEqualInterior(aChildActualRect)) {
1166 : // the childRect includes the margin
1167 : // make sure we remove it before setting
1168 : // the bounds.
1169 0 : nsMargin margin(0,0,0,0);
1170 0 : aChild->GetMargin(margin);
1171 0 : nsRect rect(aChildActualRect);
1172 0 : if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom)
1173 0 : rect.Deflate(margin);
1174 :
1175 0 : aChild->SetBounds(aState, rect);
1176 0 : aChild->Layout(aState);
1177 : }
1178 :
1179 0 : }
1180 :
1181 : void
1182 0 : nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes)
1183 : {
1184 0 : while(aComputedBoxSizes) {
1185 0 : aComputedBoxSizes->valid = false;
1186 0 : aComputedBoxSizes = aComputedBoxSizes->next;
1187 : }
1188 0 : }
1189 :
1190 : void
1191 0 : nsSprocketLayout::ComputeChildSizes(nsIBox* aBox,
1192 : nsBoxLayoutState& aState,
1193 : nscoord& aGivenSize,
1194 : nsBoxSize* aBoxSizes,
1195 : nsComputedBoxSize*& aComputedBoxSizes)
1196 : {
1197 :
1198 : //nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1);
1199 :
1200 0 : PRInt32 sizeRemaining = aGivenSize;
1201 0 : PRInt32 spacerConstantsRemaining = 0;
1202 :
1203 : // ----- calculate the spacers constants and the size remaining -----
1204 :
1205 0 : if (!aComputedBoxSizes)
1206 0 : aComputedBoxSizes = new (aState) nsComputedBoxSize();
1207 :
1208 0 : nsBoxSize* boxSizes = aBoxSizes;
1209 0 : nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes;
1210 0 : PRInt32 count = 0;
1211 0 : PRInt32 validCount = 0;
1212 :
1213 0 : while (boxSizes)
1214 : {
1215 :
1216 0 : NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size");
1217 :
1218 :
1219 : // ignore collapsed children
1220 : // if (boxSizes->collapsed)
1221 : // {
1222 : // computedBoxSizes->valid = true;
1223 : // computedBoxSizes->size = boxSizes->pref;
1224 : // validCount++;
1225 : // boxSizes->flex = 0;
1226 : // }// else {
1227 :
1228 0 : if (computedBoxSizes->valid) {
1229 0 : sizeRemaining -= computedBoxSizes->size;
1230 0 : validCount++;
1231 : } else {
1232 0 : if (boxSizes->flex == 0)
1233 : {
1234 0 : computedBoxSizes->valid = true;
1235 0 : computedBoxSizes->size = boxSizes->pref;
1236 0 : validCount++;
1237 : }
1238 :
1239 0 : spacerConstantsRemaining += boxSizes->flex;
1240 0 : sizeRemaining -= boxSizes->pref;
1241 : }
1242 :
1243 0 : sizeRemaining -= (boxSizes->left + boxSizes->right);
1244 :
1245 : //}
1246 :
1247 0 : boxSizes = boxSizes->next;
1248 :
1249 0 : if (boxSizes && !computedBoxSizes->next)
1250 0 : computedBoxSizes->next = new (aState) nsComputedBoxSize();
1251 :
1252 0 : computedBoxSizes = computedBoxSizes->next;
1253 0 : count++;
1254 : }
1255 :
1256 : // everything accounted for?
1257 0 : if (validCount < count)
1258 : {
1259 : // ----- Ok we are give a size to fit into so stretch or squeeze to fit
1260 : // ----- Make sure we look at our min and max size
1261 0 : bool limit = true;
1262 0 : for (int pass=1; true == limit; pass++)
1263 : {
1264 0 : limit = false;
1265 0 : boxSizes = aBoxSizes;
1266 0 : computedBoxSizes = aComputedBoxSizes;
1267 :
1268 0 : while (boxSizes) {
1269 :
1270 : // ignore collapsed spacers
1271 :
1272 : // if (!boxSizes->collapsed) {
1273 :
1274 0 : nscoord pref = 0;
1275 0 : nscoord max = NS_INTRINSICSIZE;
1276 0 : nscoord min = 0;
1277 0 : nscoord flex = 0;
1278 :
1279 0 : pref = boxSizes->pref;
1280 0 : min = boxSizes->min;
1281 0 : max = boxSizes->max;
1282 0 : flex = boxSizes->flex;
1283 :
1284 : // ----- look at our min and max limits make sure we aren't too small or too big -----
1285 0 : if (!computedBoxSizes->valid) {
1286 0 : PRInt32 newSize = pref + PRInt32(PRInt64(sizeRemaining) * flex / spacerConstantsRemaining);
1287 :
1288 0 : if (newSize<=min) {
1289 0 : computedBoxSizes->size = min;
1290 0 : computedBoxSizes->valid = true;
1291 0 : spacerConstantsRemaining -= flex;
1292 0 : sizeRemaining += pref;
1293 0 : sizeRemaining -= min;
1294 0 : limit = true;
1295 0 : } else if (newSize>=max) {
1296 0 : computedBoxSizes->size = max;
1297 0 : computedBoxSizes->valid = true;
1298 0 : spacerConstantsRemaining -= flex;
1299 0 : sizeRemaining += pref;
1300 0 : sizeRemaining -= max;
1301 0 : limit = true;
1302 : }
1303 : }
1304 : // }
1305 0 : boxSizes = boxSizes->next;
1306 0 : computedBoxSizes = computedBoxSizes->next;
1307 : }
1308 : }
1309 : }
1310 :
1311 : // ---- once we have removed and min and max issues just stretch us out in the remaining space
1312 : // ---- or shrink us. Depends on the size remaining and the spacer constants
1313 0 : aGivenSize = 0;
1314 0 : boxSizes = aBoxSizes;
1315 0 : computedBoxSizes = aComputedBoxSizes;
1316 :
1317 0 : while (boxSizes) {
1318 :
1319 : // ignore collapsed spacers
1320 : // if (!(boxSizes && boxSizes->collapsed)) {
1321 :
1322 0 : nscoord pref = 0;
1323 0 : nscoord flex = 0;
1324 0 : pref = boxSizes->pref;
1325 0 : flex = boxSizes->flex;
1326 :
1327 0 : if (!computedBoxSizes->valid) {
1328 0 : computedBoxSizes->size = pref + PRInt32(PRInt64(sizeRemaining) * flex / spacerConstantsRemaining);
1329 0 : computedBoxSizes->valid = true;
1330 : }
1331 :
1332 0 : aGivenSize += (boxSizes->left + boxSizes->right);
1333 0 : aGivenSize += computedBoxSizes->size;
1334 :
1335 : // }
1336 :
1337 0 : boxSizes = boxSizes->next;
1338 0 : computedBoxSizes = computedBoxSizes->next;
1339 : }
1340 0 : }
1341 :
1342 :
1343 : nsSize
1344 0 : nsSprocketLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aState)
1345 : {
1346 0 : nsSize vpref (0, 0);
1347 0 : bool isHorizontal = IsHorizontal(aBox);
1348 :
1349 0 : nscoord biggestPref = 0;
1350 :
1351 : // run through all the children and get their min, max, and preferred sizes
1352 : // return us the size of the box
1353 :
1354 0 : nsIBox* child = aBox->GetChildBox();
1355 0 : nsFrameState frameState = 0;
1356 0 : GetFrameState(aBox, frameState);
1357 0 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1358 0 : PRInt32 count = 0;
1359 :
1360 0 : while (child)
1361 : {
1362 : // ignore collapsed children
1363 0 : if (!child->IsCollapsed())
1364 : {
1365 0 : nsSize pref = child->GetPrefSize(aState);
1366 0 : AddMargin(child, pref);
1367 :
1368 0 : if (isEqual) {
1369 0 : if (isHorizontal)
1370 : {
1371 0 : if (pref.width > biggestPref)
1372 0 : biggestPref = pref.width;
1373 : } else {
1374 0 : if (pref.height > biggestPref)
1375 0 : biggestPref = pref.height;
1376 : }
1377 : }
1378 :
1379 0 : AddLargestSize(vpref, pref, isHorizontal);
1380 0 : count++;
1381 : }
1382 :
1383 0 : child = child->GetNextBox();
1384 : }
1385 :
1386 0 : if (isEqual) {
1387 0 : if (isHorizontal)
1388 0 : vpref.width = biggestPref*count;
1389 : else
1390 0 : vpref.height = biggestPref*count;
1391 : }
1392 :
1393 : // now add our border and padding
1394 0 : AddBorderAndPadding(aBox, vpref);
1395 :
1396 : return vpref;
1397 : }
1398 :
1399 : nsSize
1400 0 : nsSprocketLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aState)
1401 : {
1402 0 : nsSize minSize (0, 0);
1403 0 : bool isHorizontal = IsHorizontal(aBox);
1404 :
1405 0 : nscoord biggestMin = 0;
1406 :
1407 :
1408 : // run through all the children and get their min, max, and preferred sizes
1409 : // return us the size of the box
1410 :
1411 0 : nsIBox* child = aBox->GetChildBox();
1412 0 : nsFrameState frameState = 0;
1413 0 : GetFrameState(aBox, frameState);
1414 0 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1415 0 : PRInt32 count = 0;
1416 :
1417 0 : while (child)
1418 : {
1419 : // ignore collapsed children
1420 0 : if (!child->IsCollapsed())
1421 : {
1422 0 : nsSize min = child->GetMinSize(aState);
1423 0 : nsSize pref(0,0);
1424 :
1425 : // if the child is not flexible then
1426 : // its min size is its pref size.
1427 0 : if (child->GetFlex(aState) == 0) {
1428 0 : pref = child->GetPrefSize(aState);
1429 0 : if (isHorizontal)
1430 0 : min.width = pref.width;
1431 : else
1432 0 : min.height = pref.height;
1433 : }
1434 :
1435 0 : if (isEqual) {
1436 0 : if (isHorizontal)
1437 : {
1438 0 : if (min.width > biggestMin)
1439 0 : biggestMin = min.width;
1440 : } else {
1441 0 : if (min.height > biggestMin)
1442 0 : biggestMin = min.height;
1443 : }
1444 : }
1445 :
1446 0 : AddMargin(child, min);
1447 0 : AddLargestSize(minSize, min, isHorizontal);
1448 0 : count++;
1449 : }
1450 :
1451 0 : child = child->GetNextBox();
1452 : }
1453 :
1454 :
1455 0 : if (isEqual) {
1456 0 : if (isHorizontal)
1457 0 : minSize.width = biggestMin*count;
1458 : else
1459 0 : minSize.height = biggestMin*count;
1460 : }
1461 :
1462 : // now add our border and padding
1463 0 : AddBorderAndPadding(aBox, minSize);
1464 :
1465 : return minSize;
1466 : }
1467 :
1468 : nsSize
1469 0 : nsSprocketLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aState)
1470 : {
1471 :
1472 0 : bool isHorizontal = IsHorizontal(aBox);
1473 :
1474 0 : nscoord smallestMax = NS_INTRINSICSIZE;
1475 0 : nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1476 :
1477 : // run through all the children and get their min, max, and preferred sizes
1478 : // return us the size of the box
1479 :
1480 0 : nsIBox* child = aBox->GetChildBox();
1481 0 : nsFrameState frameState = 0;
1482 0 : GetFrameState(aBox, frameState);
1483 0 : bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
1484 0 : PRInt32 count = 0;
1485 :
1486 0 : while (child)
1487 : {
1488 : // ignore collapsed children
1489 0 : if (!child->IsCollapsed())
1490 : {
1491 : // if completely redefined don't even ask our child for its size.
1492 0 : nsSize min = child->GetMinSize(aState);
1493 0 : nsSize max = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState));
1494 :
1495 0 : AddMargin(child, max);
1496 0 : AddSmallestSize(maxSize, max, isHorizontal);
1497 :
1498 0 : if (isEqual) {
1499 0 : if (isHorizontal)
1500 : {
1501 0 : if (max.width < smallestMax)
1502 0 : smallestMax = max.width;
1503 : } else {
1504 0 : if (max.height < smallestMax)
1505 0 : smallestMax = max.height;
1506 : }
1507 : }
1508 0 : count++;
1509 : }
1510 :
1511 0 : child = child->GetNextBox();
1512 : }
1513 :
1514 0 : if (isEqual) {
1515 0 : if (isHorizontal) {
1516 0 : if (smallestMax != NS_INTRINSICSIZE)
1517 0 : maxSize.width = smallestMax*count;
1518 : else
1519 0 : maxSize.width = NS_INTRINSICSIZE;
1520 : } else {
1521 0 : if (smallestMax != NS_INTRINSICSIZE)
1522 0 : maxSize.height = smallestMax*count;
1523 : else
1524 0 : maxSize.height = NS_INTRINSICSIZE;
1525 : }
1526 : }
1527 :
1528 : // now add our border and padding
1529 0 : AddBorderAndPadding(aBox, maxSize);
1530 :
1531 : return maxSize;
1532 : }
1533 :
1534 :
1535 : nscoord
1536 0 : nsSprocketLayout::GetAscent(nsIBox* aBox, nsBoxLayoutState& aState)
1537 : {
1538 0 : nscoord vAscent = 0;
1539 :
1540 0 : bool isHorizontal = IsHorizontal(aBox);
1541 :
1542 : // run through all the children and get their min, max, and preferred sizes
1543 : // return us the size of the box
1544 :
1545 0 : nsIBox* child = aBox->GetChildBox();
1546 :
1547 0 : while (child)
1548 : {
1549 : // ignore collapsed children
1550 : //if (!child->IsCollapsed())
1551 : //{
1552 : // if completely redefined don't even ask our child for its size.
1553 0 : nscoord ascent = child->GetBoxAscent(aState);
1554 :
1555 0 : nsMargin margin;
1556 0 : child->GetMargin(margin);
1557 0 : ascent += margin.top;
1558 :
1559 0 : if (isHorizontal)
1560 : {
1561 0 : if (ascent > vAscent)
1562 0 : vAscent = ascent;
1563 : } else {
1564 0 : if (vAscent == 0)
1565 0 : vAscent = ascent;
1566 : }
1567 : //}
1568 :
1569 0 : child = child->GetNextBox();
1570 : }
1571 :
1572 0 : nsMargin borderPadding;
1573 0 : aBox->GetBorderAndPadding(borderPadding);
1574 :
1575 0 : return vAscent + borderPadding.top;
1576 : }
1577 :
1578 : void
1579 0 : nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal)
1580 : {
1581 0 : if (aIsHorizontal)
1582 : {
1583 0 : if (aSize1.height < aSize2.height)
1584 0 : aSize1.height = aSize2.height;
1585 : } else {
1586 0 : if (aSize1.width < aSize2.width)
1587 0 : aSize1.width = aSize2.width;
1588 : }
1589 0 : }
1590 :
1591 : void
1592 0 : nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal)
1593 : {
1594 0 : if (aIsHorizontal)
1595 : {
1596 0 : if (aSize1.height > aSize2.height)
1597 0 : aSize1.height = aSize2.height;
1598 : } else {
1599 0 : if (aSize1.width > aSize2.width)
1600 0 : aSize1.width = aSize2.width;
1601 :
1602 : }
1603 0 : }
1604 :
1605 : void
1606 0 : nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal)
1607 : {
1608 0 : if (aIsHorizontal)
1609 0 : AddCoord(aSize.width, aSizeToAdd.width);
1610 : else
1611 0 : AddCoord(aSize.height, aSizeToAdd.height);
1612 :
1613 0 : SetLargestSize(aSize, aSizeToAdd, aIsHorizontal);
1614 0 : }
1615 :
1616 : void
1617 0 : nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd)
1618 : {
1619 0 : if (aCoord != NS_INTRINSICSIZE)
1620 : {
1621 0 : if (aCoordToAdd == NS_INTRINSICSIZE)
1622 0 : aCoord = aCoordToAdd;
1623 : else
1624 0 : aCoord += aCoordToAdd;
1625 : }
1626 0 : }
1627 : void
1628 0 : nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal)
1629 : {
1630 0 : if (aIsHorizontal)
1631 0 : AddCoord(aSize.width, aSizeToAdd.width);
1632 : else
1633 0 : AddCoord(aSize.height, aSizeToAdd.height);
1634 :
1635 0 : SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal);
1636 0 : }
1637 :
1638 : bool
1639 0 : nsSprocketLayout::GetDefaultFlex(PRInt32& aFlex)
1640 : {
1641 0 : aFlex = 0;
1642 0 : return true;
1643 : }
1644 :
1645 0 : nsComputedBoxSize::nsComputedBoxSize()
1646 : {
1647 0 : resized = false;
1648 0 : valid = false;
1649 0 : size = 0;
1650 0 : next = nsnull;
1651 0 : }
1652 :
1653 0 : nsBoxSize::nsBoxSize()
1654 : {
1655 0 : pref = 0;
1656 0 : min = 0;
1657 0 : max = NS_INTRINSICSIZE;
1658 0 : collapsed = false;
1659 0 : left = 0;
1660 0 : right = 0;
1661 0 : flex = 0;
1662 0 : next = nsnull;
1663 0 : bogus = false;
1664 0 : }
1665 :
1666 :
1667 : void*
1668 0 : nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
1669 : {
1670 0 : return aState.AllocateStackMemory(sz);
1671 : }
1672 :
1673 :
1674 : void
1675 0 : nsBoxSize::operator delete(void* aPtr, size_t sz)
1676 : {
1677 0 : }
1678 :
1679 :
1680 : void*
1681 0 : nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
1682 : {
1683 0 : return aState.AllocateStackMemory(sz);
1684 : }
1685 :
1686 : void
1687 0 : nsComputedBoxSize::operator delete(void* aPtr, size_t sz)
1688 : {
1689 0 : }
|