001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * InfiniteMajorAxisLayoutStep.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process;
030:
031: import org.jfree.report.ElementAlignment;
032: import org.jfree.report.layout.model.BlockRenderBox;
033: import org.jfree.report.layout.model.FinishedRenderNode;
034: import org.jfree.report.layout.model.InlineRenderBox;
035: import org.jfree.report.layout.model.LogicalPageBox;
036: import org.jfree.report.layout.model.ParagraphPoolBox;
037: import org.jfree.report.layout.model.ParagraphRenderBox;
038: import org.jfree.report.layout.model.RenderBox;
039: import org.jfree.report.layout.model.RenderLength;
040: import org.jfree.report.layout.model.RenderNode;
041: import org.jfree.report.layout.model.RenderableReplacedContent;
042: import org.jfree.report.layout.model.RenderableText;
043: import org.jfree.report.layout.model.WatermarkAreaBox;
044: import org.jfree.report.layout.model.context.BoxDefinition;
045: import org.jfree.report.layout.model.context.StaticBoxLayoutProperties;
046: import org.jfree.report.layout.process.valign.BoxAlignContext;
047: import org.jfree.report.layout.process.valign.InlineBlockAlignContext;
048: import org.jfree.report.layout.process.valign.NodeAlignContext;
049: import org.jfree.report.layout.process.valign.ReplacedContentAlignContext;
050: import org.jfree.report.layout.process.valign.TextElementAlignContext;
051: import org.jfree.report.layout.process.valign.VerticalAlignmentProcessor;
052: import org.jfree.report.style.ElementStyleKeys;
053: import org.jfree.report.util.geom.StrictGeomUtility;
054: import org.jfree.util.Log;
055:
056: /**
057: * Computes the absolute layout. The computed height and y positions of all abolutely positioned elements will be stored
058: * in the 'canvasY' and 'canvasHeight' properties of RenderNode. Percentages will be resolved to zero.
059: *
060: * @author Thomas Morgner
061: */
062: public final class InfiniteMajorAxisLayoutStep extends
063: IterateVisualProcessStep {
064: // Set the maximum height to an incredibly high value. This is now 2^43 micropoints or more than
065: // 3000 kilometers. Please call me directly at any time if you need more space for printing.
066: private static final long MAX_AUTO = StrictGeomUtility
067: .toInternalValue(0x80000000000L);
068:
069: private MajorAxisParagraphBreakState breakState;
070: private RenderBox continuedElement;
071: private VerticalAlignmentProcessor processor;
072:
073: public InfiniteMajorAxisLayoutStep() {
074: this .breakState = new MajorAxisParagraphBreakState();
075: this .processor = new VerticalAlignmentProcessor();
076: }
077:
078: public void compute(final LogicalPageBox pageBox) {
079: this .breakState.deinit();
080: this .continuedElement = null;
081: try {
082: startProcessing(pageBox);
083: } finally {
084: this .continuedElement = null;
085: this .breakState.deinit();
086: }
087: }
088:
089: /**
090: * Continues processing. The renderbox must have a valid x-layout (that is: X, content-X1, content-X2 and Width)
091: *
092: * @param box
093: */
094: public void continueComputation(final RenderBox box) {
095: // This is most-likely wrong, but as we do not support inline-block elements yet, we can ignore this for now.
096: if (box.getContentAreaX2() == 0 || box.getCachedWidth() == 0) {
097: throw new IllegalStateException(
098: "Box must be layouted a bit ..");
099: }
100:
101: this .breakState.deinit();
102: this .continuedElement = box;
103: startProcessing(box);
104: this .continuedElement = null;
105: this .breakState.deinit();
106: }
107:
108: protected boolean startBlockLevelBox(final RenderBox box) {
109: if (box.isIgnorableForRendering()) {
110: return false;
111: }
112:
113: if (box.isCacheValid()) {
114: return false;
115: }
116:
117: // Compute the block-position of the box. The box is positioned relative to the previous silbling or
118: // relative to the parent.
119: box.setCachedY(computeVerticalBlockPosition(box));
120:
121: if (breakState.isActive()) {
122: // No breakstate and not being suspended? Why this?
123: if (breakState.isSuspended() == false) {
124: throw new IllegalStateException("This cannot be.");
125: }
126:
127: // this way or another - we are suspended now. So there is no need to look
128: // at the children anymore ..
129:
130: // This code is only executed for inline-block elements. Inline-block elements are not part of
131: // the 0.8.9 or 1.0 engine layouting.
132: return false;
133: }
134:
135: if (box instanceof ParagraphRenderBox) {
136: final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
137: // We cant cache that ... the shift operations later would misbehave
138: // One way around would be to at least store the layouted offsets
139: // (which should be immutable as long as the line did not change its
140: // contents) and to reapply them on each run. This is cheaper than
141: // having to compute the whole v-align for the whole line.
142: breakState.init(paragraphBox);
143: }
144:
145: return true;
146: }
147:
148: protected void processBlockLevelNode(final RenderNode node) {
149: // This could be anything, text, or an image.
150: node.setCachedY(computeVerticalBlockPosition(node));
151:
152: if (node instanceof FinishedRenderNode) {
153: final FinishedRenderNode fnode = (FinishedRenderNode) node;
154: node.setCachedHeight(fnode.getLayoutedHeight());
155: } else if (node instanceof InlineRenderBox) {
156: throw new IllegalStateException(
157: "A Inline-Box must be contained in a paragraph.");
158: } else if (node instanceof RenderableReplacedContent) {
159: final RenderableReplacedContent rpc = (RenderableReplacedContent) node;
160: node.setCachedHeight(rpc.computeHeight(
161: computeBlockContextWidth(node), node
162: .getComputedWidth()));
163: }
164: }
165:
166: protected void finishBlockLevelBox(final RenderBox box) {
167: if (box instanceof BlockRenderBox) {
168: // make sure that we resolve against zero.
169: box.setCachedHeight(computeBlockHeightAndAlign(box, 0));
170: } else {
171: box.setCachedHeight(computeCanvasHeight(box));
172: }
173:
174: if (breakState.isActive()) {
175: final Object suspender = breakState.getSuspendItem();
176: if (box.getInstanceId() == suspender) {
177: breakState.setSuspendItem(null);
178: return;
179: }
180: if (suspender != null) {
181: return;
182: }
183:
184: if (box instanceof ParagraphRenderBox) {
185: breakState.deinit();
186: }
187: }
188: }
189:
190: private long computeVerticalBlockPosition(final RenderNode node) {
191: // we have no margins yet ..
192: final long marginTop = 0;
193:
194: // The y-position of a box depends on the parent.
195: final RenderBox parent = node.getParent();
196:
197: // A table row is something special. Although it is a block box,
198: // it layouts its children from left to right
199: if (parent instanceof BlockRenderBox) {
200: final RenderNode prev = node.getVisiblePrev();
201: if (prev != null) {
202: // we have a silbling. Position yourself directly below your silbling ..
203: return (marginTop + prev.getCachedY() + prev
204: .getCachedHeight());
205: } else {
206: final StaticBoxLayoutProperties blp = parent
207: .getStaticBoxLayoutProperties();
208: final BoxDefinition bdef = parent.getBoxDefinition();
209: final long insetTop = (blp.getBorderTop() + bdef
210: .getPaddingTop());
211:
212: return (marginTop + insetTop + parent.getCachedY());
213: }
214: } else {
215: // there's no parent ..
216: return (marginTop);
217: }
218: }
219:
220: private long computeBlockHeightAndAlign(final RenderBox box,
221: final long resolveSize) {
222: // For the water-mark area, this computation is different. The Watermark-area uses the known height of
223: // the parent (=the page size)
224: if (box instanceof WatermarkAreaBox) {
225: final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
226: final LogicalPageBox lpb = watermarkAreaBox
227: .getLogicalPage();
228: // set the page-height as watermark size.
229: return lpb.getPageHeight();
230: }
231:
232: // Check the height. Set the height.
233: final BoxDefinition boxDefinition = box.getBoxDefinition();
234: final RenderLength preferredHeight = boxDefinition
235: .getPreferredHeight();
236: final RenderLength minimumHeight = boxDefinition
237: .getMinimumHeight();
238: final RenderLength maximumHeight = boxDefinition
239: .getMaximumHeight();
240:
241: final long usedHeight;
242: final long childY2;
243: final long childY1;
244: final RenderNode lastChildNode = box.getLastChild();
245: if (lastChildNode != null) {
246: childY1 = box.getFirstChild().getCachedY();
247: childY2 = lastChildNode.getCachedY()
248: + lastChildNode.getCachedHeight()
249: + lastChildNode.getEffectiveMarginBottom();
250: usedHeight = (childY2 - childY1);
251: } else {
252: usedHeight = 0;
253: childY2 = 0;
254: childY1 = 0;
255: }
256:
257: //final long blockContextWidth = box.getStaticBoxLayoutProperties().getBlockContextWidth();
258: final long rminH = minimumHeight.resolve(resolveSize, 0);
259: final long rmaxH = maximumHeight.resolve(resolveSize, MAX_AUTO);
260:
261: final StaticBoxLayoutProperties blp = box
262: .getStaticBoxLayoutProperties();
263: final long insetBottom = blp.getBorderBottom()
264: + boxDefinition.getPaddingBottom();
265: final long insetTop = blp.getBorderTop()
266: + boxDefinition.getPaddingTop();
267:
268: final long computedHeight;
269: if (boxDefinition.isSizeSpecifiesBorderBox()) {
270: final long rprefH = preferredHeight.resolve(resolveSize,
271: usedHeight + insetTop + insetBottom);
272: final long specifiedHeight = computeHeight(rminH, rmaxH,
273: rprefH);
274: computedHeight = specifiedHeight - insetTop - insetBottom;
275: } else {
276: final long rprefH = preferredHeight.resolve(resolveSize,
277: usedHeight);
278: final long specifiedHeight = computeHeight(rminH, rmaxH,
279: rprefH);
280: computedHeight = specifiedHeight;
281: }
282:
283: if (lastChildNode != null) {
284: // grab the node's y2
285: if (computedHeight > usedHeight) {
286: // we have extra space to distribute. So lets shift some boxes.
287: final ElementAlignment valign = box
288: .getNodeLayoutProperties()
289: .getVerticalAlignment();
290: if (ElementAlignment.BOTTOM.equals(valign)) {
291: final long boxBottom = (box.getCachedY()
292: + box.getCachedHeight() - insetBottom);
293: final long delta = boxBottom - childY2;
294: CacheBoxShifter.shiftBoxChilds(box, delta);
295: } else if (ElementAlignment.MIDDLE.equals(valign)) {
296: final long extraHeight = computedHeight
297: - usedHeight;
298: final long boxTop = box.getCachedY() + insetTop
299: + (extraHeight / 2);
300: final long delta = boxTop - childY1;
301: CacheBoxShifter.shiftBoxChilds(box, delta);
302: }
303: return Math.max(0, computedHeight + insetTop
304: + insetBottom);
305: }
306: return Math.max(0, computedHeight + insetTop + insetBottom);
307: } else {
308: return Math.max(0, computedHeight + insetTop + insetBottom);
309: }
310: }
311:
312: private long computeBlockContextWidth(final RenderNode box) {
313: final RenderBox parentBlockContext = box.getParent();
314: if (parentBlockContext == null) {
315: final LogicalPageBox logicalPage = box.getLogicalPage();
316: if (logicalPage == null) {
317: return 0;
318: }
319: return logicalPage.getPageWidth();
320: }
321: return parentBlockContext.getStaticBoxLayoutProperties()
322: .getBlockContextWidth();
323: }
324:
325: protected void processParagraphChilds(final ParagraphRenderBox box) {
326: // Process the direct childs of the paragraph
327: // Each direct child represents a line ..
328:
329: RenderNode node = box.getVisibleFirst();
330: while (node != null) {
331: // all childs of the linebox container must be inline boxes. They
332: // represent the lines in the paragraph. Any other element here is
333: // a error that must be reported
334: if (node instanceof ParagraphPoolBox == false) {
335: throw new IllegalStateException("Encountered "
336: + node.getClass());
337: }
338: final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
339: if (startLine(inlineRenderBox)) {
340: processBoxChilds(inlineRenderBox);
341: finishLine(inlineRenderBox);
342: }
343:
344: node = node.getVisibleNext();
345: }
346: }
347:
348: private boolean startLine(final ParagraphPoolBox box) {
349: box.setCachedY(computeVerticalBlockPosition(box));
350:
351: if (breakState.isActive() == false) {
352: return false;
353: }
354:
355: if (breakState.isSuspended()) {
356: return false;
357: }
358:
359: breakState.openContext(new BoxAlignContext(box));
360: return true;
361: }
362:
363: private void finishLine(final ParagraphPoolBox inlineRenderBox) {
364: if (breakState.isActive() == false || breakState.isSuspended()) {
365: return;
366: }
367:
368: final BoxAlignContext boxAlignContext = breakState
369: .closeContext();
370:
371: // This aligns all direct childs. Once that is finished, we have to
372: // check, whether possibly existing inner-paragraphs are still valid
373: // or whether moving them violated any of the inner-pagebreak constraints.
374:
375: final StaticBoxLayoutProperties blp = inlineRenderBox
376: .getStaticBoxLayoutProperties();
377: final BoxDefinition bdef = inlineRenderBox.getBoxDefinition();
378: final long insetTop = (blp.getBorderTop() + bdef
379: .getPaddingTop());
380:
381: final long contentAreaY1 = inlineRenderBox.getCachedY()
382: + insetTop;
383: final long lineHeight = inlineRenderBox.getLineHeight();
384: processor.align(boxAlignContext, contentAreaY1, lineHeight);
385: }
386:
387: protected boolean startInlineLevelBox(final RenderBox box) {
388: if (box.isCacheValid()) {
389: return false;
390: }
391:
392: box.setCachedY(computeVerticalInlinePosition(box));
393: computeBaselineInfo(box);
394:
395: if (breakState == null) {
396: // ignore .. should not happen anyway ..
397: return true;
398: }
399:
400: if (breakState.isSuspended()) {
401: return false;
402: }
403:
404: if (box instanceof InlineRenderBox) {
405: breakState.openContext(new BoxAlignContext(box));
406: return true;
407: }
408:
409: breakState.getCurrentLine().addChild(
410: new InlineBlockAlignContext(box));
411: breakState.setSuspendItem(box.getInstanceId());
412: return false;
413: }
414:
415: private void computeBaselineInfo(final RenderBox box) {
416: if (box.getBaselineInfo() == null) {
417: return;
418: }
419:
420: RenderNode node = box.getVisibleFirst();
421: while (node != null) {
422: if (node instanceof RenderableText) {
423: // grab the baseline info from there ...
424: final RenderableText text = (RenderableText) node;
425: box.setBaselineInfo(text.getBaselineInfo());
426: break;
427: }
428:
429: node = node.getVisibleNext();
430: }
431:
432: // If we have no baseline info here, ask the parent. If that one has none
433: // either, then we cant do anything about it.
434: if (box.getBaselineInfo() == null) {
435: box.setBaselineInfo(box.getStaticBoxLayoutProperties()
436: .getNominalBaselineInfo());
437: }
438: }
439:
440: protected void processInlineLevelNode(final RenderNode node) {
441: // compute the intial position.
442: node.setCachedY(computeVerticalInlinePosition(node));
443: // the height and the real position will be computed during the vertical-alignment computation.
444:
445: if (breakState.isActive() == false || breakState.isSuspended()) {
446: return;
447: }
448:
449: if (node instanceof RenderableText) {
450: breakState.getCurrentLine().addChild(
451: new TextElementAlignContext((RenderableText) node));
452: } else if (node instanceof RenderableReplacedContent) {
453: breakState.getCurrentLine().addChild(
454: new ReplacedContentAlignContext(
455: (RenderableReplacedContent) node));
456: } else {
457: breakState.getCurrentLine().addChild(
458: new NodeAlignContext(node));
459: }
460: }
461:
462: protected void finishInlineLevelBox(final RenderBox box) {
463: // The height of an inline-level box will be computed when the vertical-alignemnt is done.
464:
465: if (breakState.isActive() == false) {
466: return;
467: }
468:
469: if (box instanceof InlineRenderBox) {
470: breakState.closeContext();
471: return;
472: }
473:
474: final Object suspender = breakState.getSuspendItem();
475: if (box.getInstanceId() == suspender) {
476: breakState.setSuspendItem(null);
477: return;
478: }
479:
480: if (suspender != null) {
481: return;
482: }
483:
484: if (box instanceof ParagraphRenderBox) {
485: throw new IllegalStateException("This cannot be.");
486: }
487: }
488:
489: private long computeVerticalInlinePosition(final RenderNode node) {
490: final RenderBox parent = node.getParent();
491:
492: if (parent != null) {
493: // the computed position of an inline-element must be the same as the position of the parent element.
494: // A inline-box always has an other inline-box as parent (the paragraph-pool-box is the only exception;
495: // and this one is handled elsewhere).
496:
497: // Top and bottom margins are not applied to inline-elements.
498: final StaticBoxLayoutProperties blp = parent
499: .getStaticBoxLayoutProperties();
500: final BoxDefinition bdef = parent.getBoxDefinition();
501: final long insetTop = (blp.getBorderTop() + bdef
502: .getPaddingTop());
503:
504: return (insetTop + parent.getCachedY());
505: } else {
506: // there's no parent .. Should not happen, shouldn't it?
507: return (0);
508: }
509: }
510:
511: protected boolean startCanvasLevelBox(final RenderBox box) {
512: if (box.isCacheValid()) {
513: return false;
514: }
515:
516: if (box.isIgnorableForRendering()) {
517: return false;
518: }
519:
520: box.setCachedY(computeVerticalCanvasPosition(box));
521:
522: if (breakState.isActive() == false) {
523: if (box instanceof ParagraphRenderBox) {
524: final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
525: // We cant cache that ... the shift operations later would misbehave
526: // One way around would be to at least store the layouted offsets
527: // (which should be immutable as long as the line did not change its
528: // contents) and to reapply them on each run. This is cheaper than
529: // having to compute the whole v-align for the whole line.
530: breakState.init(paragraphBox);
531: }
532:
533: return true;
534: }
535:
536: // No breakstate and not being suspended? Why this?
537: if (breakState.isSuspended() == false) {
538: throw new IllegalStateException("This cannot be.");
539: }
540:
541: // this way or another - we are suspended now. So there is no need to look
542: // at the children anymore ..
543: return false;
544: }
545:
546: protected void processCanvasLevelNode(final RenderNode node) {
547: node.setCachedY(computeVerticalCanvasPosition(node));
548:
549: // docmark
550: if (node instanceof RenderableReplacedContent) {
551: final RenderableReplacedContent rpc = (RenderableReplacedContent) node;
552: final long computedHeight = rpc.computeHeight(0, node
553: .getComputedWidth());
554: node.setCachedHeight(computedHeight);
555: } else if (node instanceof FinishedRenderNode) {
556: final FinishedRenderNode fnode = (FinishedRenderNode) node;
557: node.setCachedHeight(fnode.getLayoutedHeight());
558: } else {
559: node.setCachedHeight(0);
560: }
561: }
562:
563: /**
564: * Finishes up a canvas level box. This updates/affects the height of the parent, as the canvas model defines that the
565: * parent always fully encloses all of its childs.
566: * <p/>
567: * When no preferred height is defined, the height of an element is the maximum of its minimum-height and the absolute
568: * height of all of its direct children.
569: * <p/>
570: * To resolve the value of percentages, the system uses the maximum of the parent's height and the maximum of all (y +
571: * height) of all children.)
572: *
573: * @param box
574: */
575: protected void finishCanvasLevelBox(final RenderBox box) {
576: if (box instanceof BlockRenderBox) {
577: // make sure that we resolve against zero.
578: box.setCachedHeight(computeBlockHeightAndAlign(box, 0));
579: } else {
580:
581: box.setCachedHeight(computeCanvasHeight(box));
582: }
583:
584: if (breakState.isActive()) {
585: final Object suspender = breakState.getSuspendItem();
586: if (box.getInstanceId() == suspender) {
587: breakState.setSuspendItem(null);
588: return;
589: }
590: if (suspender != null) {
591: return;
592: }
593:
594: if (box instanceof ParagraphRenderBox) {
595: breakState.deinit();
596: }
597: }
598: }
599:
600: private long computeVerticalCanvasPosition(final RenderNode node) {
601: final RenderBox parent = node.getParent();
602: final long parentPosition;
603: if (parent == null) {
604: parentPosition = 0;
605: } else {
606: final StaticBoxLayoutProperties blp = parent
607: .getStaticBoxLayoutProperties();
608: final BoxDefinition bdef = parent.getBoxDefinition();
609: final long insetsTop = (blp.getBorderTop() + bdef
610: .getPaddingTop());
611: parentPosition = parent.getCachedY() + insetsTop;
612: }
613:
614: final double posY = node.getStyleSheet()
615: .getDoubleStyleProperty(ElementStyleKeys.POS_Y, 0);
616: if (node.isSizeSpecifiesBorderBox()) {
617: return (parentPosition + RenderLength
618: .resolveLength(0, posY));
619: } else {
620: final long insetsTop;
621: if (node instanceof RenderBox) {
622: final RenderBox box = (RenderBox) node;
623: final StaticBoxLayoutProperties blp = box
624: .getStaticBoxLayoutProperties();
625: final BoxDefinition bdef = box.getBoxDefinition();
626: insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
627: } else {
628: insetsTop = 0;
629: }
630: return (parentPosition
631: + RenderLength.resolveLength(0, posY) - insetsTop);
632: }
633: }
634:
635: private long computeCanvasHeight(final RenderBox box) {
636: final StaticBoxLayoutProperties blp = box
637: .getStaticBoxLayoutProperties();
638: final BoxDefinition bdef = box.getBoxDefinition();
639:
640: final BoxDefinition boxDefinition = box.getBoxDefinition();
641: final RenderLength minHeight = boxDefinition.getMinimumHeight();
642: final RenderLength preferredHeight = boxDefinition
643: .getPreferredHeight();
644: final RenderLength maxHeight = boxDefinition.getMaximumHeight();
645:
646: final long insetsTop = (blp.getBorderTop() + bdef
647: .getPaddingTop());
648: final long insetsBottom = blp.getBorderBottom()
649: + bdef.getPaddingBottom();
650: final long insets = insetsTop + insetsBottom;
651:
652: // find the maximum of the used height (for all childs) and the specified min-height.
653: long consumedHeight = minHeight.resolve(0);
654:
655: if (box.isSizeSpecifiesBorderBox()) {
656: consumedHeight = Math.max(0, consumedHeight - insetsBottom);
657: } else {
658: consumedHeight = Math.max(0, consumedHeight + insetsTop);
659: }
660:
661: final long boxY = box.getCachedY();
662:
663: RenderNode node = box.getFirstChild();
664: while (node != null) {
665: final long childY2 = (insetsTop + (node.getCachedY() + node
666: .getCachedHeight()));
667: final long childLocalY2 = childY2 - boxY;
668: if (childLocalY2 > consumedHeight) {
669: consumedHeight = childLocalY2;
670: }
671: node = node.getNext();
672: }
673:
674: consumedHeight += insetsBottom;
675:
676: // The consumed height computed above specifies the size at the border-edge.
677: // However, depending on the box-sizing property, we may have to resolve them against the
678: // content-edge instead.
679:
680: if (box.isSizeSpecifiesBorderBox()) {
681: final long minHeightResolved = minHeight.resolve(0);
682: final long maxHeightResolved = maxHeight.resolve(0);
683: final long prefHeightResolved;
684: if (preferredHeight == RenderLength.AUTO) {
685: prefHeightResolved = consumedHeight;
686: } else {
687: prefHeightResolved = preferredHeight.resolve(0);
688: }
689:
690: final long height = computeHeight(minHeightResolved,
691: maxHeightResolved, prefHeightResolved);
692: return (height);
693: } else {
694: consumedHeight = Math.max(0, consumedHeight - insets);
695: final long minHeightResolved = minHeight.resolve(0);
696: final long maxHeightResolved = maxHeight.resolve(0);
697: final long prefHeightResolved;
698: if (preferredHeight == RenderLength.AUTO) {
699: prefHeightResolved = consumedHeight;
700: } else {
701: prefHeightResolved = preferredHeight.resolve(0);
702: }
703:
704: final long height = computeHeight(minHeightResolved,
705: maxHeightResolved, prefHeightResolved);
706: return (height + insets);
707: }
708: }
709:
710: public static long computeHeight(final long min, final long max,
711: final long pref) {
712: if (pref > max) {
713: if (max < min) {
714: return min;
715: }
716: return max;
717: }
718:
719: if (pref < min) {
720: if (max < min) {
721: return max;
722: }
723: return min;
724: }
725:
726: if (max < pref) {
727: return max;
728: }
729: return pref;
730: }
731: }
|