paragraph

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/BaseBoxVisitor.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/BaseBoxVisitor.java
index 565e77c..8fb3c17 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/BaseBoxVisitor.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/BaseBoxVisitor.java
@@ -29,4 +29,14 @@
 	public void visit(final HorizontalBar box) {
 		// ignore
 	}
+
+	@Override
+	public void visit(final Paragraph box) {
+		// ignore
+	}
+
+	@Override
+	public void visit(final StaticText box) {
+		// ignore
+	}
 }
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IBoxVisitor.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IBoxVisitor.java
index efa929c..ddc262c 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IBoxVisitor.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IBoxVisitor.java
@@ -20,4 +20,8 @@
 	void visit(VerticalBlock box);
 
 	void visit(HorizontalBar box);
+
+	void visit(Paragraph box);
+
+	void visit(StaticText box);
 }
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IInlineBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IInlineBox.java
new file mode 100644
index 0000000..a4a367d
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/IInlineBox.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Florian Thienel 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:
+ * 		Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import org.eclipse.vex.core.internal.core.Rectangle;
+
+/**
+ * @author Florian Thienel
+ */
+public interface IInlineBox extends IBox {
+
+	void setPosition(int top, int left);
+
+	int getTop();
+
+	int getLeft();
+
+	/**
+	 * The baseline is relative to the top of this box.
+	 */
+	int getBaseline();
+
+	/**
+	 * The bounds are always relative to the parent box.
+	 */
+	Rectangle getBounds();
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Paragraph.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Paragraph.java
new file mode 100644
index 0000000..434ae0f
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Paragraph.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Florian Thienel 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:
+ * 		Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import java.util.ArrayList;
+
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.core.Rectangle;
+
+/**
+ * @author Florian Thienel
+ */
+public class Paragraph implements IChildBox {
+
+	private int top;
+	private int left;
+	private int width;
+	private int height;
+
+	private int visibleChildren;
+
+	private final ArrayList<IInlineBox> children = new ArrayList<IInlineBox>();
+
+	@Override
+	public void setPosition(final int top, final int left) {
+		this.top = top;
+		this.left = left;
+	}
+
+	@Override
+	public Rectangle getBounds() {
+		return new Rectangle(left, top, width, height);
+	}
+
+	@Override
+	public int getTop() {
+		return top;
+	}
+
+	@Override
+	public int getLeft() {
+		return left;
+	}
+
+	@Override
+	public int getWidth() {
+		return width;
+	}
+
+	@Override
+	public void setWidth(final int width) {
+		this.width = width;
+	}
+
+	@Override
+	public int getHeight() {
+		return height;
+	}
+
+	@Override
+	public void accept(final IBoxVisitor visitor) {
+		visitor.visit(this);
+	}
+
+	public boolean hasChildren() {
+		return !children.isEmpty();
+	}
+
+	public void appendChild(final IInlineBox box) {
+		children.add(box);
+	}
+
+	@Override
+	public void layout(final Graphics graphics) {
+		height = 0;
+		visibleChildren = 0;
+		int baseline = 0;
+		int currentWidth = 0;
+		for (int i = 0; i < children.size(); i += 1) {
+			final IInlineBox child = children.get(i);
+
+			child.layout(graphics);
+
+			height = Math.max(height, child.getHeight());
+			baseline = Math.max(baseline, child.getBaseline());
+
+			visibleChildren += 1;
+			currentWidth += child.getWidth();
+			if (currentWidth >= width) {
+				break;
+			}
+		}
+
+		int childLeft = 0;
+		for (int i = 0; i < visibleChildren; i += 1) {
+			final IInlineBox child = children.get(i);
+
+			final int childTop = baseline - child.getBaseline();
+			child.setPosition(childTop, childLeft);
+
+			childLeft += child.getWidth();
+			if (childLeft >= width) {
+				break;
+			}
+		}
+	}
+
+	@Override
+	public void paint(final Graphics graphics) {
+		for (int i = 0; i < visibleChildren; i += 1) {
+			final IInlineBox child = children.get(i);
+			graphics.moveOrigin(child.getLeft(), child.getTop());
+			child.paint(graphics);
+			graphics.moveOrigin(-child.getLeft(), -child.getTop());
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/StaticText.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/StaticText.java
new file mode 100644
index 0000000..2b5f381
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/StaticText.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Florian Thienel 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:
+ * 		Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import org.eclipse.vex.core.internal.core.FontMetrics;
+import org.eclipse.vex.core.internal.core.FontResource;
+import org.eclipse.vex.core.internal.core.FontSpec;
+import org.eclipse.vex.core.internal.core.Graphics;
+import org.eclipse.vex.core.internal.core.Rectangle;
+
+/**
+ * @author Florian Thienel
+ */
+public class StaticText implements IInlineBox {
+
+	private int top;
+	private int left;
+	private int width;
+	private int height;
+	private int baseline;
+
+	private String text;
+	private FontSpec fontSpec;
+
+	private boolean layoutValid;
+
+	@Override
+	public void setPosition(final int top, final int left) {
+		this.top = top;
+		this.left = left;
+	}
+
+	@Override
+	public Rectangle getBounds() {
+		return new Rectangle(left, top, width, height);
+	}
+
+	@Override
+	public int getTop() {
+		return top;
+	}
+
+	@Override
+	public int getLeft() {
+		return left;
+	}
+
+	@Override
+	public int getWidth() {
+		return width;
+	}
+
+	@Override
+	public int getHeight() {
+		return height;
+	}
+
+	@Override
+	public int getBaseline() {
+		return baseline;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public void setText(final String text) {
+		this.text = text;
+		layoutValid = false;
+	}
+
+	public FontSpec getFont() {
+		return fontSpec;
+	}
+
+	public void setFont(final FontSpec fontSpec) {
+		this.fontSpec = fontSpec;
+		layoutValid = false;
+	}
+
+	@Override
+	public void accept(final IBoxVisitor visitor) {
+		visitor.visit(this);
+	}
+
+	@Override
+	public void layout(final Graphics graphics) {
+		if (layoutValid) {
+			return;
+		}
+
+		final FontResource font = graphics.getFont(fontSpec);
+		graphics.setCurrentFont(font);
+		width = graphics.stringWidth(text);
+
+		final FontMetrics fontMetrics = graphics.getFontMetrics();
+		height = fontMetrics.getHeight();
+		baseline = fontMetrics.getAscent() + fontMetrics.getLeading();
+
+		layoutValid = true;
+	}
+
+	@Override
+	public void paint(final Graphics graphics) {
+		final FontResource font = graphics.getFont(fontSpec);
+		graphics.setCurrentFont(font);
+		graphics.drawString(text, 0, 0);
+	}
+
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
index 0a0dea1..8f918f3 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
@@ -71,16 +71,32 @@
 		}
 
 		rootBox = new RootBox();
-		for (int i = 0; i < 500000; i += 1) {
-			final HorizontalBar bar = new HorizontalBar();
-			bar.setHeight(10);
-			bar.setColor(Color.BLACK);
+		for (int i = 0; i < 50000; i += 1) {
 			final VerticalBlock block = new VerticalBlock();
-			block.appendChild(bar);
 			block.setMargin(new Margin(10, 20, 30, 40));
 			block.setBorder(new Border(10));
 			block.setPadding(new Padding(15, 25, 35, 45));
 			rootBox.appendChild(block);
+
+			final HorizontalBar upperBar = new HorizontalBar();
+			upperBar.setHeight(2);
+			upperBar.setColor(Color.BLACK);
+			block.appendChild(upperBar);
+
+			final Paragraph paragraph = new Paragraph();
+			block.appendChild(paragraph);
+
+			for (int j = 0; j < 20; j += 1) {
+				final StaticText lorem = new StaticText();
+				lorem.setText("Lorem ipsum " + i + " ");
+				lorem.setFont(new FontSpec(new String[] { "Arial" }, 0, 10.0f + j));
+				paragraph.appendChild(lorem);
+			}
+
+			final HorizontalBar lowerBar = new HorizontalBar();
+			lowerBar.setHeight(2);
+			lowerBar.setColor(Color.BLACK);
+			block.appendChild(lowerBar);
 		}
 	}