diff options
Diffstat (limited to 'org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java')
-rw-r--r-- | org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java new file mode 100644 index 00000000..7242cf48 --- /dev/null +++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java @@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 John Krasnay and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * John Krasnay - initial API and implementation + * Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325) + *******************************************************************************/ +package org.eclipse.vex.core.internal.layout; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.vex.core.internal.core.Caret; +import org.eclipse.vex.core.internal.core.IntRange; +import org.eclipse.vex.core.internal.css.CSS; +import org.eclipse.vex.core.internal.css.Styles; +import org.eclipse.vex.core.internal.dom.Element; + +/** + * A box that wraps inline content into a paragraph. + */ +public class ParagraphBox extends AbstractBox implements BlockBox { + + private LineBox[] children; + private LineBox firstContentLine; + private LineBox lastContentLine; + + /** + * Class constructor. + * + * @param children + * Line boxes that comprise the paragraph. + */ + private ParagraphBox(LineBox[] children) { + this.children = children; + for (int i = 0; i < children.length; i++) { + if (children[i].hasContent()) { + if (this.firstContentLine == null) { + this.firstContentLine = children[i]; + } + this.lastContentLine = children[i]; + } + } + } + + /** + * Create a paragraph by word-wrapping a list of inline boxes. + * + * @param context + * LayoutContext used for this layout. + * @param element + * Element that controls the styling for this paragraph. + * @param inlines + * List of InlineBox objects to be wrapped + * @param width + * width to which the paragraph is to be wrapped + */ + public static ParagraphBox create(LayoutContext context, Element element, + List<InlineBox> inlines, int width) { + InlineBox[] array = inlines.toArray(new InlineBox[inlines.size()]); + return create(context, element, array, width); + } + + /** + * Create a paragraph by word-wrapping a list of inline boxes. + * + * @param context + * LayoutContext used for this layout + * @param element + * Element that controls the styling of this paragraph, in + * particular text alignment. + * @param inlines + * Array of InlineBox objects to be wrapped. + * @param width + * width to which the paragraph is to be wrapped. + */ + public static ParagraphBox create(LayoutContext context, Element element, + InlineBox[] inlines, int width) { + + // lines is the list of LineBoxes we are creating + List<Box> lines = new ArrayList<Box>(); + + InlineBox right = new LineBox(context, element, inlines); + + while (right != null) { + InlineBox.Pair pair = right.split(context, width, true); + //FIXME icampist this indicates some design problem, since later on, LineBoxes are expected + //either we cast here to LineBox or we make an if later on. + lines.add(pair.getLeft()); + right = pair.getRight(); + } + + Styles styles = context.getStyleSheet().getStyles(element); + String textAlign = styles.getTextAlign(); + + // y-offset of the next line + int y = 0; + + int actualWidth = 0; + + //children for the ParagraphBox constructor that accepts only LineBoxes + List<LineBox> lineBoxesChildren = new ArrayList<LineBox>(); + + for (Box lineBox : lines) { + int x; + if (textAlign.equals(CSS.RIGHT)) { + x = width - lineBox.getWidth(); + } else if (textAlign.equals(CSS.CENTER)) { + x = (width - lineBox.getWidth()) / 2; + } else { + x = 0; + } + + lineBox.setX(x); + lineBox.setY(y); + + y += lineBox.getHeight(); + actualWidth = Math.max(actualWidth, lineBox.getWidth()); + + //strange, but we need to check the case because it's not explicit anywhere + if (lineBox instanceof LineBox) { + lineBoxesChildren.add((LineBox) lineBox); + } + } + + ParagraphBox para = new ParagraphBox(lineBoxesChildren.toArray(new LineBox[lineBoxesChildren.size()])); + para.setWidth(actualWidth); + para.setHeight(y); + + // BlockElementBox uses a scaling factor to estimate box height based + // on font size, layout width, and character count, as follows. + // + // estHeight = factor * fontSize * fontSize * charCount / width + // + // This bit reports the actual factor that would correctly estimate + // the height of a BlockElementBox containing only this paragraph. + // + // factor = estHeight * width / (fontSize * fontSize * charCount) + // + /* + * Box firstContentBox = null; for (int i = 0; i < inlines.length; i++) + * { Box box = inlines[i]; if (box.hasContent()) { firstContentBox = + * box; break; } } + * + * if (firstContentBox != null) { float fontSize = styles.getFontSize(); + * int charCount = lastContentBox.getEndOffset() - + * firstContentBox.getStartOffset(); float factor = para.getHeight() + * para.getWidth() / (fontSize fontSize charCount); + * System.out.println("Actual factor is " + factor); } + */ + + return para; + } + + /** + * @see org.eclipse.vex.core.internal.layout.Box#getCaret(org.eclipse.vex.core.internal.layout.LayoutContext, + * int) + */ + public Caret getCaret(LayoutContext context, int offset) { + + LineBox line = this.getLineAt(offset); + Caret caret = line.getCaret(context, offset); + caret.translate(line.getX(), line.getY()); + return caret; + + } + + public Box[] getChildren() { + return this.children; + } + + public int getEndOffset() { + return this.lastContentLine.getEndOffset(); + } + + /** + * @see org.eclipse.vex.core.internal.layout.BlockBox#getFirstLine() + */ + public LineBox getFirstLine() { + if (this.children.length == 0) { + return null; + } else { + return this.children[0]; + } + } + + /** + * @see org.eclipse.vex.core.internal.layout.BlockBox#getLastLine() + */ + public LineBox getLastLine() { + if (this.children.length == 0) { + return null; + } else { + return this.children[this.children.length - 1]; + } + } + + /** + * Returns the LineBox at the given offset. + * + * @param offset + * the offset to check. + */ + public LineBox getLineAt(int offset) { + LineBox[] children = this.children; + for (int i = 0; i < children.length; i++) { + if (children[i].hasContent() + && offset <= children[i].getEndOffset()) { + return children[i]; + } + } + return this.lastContentLine; + } + + public int getLineEndOffset(int offset) { + return this.getLineAt(offset).getEndOffset(); + } + + public int getLineStartOffset(int offset) { + return this.getLineAt(offset).getStartOffset(); + } + + public int getMarginBottom() { + return 0; + } + + public int getMarginTop() { + return 0; + } + + public int getNextLineOffset(LayoutContext context, int offset, int x) { + LineBox nextLine = null; + LineBox[] children = this.children; + for (int i = 0; i < children.length; i++) { + if (children[i].hasContent() + && children[i].getStartOffset() > offset) { + nextLine = children[i]; + break; + } + } + if (nextLine == null) { + // return this.getEndOffset() + 1; + return -1; + } else { + return nextLine.viewToModel(context, x - nextLine.getX(), 0); + } + } + + public BlockBox getParent() { + throw new IllegalStateException( + "ParagraphBox does not currently track parent"); + } + + public int getPreviousLineOffset(LayoutContext context, int offset, int x) { + LineBox prevLine = null; + LineBox[] children = this.children; + for (int i = children.length - 1; i >= 0; i--) { + if (children[i].hasContent() && children[i].getEndOffset() < offset) { + prevLine = children[i]; + break; + } + } + if (prevLine == null) { + // return this.getStartOffset() - 1; + return -1; + } else { + return prevLine.viewToModel(context, x - prevLine.getX(), 0); + } + } + + public int getStartOffset() { + return this.firstContentLine.getStartOffset(); + } + + public boolean hasContent() { + return this.firstContentLine != null; + } + + public IntRange layout(LayoutContext context, int top, int bottom) { + return null; + } + + public void invalidate(boolean direct) { + throw new IllegalStateException( + "invalidate called on a non-element BlockBox"); + } + + public void setInitialSize(LayoutContext context) { + // NOP - size calculated in factory method + } + + public String toString() { + return "ParagraphBox"; + } + + public int viewToModel(LayoutContext context, int x, int y) { + + LineBox[] children = this.children; + for (int i = 0; i < children.length; i++) { + Box child = children[i]; + if (child.hasContent() && y <= child.getY() + child.getHeight()) { + return child.viewToModel(context, x - child.getX(), y + - child.getY()); + } + } + throw new RuntimeException("No line at (" + x + ", " + y + ")"); + } + + // ===================================================== PRIVATE + +} |