Fix the line truncation function for Emerson so that at the far left setting of...
[WebKit-https.git] / WebCore / khtml / rendering / render_flexbox.cpp
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "render_flexbox.h"
26
27 using namespace DOM;
28
29 namespace khtml {
30
31 class FlexBoxIterator {
32 public:
33     FlexBoxIterator(RenderFlexibleBox* parent) {
34         box = parent;
35         if (box->style()->boxOrient() == HORIZONTAL && box->style()->direction() == RTL)
36             forward = box->style()->boxDirection() != BNORMAL;
37         else
38             forward = box->style()->boxDirection() == BNORMAL;
39         lastOrdinal = 1; 
40         if (!forward) {
41             // No choice, since we're going backwards, we have to find out the highest ordinal up front.
42             RenderObject* child = box->firstChild();
43             while (child) {
44                 if (child->style()->boxOrdinalGroup() > lastOrdinal)
45                     lastOrdinal = child->style()->boxOrdinalGroup();
46                 child = child->nextSibling();
47             }
48         }
49         
50         reset();
51     }
52
53     void reset() {
54         current = 0;
55         currentOrdinal = forward ? 0 : lastOrdinal+1;
56     }
57
58     RenderObject* first() {
59         reset();
60         return next();
61     }
62     
63     RenderObject* next() {
64
65         do { 
66             if (!current) {
67                 if (forward) {
68                     currentOrdinal++; 
69                     if (currentOrdinal > lastOrdinal)
70                         return 0;
71                     current = box->firstChild();
72                 }
73                 else {
74                     currentOrdinal--;
75                     if (currentOrdinal == 0)
76                         return 0;
77                     current = box->lastChild();
78                 }
79             }
80             else
81                 current = forward ? current->nextSibling() : current->previousSibling();
82             if (current && current->style()->boxOrdinalGroup() > lastOrdinal)
83                 lastOrdinal = current->style()->boxOrdinalGroup();
84         } while (!current || current->style()->boxOrdinalGroup() != currentOrdinal ||
85                  current->style()->visibility() == COLLAPSE);
86         return current;
87     }
88
89 private:
90     RenderFlexibleBox* box;
91     RenderObject* current;
92     bool forward;
93     unsigned int currentOrdinal;
94     unsigned int lastOrdinal;
95 };
96     
97 RenderFlexibleBox::RenderFlexibleBox(DOM::NodeImpl* node)
98 :RenderBlock(node)
99 {
100     setChildrenInline(false); // All of our children must be block-level
101     m_flexingChildren = m_stretchingChildren = false;
102 }
103
104 RenderFlexibleBox::~RenderFlexibleBox()
105 {
106 }
107
108 void RenderFlexibleBox::calcHorizontalMinMaxWidth()
109 {
110     RenderObject *child = firstChild();
111     while (child) {
112         // positioned children don't affect the minmaxwidth
113         if (child->isPositioned() || child->style()->visibility() == COLLAPSE)
114         {
115             child = child->nextSibling();
116             continue;
117         }
118
119         int margin=0;
120         //  auto margins don't affect minwidth
121
122         Length ml = child->style()->marginLeft();
123         Length mr = child->style()->marginRight();
124
125         // Call calcWidth on the child to ensure that our margins are
126         // up to date.  This method can be called before the child has actually
127         // calculated its margins (which are computed inside calcWidth).
128         child->calcWidth();
129
130         if (!(ml.type==Variable) && !(mr.type==Variable))
131         {
132             if (!(child->style()->width().type==Variable))
133             {
134                 if (child->style()->direction()==LTR)
135                     margin = child->marginLeft();
136                 else
137                     margin = child->marginRight();
138             }
139             else
140                 margin = child->marginLeft()+child->marginRight();
141
142         }
143         else if (!(ml.type == Variable))
144             margin = child->marginLeft();
145         else if (!(mr.type == Variable))
146             margin = child->marginRight();
147
148         if (margin < 0) margin = 0;
149
150         m_minWidth += child->minWidth() + margin;
151         m_maxWidth += child->maxWidth() + margin;
152
153         child = child->nextSibling();
154     }    
155 }
156
157 void RenderFlexibleBox::calcVerticalMinMaxWidth()
158 {
159     RenderObject *child = firstChild();
160     while(child != 0)
161     {
162         // Positioned children and collapsed children don't affect the min/max width
163         if (child->isPositioned() || child->style()->visibility() == COLLAPSE) {
164             child = child->nextSibling();
165             continue;
166         }
167
168         Length ml = child->style()->marginLeft();
169         Length mr = child->style()->marginRight();
170
171         // Call calcWidth on the child to ensure that our margins are
172         // up to date.  This method can be called before the child has actually
173         // calculated its margins (which are computed inside calcWidth).
174         if (ml.type == Percent || mr.type == Percent)
175             calcWidth();
176
177         // A margin basically has three types: fixed, percentage, and auto (variable).
178         // Auto margins simply become 0 when computing min/max width.
179         // Fixed margins can be added in as is.
180         // Percentage margins are computed as a percentage of the width we calculated in
181         // the calcWidth call above.  In this case we use the actual cached margin values on
182         // the RenderObject itself.
183         int margin = 0;
184         if (ml.type == Fixed)
185             margin += ml.value;
186         else if (ml.type == Percent)
187             margin += child->marginLeft();
188
189         if (mr.type == Fixed)
190             margin += mr.value;
191         else if (mr.type == Percent)
192             margin += child->marginRight();
193
194         if (margin < 0) margin = 0;
195         
196         int w = child->minWidth() + margin;
197         if(m_minWidth < w) m_minWidth = w;
198         
199         w = child->maxWidth() + margin;
200
201         if(m_maxWidth < w) m_maxWidth = w;
202
203         child = child->nextSibling();
204     }    
205 }
206
207 void RenderFlexibleBox::calcMinMaxWidth()
208 {
209     KHTMLAssert( !minMaxKnown() );
210
211     m_minWidth = 0;
212     m_maxWidth = 0;
213
214     if (hasMultipleLines() || isVertical())
215         calcVerticalMinMaxWidth();
216     else
217         calcHorizontalMinMaxWidth();
218
219     if(m_maxWidth < m_minWidth) m_maxWidth = m_minWidth;
220
221     if (style()->width().isFixed() && style()->width().value > 0)
222         m_minWidth = m_maxWidth = style()->width().value;
223    
224     if (style()->minWidth().isFixed() && style()->minWidth().value > 0) {
225         m_maxWidth = kMax(m_maxWidth, style()->minWidth().value);
226         m_minWidth = kMax(m_minWidth, style()->minWidth().value);
227     }
228     
229     if (style()->maxWidth().isFixed() && style()->maxWidth().value != UNDEFINED) {
230         m_maxWidth = kMin(m_maxWidth, style()->maxWidth().value);
231         m_minWidth = kMin(m_minWidth, style()->maxWidth().value);
232     }
233
234     int toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight();
235     m_minWidth += toAdd;
236     m_maxWidth += toAdd;
237
238     setMinMaxKnown();
239 }
240
241 void RenderFlexibleBox::layoutBlock(bool relayoutChildren)
242 {
243     KHTMLAssert(needsLayout());
244     KHTMLAssert(minMaxKnown());
245
246     if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
247         // All we have to is lay out our positioned objects.
248         layoutPositionedObjects(relayoutChildren);
249         if (hasOverflowClip())
250             m_layer->updateScrollInfoAfterLayout();
251         setNeedsLayout(false);
252         return;
253     }
254
255     QRect oldBounds;
256     bool checkForRepaint = checkForRepaintDuringLayout();
257     if (checkForRepaint)
258         oldBounds = getAbsoluteRepaintRect();
259     
260     int oldWidth = m_width;
261     int oldHeight = m_height;
262     
263     calcWidth();
264     calcHeight();
265     m_overflowWidth = m_width;
266
267     if (oldWidth != m_width || oldHeight != m_height ||
268         (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL &&
269          parent()->style()->boxAlign() == BSTRETCH))
270         relayoutChildren = true;
271
272     m_height = 0;
273     m_overflowHeight = 0;
274     m_flexingChildren = m_stretchingChildren = false;
275
276     initMaxMarginValues();
277
278     if (scrollsOverflow()) {
279         // For overflow:scroll blocks, ensure we have both scrollbars in place always.
280         if (style()->overflow() == OSCROLL) {
281             m_layer->setHasHorizontalScrollbar(true);
282             m_layer->setHasVerticalScrollbar(true);
283         }
284
285         // Move the scrollbars aside during layout.  The layer will move them back when it
286         // does painting or event handling.
287         m_layer->moveScrollbarsAside();
288     }
289
290     if (isHorizontal())
291         layoutHorizontalBox(relayoutChildren);
292     else
293         layoutVerticalBox(relayoutChildren);
294     
295     oldHeight = m_height;
296     calcHeight();
297     if (oldHeight != m_height) {
298         relayoutChildren = true;
299
300         // If the block got expanded in size, then increase our overflowheight to match.
301         if (m_overflowHeight > m_height)
302             m_overflowHeight -= (borderBottom()+paddingBottom());
303         if (m_overflowHeight < m_height)
304             m_overflowHeight = m_height;
305     }
306
307     layoutPositionedObjects( relayoutChildren );
308
309     //kdDebug() << renderName() << " layout width=" << m_width << " height=" << m_height << endl;
310
311     if (!isFloatingOrPositioned() && m_height == 0) {
312         // We are a block with no border and padding and a computed height
313         // of 0.  The CSS spec states that zero-height blocks collapse their margins
314         // together.
315         // When blocks are self-collapsing, we just use the top margin values and set the
316         // bottom margin max values to 0.  This way we don't factor in the values
317         // twice when we collapse with our previous vertically adjacent and
318         // following vertically adjacent blocks.
319         if (m_maxBottomPosMargin > m_maxTopPosMargin)
320             m_maxTopPosMargin = m_maxBottomPosMargin;
321         if (m_maxBottomNegMargin > m_maxTopNegMargin)
322             m_maxTopNegMargin = m_maxBottomNegMargin;
323         m_maxBottomNegMargin = m_maxBottomPosMargin = 0;
324     }
325
326     // Always ensure our overflow width is at least as large as our width.
327     if (m_overflowWidth < m_width)
328         m_overflowWidth = m_width;
329
330     // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if
331     // we overflow or not.
332     if (hasOverflowClip())
333         m_layer->updateScrollInfoAfterLayout();
334
335     // Repaint with our new bounds if they are different from our old bounds.
336     if (checkForRepaint)
337         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
338     
339     setNeedsLayout(false);
340 }
341
342 void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
343 {
344     int toAdd = borderBottom() + paddingBottom();
345     int yPos = borderTop() + paddingTop();
346     int xPos = borderLeft() + paddingLeft();
347     bool heightSpecified = false;
348     int oldHeight = 0;
349     
350     unsigned int highestFlexGroup = 0;
351     unsigned int lowestFlexGroup = 0;
352     bool haveFlex = false;
353     int remainingSpace = 0;
354     m_overflowHeight = m_height;
355
356     // The first walk over our kids is to find out if we have any flexible children.
357     FlexBoxIterator iterator(this);
358     RenderObject* child = iterator.next();
359     while (child) {
360         // Check to see if this child flexes.
361         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
362             // We always have to lay out flexible objects again, since the flex distribution
363             // may have changed, and we need to reallocate space.
364             child->setOverrideSize(-1);
365             if (!relayoutChildren)
366                 child->setChildNeedsLayout(true, false);
367             haveFlex = true;
368             unsigned int flexGroup = child->style()->boxFlexGroup();
369             if (lowestFlexGroup == 0)
370                 lowestFlexGroup = flexGroup;
371             if (flexGroup < lowestFlexGroup)
372                 lowestFlexGroup = flexGroup;
373             if (flexGroup > highestFlexGroup)
374                 highestFlexGroup = flexGroup;
375         }
376         child = iterator.next();
377     }
378     
379     // We do 2 passes.  The first pass is simply to lay everyone out at
380     // their preferred widths.  The second pass handles flexing the children.
381     do {
382         // Reset our height.
383         m_height = yPos;
384         m_overflowHeight = m_height;
385         xPos = borderLeft() + paddingLeft();
386                 
387         // Our first pass is done without flexing.  We simply lay the children
388         // out within the box.  We have to do a layout first in order to determine
389         // our box's intrinsic height.
390         int maxAscent = 0, maxDescent = 0;
391         child = iterator.first();
392         while (child) {
393             // make sure we relayout children if we need it.
394             if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))
395                 child->setChildNeedsLayout(true, false);
396             
397             if (child->isPositioned()) {
398                 child = iterator.next();
399                 continue;
400             }
401     
402             // Compute the child's vertical margins.
403             child->calcVerticalMargins();
404     
405             // Now do the layout.
406             child->layoutIfNeeded();
407     
408             // Update our height and overflow height.
409             if (style()->boxAlign() == BBASELINE) {
410                 int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
411                 if (ascent == -1)
412                     ascent = child->marginTop() + child->height() + child->marginBottom();
413                 int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent;
414                 
415                 // Update our maximum ascent.
416                 maxAscent = kMax(maxAscent, ascent);
417                 
418                 // Update our maximum descent.
419                 maxDescent = kMax(maxDescent, descent);
420                 
421                 // Now update our height.
422                 m_height = kMax(yPos + maxAscent + maxDescent, m_height);
423             }
424             else
425                 m_height = kMax(m_height, yPos + child->marginTop() + child->height() + child->marginBottom());
426
427             child = iterator.next();
428         }
429         m_height += toAdd;
430
431         // Always make sure our overflowheight is at least our height.
432         if (m_overflowHeight < m_height)
433             m_overflowHeight = m_height;
434         
435         oldHeight = m_height;
436         calcHeight();
437
438         relayoutChildren = false;
439         if (oldHeight != m_height) {
440             heightSpecified = true;
441     
442             // If the block got expanded in size, then increase our overflowheight to match.
443             if (m_overflowHeight > m_height)
444                 m_overflowHeight -= (borderBottom() + paddingBottom());
445             if (m_overflowHeight < m_height)
446                 m_overflowHeight = m_height;
447         }
448         
449         // Now that our height is actually known, we can place our boxes.
450         m_stretchingChildren = (style()->boxAlign() == BSTRETCH);
451         child = iterator.first();
452         while (child) {
453             if (child->isPositioned()) {
454                 child->containingBlock()->insertPositionedObject(child);
455                 if (child->hasStaticX()) {
456                     if (style()->direction() == LTR)
457                         child->setStaticX(xPos);
458                     else child->setStaticX(width() - xPos);
459                 }
460                 if (child->hasStaticY())
461                     child->setStaticY(yPos);
462                 child = iterator.next();
463                 continue;
464             }
465     
466             // We need to see if this child's height has changed, since we make block elements
467             // fill the height of a containing box by default.
468             // Now do a layout.
469             int oldChildHeight = child->height();
470             static_cast<RenderBox*>(child)->calcHeight();
471             if (oldChildHeight != child->height())
472                 child->setChildNeedsLayout(true, false);
473             child->layoutIfNeeded();
474     
475             // We can place the child now, using our value of box-align.
476             xPos += child->marginLeft();
477             int childY = yPos;
478             switch (style()->boxAlign()) {
479                 case BCENTER:
480                     childY += child->marginTop() + (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2;
481                     break;
482                 case BBASELINE: {
483                     int ascent = child->marginTop() + child->getBaselineOfFirstLineBox();
484                     if (ascent == -1)
485                         ascent = child->marginTop() + child->height() + child->marginBottom();
486                     childY += child->marginTop() + (maxAscent - ascent);
487                     break;
488                 }
489                 case BEND:
490                     childY += contentHeight() - child->marginBottom() - child->height();
491                     break;
492                 default: // BSTART
493                     childY += child->marginTop();
494                     break;
495             }
496
497             placeChild(child, xPos, childY);
498             m_overflowHeight = kMax(m_overflowHeight, childY + child->overflowHeight(false));
499             
500             xPos += child->width() + child->marginRight();
501     
502             child = iterator.next();
503         }
504
505         remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos;
506         
507         m_stretchingChildren = false;
508         if (m_flexingChildren)
509             haveFlex = false; // We're done.
510         else if (haveFlex) {
511             // We have some flexible objects.  See if we need to grow/shrink them at all.
512             if (!remainingSpace)
513                 break;
514
515             // Allocate the remaining space among the flexible objects.  If we are trying to
516             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
517             // we go from the highest flex group to the lowest group.
518             bool expanding = remainingSpace > 0;
519             unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
520             unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
521             for (unsigned int i = start; i <= end && remainingSpace; i++) {
522                 // Always start off by assuming the group can get all the remaining space.
523                 int groupRemainingSpace = remainingSpace;
524                 do {
525                     // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
526                     // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
527                     // computing the allowed growth before an object hits its min/max width (and thus
528                     // forces a totalFlex recomputation).
529                     float totalFlex = 0.0f;
530                     child = iterator.first();
531                     while (child) {
532                         if (allowedChildFlex(child, expanding, i))
533                             totalFlex += child->style()->boxFlex();
534                         child = iterator.next();
535                     }
536                     child = iterator.first();
537                     int spaceAvailableThisPass = groupRemainingSpace;
538                     while (child) {
539                         int allowedFlex = allowedChildFlex(child, expanding, i);
540                         if (allowedFlex) {
541                             int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex()));
542                             spaceAvailableThisPass = expanding ? kMin(spaceAvailableThisPass, projectedFlex) : kMax(spaceAvailableThisPass, projectedFlex);
543                         }
544                         child = iterator.next();
545                     }
546
547                     // The flex groups may not have any flexible objects this time around. 
548                     if (!spaceAvailableThisPass || totalFlex == 0.0f) {
549                         // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
550                         groupRemainingSpace = 0;
551                         continue;
552                     }
553
554                     // Now distribute the space to objects.
555                     child = iterator.first();
556                     while (child && spaceAvailableThisPass && totalFlex) {
557                         if (allowedChildFlex(child, expanding, i)) {
558                             int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex));
559                             if (spaceAdd) {
560                                 child->setOverrideSize(child->overrideWidth() + spaceAdd);
561                                 m_flexingChildren = true;
562                                 relayoutChildren = true;
563                             }
564
565                             spaceAvailableThisPass -= spaceAdd;
566                             remainingSpace -= spaceAdd;
567                             groupRemainingSpace -= spaceAdd;
568                             
569                             totalFlex -= child->style()->boxFlex();
570                         }
571                         child = iterator.next();
572                     }
573                 } while (groupRemainingSpace);
574             }
575
576             // We didn't find any children that could grow.
577             if (haveFlex && !m_flexingChildren)
578                 haveFlex = false;
579         }
580     } while (haveFlex);
581
582     m_flexingChildren = false;
583     
584     if (xPos > m_overflowWidth)
585         m_overflowWidth = xPos;
586
587     if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) ||
588                                (style()->direction() == RTL && style()->boxPack() != BEND))) {
589         // Children must be repositioned.
590         int offset = 0;
591         if (style()->boxPack() == BJUSTIFY) {
592             // Determine the total number of children.
593             int totalChildren = 0;
594             child = iterator.first();
595             while (child) {
596                 if (child->isPositioned()) {
597                     child = iterator.next();
598                     continue;
599                 }
600                 totalChildren++;
601                 child = iterator.next();
602             }
603
604             // Iterate over the children and space them out according to the
605             // justification level.
606             if (totalChildren > 1) {
607                 totalChildren--;
608                 bool firstChild = true;
609                 child = iterator.first();
610                 while (child) {
611                     if (child->isPositioned()) {
612                         child = iterator.next();
613                         continue;
614                     }
615
616                     if (firstChild) {
617                         firstChild = false;
618                         child = iterator.next();
619                         continue;
620                     }
621
622                     offset += remainingSpace/totalChildren;
623                     remainingSpace -= (remainingSpace/totalChildren);
624                     totalChildren--;
625
626                     placeChild(child, child->xPos()+offset, child->yPos());
627                     child = iterator.next();
628                 }
629             }
630         }
631         else {
632             if (style()->boxPack() == BCENTER)
633                 offset += remainingSpace/2;
634             else // END for LTR, START for RTL
635                 offset += remainingSpace;
636             child = iterator.first();
637             while (child) {
638                 if (child->isPositioned()) {
639                     child = iterator.next();
640                     continue;
641                 }
642                 placeChild(child, child->xPos()+offset, child->yPos());
643                 child = iterator.next();
644             }
645         }
646     }
647     
648     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
649     // a height change, we revert our height back to the intrinsic height before returning.
650     if (heightSpecified)
651         m_height = oldHeight;
652 }
653
654 void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren)
655 {
656     int xPos = borderLeft() + paddingLeft();
657     int yPos = borderTop() + paddingTop();
658     if( style()->direction() == RTL )
659         xPos = m_width - paddingRight() - borderRight();
660     int toAdd = borderBottom() + paddingBottom();
661     bool heightSpecified = false;
662     int oldHeight = 0;
663     
664     unsigned int highestFlexGroup = 0;
665     unsigned int lowestFlexGroup = 0;
666     bool haveFlex = false;
667     int remainingSpace = 0;
668     
669     // The first walk over our kids is to find out if we have any flexible children.
670     FlexBoxIterator iterator(this);
671     RenderObject *child = iterator.next();
672     while (child) {
673         // Check to see if this child flexes.
674         if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) {
675             // We always have to lay out flexible objects again, since the flex distribution
676             // may have changed, and we need to reallocate space.
677             child->setOverrideSize(-1);
678             if (!relayoutChildren)
679                 child->setChildNeedsLayout(true);
680             haveFlex = true;
681             unsigned int flexGroup = child->style()->boxFlexGroup();
682             if (lowestFlexGroup == 0)
683                 lowestFlexGroup = flexGroup;
684             if (flexGroup < lowestFlexGroup)
685                 lowestFlexGroup = flexGroup;
686             if (flexGroup > highestFlexGroup)
687                 highestFlexGroup = flexGroup;
688         }
689         child = iterator.next();
690     }
691
692 #if APPLE_CHANGES
693     // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
694     // mainstream block layout) and put it all inside APPLE_CHANGES to denote that this is not
695     // really part of the XUL box model.
696     bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100;
697     if (haveLineClamp) {
698         int maxLineCount = 0;
699         child = iterator.first();
700         while (child) {
701             if (!child->isPositioned()) {
702                 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) ||
703                     (child->style()->height().isVariable() && child->isBlockFlow() && !child->needsLayout())) {
704                     child->setChildNeedsLayout(true);
705                     
706                     // Dirty all the positioned objects.
707                     static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout();
708                     static_cast<RenderBlock*>(child)->clearTruncation();
709                 }
710                 child->layoutIfNeeded();
711                 if (child->style()->height().isVariable() && child->isBlockFlow())
712                     maxLineCount = kMax(maxLineCount, static_cast<RenderBlock*>(child)->lineCount());
713             }
714             child = iterator.next();
715         }
716         
717         // Get the # of lines and then alter all block flow children with auto height to use the
718         // specified height.
719         int numVisibleLines = int((maxLineCount+1)*style()->lineClamp()/100.0);
720         if (numVisibleLines < maxLineCount) {
721             for (child = iterator.first(); child; child = iterator.next()) {
722                 if (child->isPositioned() || !child->style()->height().isVariable() || !child->isBlockFlow())
723                     continue;
724                 
725                 RenderBlock* blockChild = static_cast<RenderBlock*>(child);
726                 int lineCount = blockChild->lineCount();
727                 if (lineCount <= numVisibleLines) continue;
728                 
729                 int newHeight = blockChild->heightForLineCount(numVisibleLines);
730                 if (newHeight == child->height()) continue;
731                 
732                 child->setChildNeedsLayout(true);
733                 child->setOverrideSize(newHeight);
734                 m_flexingChildren = true;
735                 child->layoutIfNeeded();
736                 m_flexingChildren = false;
737                 child->setOverrideSize(-1);
738                 
739                 // FIXME: For now don't support RTL.
740                 if (style()->direction() != LTR) continue;
741                 
742                 // Get the last line
743                 RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1);
744                 if (!lastLine) continue;
745                 
746                 // See if the last item is an anchor
747                 InlineBox* anchorBox = lastLine->lastChild();
748                 if (!anchorBox) continue;
749                 if (!anchorBox->object()->element()) continue;
750                 if (!anchorBox->object()->element()->hasAnchor()) continue;
751                 
752                 RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1);
753                 if (!lastVisibleLine) continue;
754
755                 const unsigned short ellipsisAndSpace[2] = { 0x2026, ' ' };
756                 static AtomicString ellipsisAndSpaceStr(ellipsisAndSpace, 2);
757                 const Font& font = style(numVisibleLines == 1)->htmlFont();
758                 int ellipsisAndSpaceWidth = font.width(const_cast<QChar*>(ellipsisAndSpaceStr.unicode()), 2, 0, 2);
759
760                 // Get ellipsis width + " " + anchor width
761                 int totalWidth = ellipsisAndSpaceWidth + anchorBox->width();
762                 
763                 // See if this width can be accommodated on the last visible line
764                 RenderBlock* destBlock = static_cast<RenderBlock*>(lastVisibleLine->object());
765                 RenderBlock* srcBlock = static_cast<RenderBlock*>(lastLine->object());
766                 
767                 // FIXME: Directions of src/destBlock could be different from our direction and from one another.
768                 if (srcBlock->style()->direction() != LTR) continue;
769                 if (destBlock->style()->direction() != LTR) continue;
770
771                 int blockEdge = destBlock->rightOffset(lastVisibleLine->yPos());
772                 if (!lastVisibleLine->canAccommodateEllipsis(true, blockEdge, 
773                                                              lastVisibleLine->xPos() + lastVisibleLine->width(),
774                                                              totalWidth))
775                     continue;
776
777                 // Let the truncation code kick in.
778                 lastVisibleLine->placeEllipsis(ellipsisAndSpaceStr, true, blockEdge, totalWidth, anchorBox);
779                 destBlock->setHasMarkupTruncation(true);
780             }
781         }
782     }
783 #endif
784
785     // We do 2 passes.  The first pass is simply to lay everyone out at
786     // their preferred widths.  The second pass handles flexing the children.
787     // Our first pass is done without flexing.  We simply lay the children
788     // out within the box.
789     do {
790         m_height = borderTop() + paddingTop();
791         int minHeight = m_height + toAdd;
792         m_overflowHeight = m_height;
793
794         child = iterator.first();
795         while (child) {
796             // make sure we relayout children if we need it.
797             if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))))
798                 child->setChildNeedsLayout(true);
799     
800             if (child->isPositioned())
801             {
802                 child->containingBlock()->insertPositionedObject(child);
803                 if (child->hasStaticX()) {
804                     if (style()->direction() == LTR)
805                         child->setStaticX(borderLeft()+paddingLeft());
806                     else
807                         child->setStaticX(borderRight()+paddingRight());
808                 }
809                 if (child->hasStaticY())
810                     child->setStaticY(m_height);
811                 child = iterator.next();
812                 continue;
813             } 
814     
815             // Compute the child's vertical margins.
816             child->calcVerticalMargins();
817     
818             // Add in the child's marginTop to our height.
819             m_height += child->marginTop();
820     
821             // Now do a layout.
822             child->layoutIfNeeded();
823     
824             // We can place the child now, using our value of box-align.
825             int childX = borderLeft() + paddingLeft();
826             switch (style()->boxAlign()) {
827                 case BCENTER:
828                 case BBASELINE: // Baseline just maps to center for vertical boxes
829                     childX += child->marginLeft() + (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2;
830                     break;
831                 case BEND:
832                     if (style()->direction() == RTL)
833                         childX += child->marginLeft();
834                     else
835                         childX += contentWidth() - child->marginRight() - child->width();
836                     break;
837                 default: // BSTART/BSTRETCH
838                     if (style()->direction() == LTR)
839                         childX += child->marginLeft();
840                     else
841                         childX += contentWidth() - child->marginRight() - child->width();
842                     break;
843             }
844     
845             // Place the child.
846             placeChild(child, childX, m_height);
847             m_height += child->height() + child->marginBottom();
848     
849             // See if this child has made our overflow need to grow.
850             // XXXdwh Work with left overflow as well as right overflow.
851             int rightChildPos = child->xPos() + kMax(child->overflowWidth(false), child->width());
852             if (rightChildPos > m_overflowWidth)
853                 m_overflowWidth = rightChildPos;
854             
855             child = iterator.next();
856         }
857
858         yPos = m_height;
859         m_height += toAdd;
860
861         // Negative margins can cause our height to shrink below our minimal height (border/padding).
862         // If this happens, ensure that the computed height is increased to the minimal height.
863         if (m_height < minHeight)
864             m_height = minHeight;
865
866         // Always make sure our overflowheight is at least our height.
867         if (m_overflowHeight < m_height)
868             m_overflowHeight = m_height;
869
870         // Now we have to calc our height, so we know how much space we have remaining.
871         oldHeight = m_height;
872         calcHeight();
873         if (oldHeight != m_height)
874             heightSpecified = true;
875
876         remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
877         
878         if (m_flexingChildren)
879             haveFlex = false; // We're done.
880         else if (haveFlex) {
881             // We have some flexible objects.  See if we need to grow/shrink them at all.
882             if (!remainingSpace)
883                 break;
884
885             // Allocate the remaining space among the flexible objects.  If we are trying to
886             // grow, then we go from the lowest flex group to the highest flex group.  For shrinking,
887             // we go from the highest flex group to the lowest group.
888             bool expanding = remainingSpace > 0;
889             unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
890             unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
891             for (unsigned int i = start; i <= end && remainingSpace; i++) {
892                 // Always start off by assuming the group can get all the remaining space.
893                 int groupRemainingSpace = remainingSpace;
894                 do {
895                     // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
896                     // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
897                     // computing the allowed growth before an object hits its min/max width (and thus
898                     // forces a totalFlex recomputation).
899                     float totalFlex = 0.0f;
900                     child = iterator.first();
901                     while (child) {
902                         if (allowedChildFlex(child, expanding, i))
903                             totalFlex += child->style()->boxFlex();
904                         child = iterator.next();
905                     }
906                     child = iterator.first();
907                     int spaceAvailableThisPass = groupRemainingSpace;
908                     while (child) {
909                         int allowedFlex = allowedChildFlex(child, expanding, i);
910                         if (allowedFlex) {
911                             int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex()));
912                             spaceAvailableThisPass = expanding ? kMin(spaceAvailableThisPass, projectedFlex) : kMax(spaceAvailableThisPass, projectedFlex);
913                         }
914                         child = iterator.next();
915                     }
916
917                     // The flex groups may not have any flexible objects this time around. 
918                     if (!spaceAvailableThisPass || totalFlex == 0.0f) {
919                         // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
920                         groupRemainingSpace = 0;
921                         continue;
922                     }
923             
924                     // Now distribute the space to objects.
925                     child = iterator.first();
926                     while (child && spaceAvailableThisPass && totalFlex) {
927                         if (allowedChildFlex(child, expanding, i)) {
928                             int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex));
929                             if (spaceAdd) {
930                                 child->setOverrideSize(child->overrideHeight() + spaceAdd);
931                                 m_flexingChildren = true;
932                                 relayoutChildren = true;
933                             }
934
935                             spaceAvailableThisPass -= spaceAdd;
936                             remainingSpace -= spaceAdd;
937                             groupRemainingSpace -= spaceAdd;
938                             
939                             totalFlex -= child->style()->boxFlex();
940                         }
941                         child = iterator.next();
942                     }
943                 } while (groupRemainingSpace);
944             }
945
946             // We didn't find any children that could grow.
947             if (haveFlex && !m_flexingChildren)
948                 haveFlex = false;
949         }        
950     } while (haveFlex);
951
952     if (style()->boxPack() != BSTART && remainingSpace > 0) {
953         // Children must be repositioned.
954         int offset = 0;
955         if (style()->boxPack() == BJUSTIFY) {
956             // Determine the total number of children.
957             int totalChildren = 0;
958             child = iterator.first();
959             while (child) {
960                 if (child->isPositioned()) {
961                     child = iterator.next();
962                     continue;
963                 }
964                 totalChildren++;
965                 child = iterator.next();
966             }
967             
968             // Iterate over the children and space them out according to the
969             // justification level.
970             if (totalChildren > 1) {
971                 totalChildren--;
972                 bool firstChild = true;
973                 child = iterator.first();
974                 while (child) {
975                     if (child->isPositioned()) {
976                         child = iterator.next();
977                         continue;
978                     }
979                     
980                     if (firstChild) {
981                         firstChild = false;
982                         child = iterator.next();
983                         continue;
984                     }
985
986                     offset += remainingSpace/totalChildren;
987                     remainingSpace -= (remainingSpace/totalChildren);
988                     totalChildren--;
989                     placeChild(child, child->xPos(), child->yPos()+offset);
990                     child = iterator.next();
991                 }
992             }
993         }
994         else {
995             if (style()->boxPack() == BCENTER)
996                 offset += remainingSpace/2;
997             else // END
998                 offset += remainingSpace;
999             child = iterator.first();
1000             while (child) {
1001                 if (child->isPositioned()) {
1002                     child = iterator.next();
1003                     continue;
1004                 }
1005                 placeChild(child, child->xPos(), child->yPos()+offset);
1006                 child = iterator.next();
1007             }
1008         }
1009     }
1010     
1011     // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of
1012     // a height change, we revert our height back to the intrinsic height before returning.
1013     if (heightSpecified)
1014         m_height = oldHeight;    
1015 }
1016
1017 void RenderFlexibleBox::placeChild(RenderObject* child, int x, int y)
1018 {
1019     int oldChildX = child->xPos();
1020     int oldChildY = child->yPos();
1021
1022     // Place the child.
1023     child->setPos(x, y);
1024
1025     // If the child moved, we have to repaint it as well as any floating/positioned
1026     // descendants.  An exception is if we need a layout.  In this case, we know we're going to
1027     // repaint ourselves (and the child) anyway.
1028     if (!selfNeedsLayout() && child->checkForRepaintDuringLayout())
1029         child->repaintDuringLayoutIfMoved(oldChildX, oldChildY);
1030 }
1031
1032 int RenderFlexibleBox::allowedChildFlex(RenderObject* child, bool expanding, unsigned int group)
1033 {
1034     if (child->isPositioned() || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group)
1035         return 0;
1036                         
1037     if (expanding) {
1038         if (isHorizontal()) {
1039             // FIXME: For now just handle fixed values.
1040             int maxW = INT_MAX;
1041             int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight());
1042             if (child->style()->maxWidth().value != UNDEFINED &&
1043                 child->style()->maxWidth().isFixed())
1044                 maxW = child->style()->maxWidth().value;
1045             else if (child->style()->maxWidth().type == Intrinsic)
1046                 maxW = child->maxWidth();
1047             else if (child->style()->maxWidth().type == MinIntrinsic)
1048                 maxW = child->minWidth();
1049             int allowedGrowth = kMax(0, maxW - w);
1050             return allowedGrowth;
1051         }
1052         else {
1053             // FIXME: For now just handle fixed values.
1054             int maxH = INT_MAX;
1055             int h = child->overrideHeight() - (child->borderTop() + child->borderBottom() + child->paddingTop() + child->paddingBottom());
1056             if (child->style()->maxHeight().value != UNDEFINED &&
1057                 child->style()->maxHeight().isFixed())
1058                 maxH = child->style()->maxHeight().value;
1059             int allowedGrowth = kMax(0, maxH - h);
1060             return allowedGrowth;
1061         }
1062     }
1063
1064     // FIXME: For now just handle fixed values.
1065     if (isHorizontal()) {
1066         int minW = child->minWidth();
1067         int w = child->contentWidth();
1068         if (child->style()->minWidth().isFixed())
1069             minW = child->style()->minWidth().value;
1070         else if (child->style()->minWidth().type == Intrinsic)
1071             minW = child->maxWidth();
1072         else if (child->style()->minWidth().type == MinIntrinsic)
1073             minW = child->minWidth();
1074             
1075         int allowedShrinkage = kMin(0, minW - w);
1076         return allowedShrinkage;
1077     }
1078     else {
1079         if (child->style()->minHeight().isFixed()) {
1080             int minH = child->style()->minHeight().value;
1081             int h = child->contentHeight();
1082             int allowedShrinkage = kMin(0, minH - h);
1083             return allowedShrinkage;
1084         }
1085     }
1086     
1087     return 0;
1088 }
1089
1090 const char *RenderFlexibleBox::renderName() const
1091 {
1092     if (isFloating())
1093         return "RenderFlexibleBox (floating)";
1094     if (isPositioned())
1095         return "RenderFlexibleBox (positioned)";
1096     if (isRelPositioned())
1097         return "RenderFlexibleBox (relative positioned)";
1098     return "RenderFlexibleBox";
1099 }
1100
1101
1102 } // namespace khtml
1103