/******************************************************************************* * 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 *******************************************************************************/ package net.sf.vex.layout; import java.util.ArrayList; import java.util.List; import net.sf.vex.core.Caret; import net.sf.vex.core.Insets; import net.sf.vex.dom.Element; /** * Box representing a row in a table. */ public class TableRowBox extends AbstractBlockBox { public TableRowBox(LayoutContext context, TableRowGroupBox parent, Element element) { super(context, parent, element); } public TableRowBox(LayoutContext context, BlockBox parent, int startOffset, int endOffset) { super(context, parent, startOffset, endOffset); } protected List createChildren(final LayoutContext context) { final List children = new ArrayList(); Element element = this.findContainingElement(); final int[] widths = this.getTableBox().getColumnWidths(); LayoutUtils.iterateTableCells(context.getStyleSheet(), element, this.getStartOffset(), this.getEndOffset(), new ElementOrRangeCallback() { private int column = 0; public void onElement(Element child, String displayStyle) { children.add(new TableCellBox(context, TableRowBox.this, child, widths[column++])); } public void onRange(Element parent, int startOffset, int endOffset) { children.add(new TableCellBox(context, TableRowBox.this, startOffset, endOffset, widths[column++])); } }); return children; } /** * Override drawBox to do nothing. Table rows have no borders in * border-collapse:separate mode. */ public void drawBox(LayoutContext context, int x, int y, int containerWidth, boolean drawBorders) { } public Caret getCaret(LayoutContext context, int offset) { int hSpacing = this.getTableBox().getHorizonalSpacing(); Box[] children = this.getChildren(); // If we haven't yet laid out this block, estimate the caret. if (children == null) { int relative = offset - this.getStartOffset(); int size = this.getEndOffset() - this.getStartOffset(); int y = 0; if (size > 0) { y = this.getHeight() * relative / size; } return new HCaret(0, y, this.getWidth()); } int x = hSpacing / 2; int[] widths = this.getTableBox().getColumnWidths(); for (int i = 0; i < children.length; i++) { Box child = children[i]; if (!child.hasContent()) { continue; // TODO can we really have generated table cells? } if (offset < child.getStartOffset()) { return new TextCaret(x, 0, this.getHeight()); } if (offset >= child.getStartOffset() && offset <= child.getEndOffset()) { Caret caret = child.getCaret(context, offset); caret.translate(child.getX(), child.getY()); return caret; } x += widths[i] + hSpacing; } return new TextCaret(x, 0, this.getHeight()); } /** * Override to return zero insets. Table rows have no insets in * border-collapse:separate mode. */ public Insets getInsets(LayoutContext context, int containerWidth) { return Insets.ZERO_INSETS; } public int getMarginBottom() { return 0; } public int getMarginTop() { return 0; } public int getNextLineOffset(LayoutContext context, int offset, int x) { BlockBox[] children = this.getContentChildren(); int[] widths = this.getTableBox().getColumnWidths(); int leftEdge = 0; for (int i = 0; i < children.length; i++) { if (leftEdge + widths[i] > x) { int newOffset = children[i].getNextLineOffset(context, offset, x - leftEdge); if (newOffset == children[i].getEndOffset() + 1) { return -1; } else { return newOffset; } } leftEdge += widths[i]; } return -1; } public int getPreviousLineOffset(LayoutContext context, int offset, int x) { BlockBox[] children = this.getContentChildren(); int[] widths = this.getTableBox().getColumnWidths(); int leftEdge = 0; for (int i = 0; i < children.length; i++) { if (leftEdge + widths[i] > x) { int newOffset = children[i].getPreviousLineOffset(context, offset, x - leftEdge); if (newOffset == children[i].getStartOffset() - 1) { return -1; } else { return newOffset; } } leftEdge += widths[i]; } return -1; } /** * Returns the TableBox associated with this row. */ public TableBox getTableBox() { return (TableBox) this.getParent().getParent().getParent(); } protected int positionChildren(LayoutContext context) { int hSpacing = this.getTableBox().getHorizonalSpacing(); int childX = hSpacing; int topInset = 0; int height = 0; int bottomInset = 0; for (int i = 0; i < this.getChildren().length; i++) { Box child = this.getChildren()[i]; Insets insets = child.getInsets(context, this.getWidth()); childX += insets.getLeft(); child.setX(childX); childX += child.getWidth() + insets.getRight() + hSpacing; topInset = Math.max(topInset, insets.getTop()); height = Math.max(height, child.getHeight()); bottomInset = Math.max(bottomInset, insets.getBottom()); } this.setHeight(topInset + height + bottomInset); for (int i = 0; i < this.getChildren().length; i++) { Box child = this.getChildren()[i]; child.setY(topInset); child.setHeight(height); } return -1; // TODO revisit } public int viewToModel(LayoutContext context, int x, int y) { Box[] children = this.getChildren(); if (children == null) { int charCount = this.getEndOffset() - this.getStartOffset() - 1; if (charCount == 0 || this.getHeight() == 0) { return this.getEndOffset(); } else { return this.getStartOffset() + charCount * y / this.getHeight(); } } else { for (int i = 0; i < children.length; i++) { Box child = children[i]; if (!child.hasContent()) { continue; } if (x < child.getX()) { return child.getStartOffset() - 1; } else if (x < child.getX() + child.getWidth()) { return child.viewToModel(context, x - child.getX(), y - child.getY()); } } } return this.getEndOffset(); } }