blob: 19f3ac5789e4ff98b57897ff4c96052142d6ae3b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.draw2d.text;
import java.util.List;
import org.eclipse.swt.graphics.Font;
/**
* The layout for {@link TextFlow}.
*
* @author hudsonr
* @since 2.1
*/
public class ParagraphTextLayout extends TextLayout {
/**
* Wrapping will ONLY occur at valid line breaks
*/
public static final int WORD_WRAP_HARD = 0;
/**
* Wrapping will always occur at the end of the available space, breaking in
* the middle of a word.
*/
public static final int WORD_WRAP_SOFT = 1;
/**
* Wrapping will always occur at the end of available space, truncating a
* word if it doesn't fit. Note that truncation is not supported across
* multiple figures and with BiDi. Undesired effects may result if that is
* the case.
*/
public static final int WORD_WRAP_TRUNCATE = 2;
private int wrappingStyle = WORD_WRAP_HARD;
/**
* Constructs a new ParagraphTextLayout on the specified TextFlow.
*
* @param flow
* the TextFlow
*/
public ParagraphTextLayout(TextFlow flow) {
super(flow);
}
/**
* Constructs the layout with the specified TextFlow and wrapping style. The
* wrapping style must be one of:
* <UL>
* <LI>{@link #WORD_WRAP_HARD}</LI>
* <LI>{@link #WORD_WRAP_SOFT}</LI>
* <LI>{@link #WORD_WRAP_TRUNCATE}</LI>
* </UL>
*
* @param flow
* the textflow
* @param style
* the style of wrapping
*/
public ParagraphTextLayout(TextFlow flow, int style) {
this(flow);
wrappingStyle = style;
}
/**
* Given the Bidi levels of the given text, this method breaks the given
* text up by its level runs.
*
* @param text
* the String that needs to be broken up into its level runs
* @param levelInfo
* the Bidi levels
* @return the requested segment
*/
private String[] getSegments(String text, int levelInfo[]) {
if (levelInfo.length == 1)
return new String[] { text };
String result[] = new String[levelInfo.length / 2 + 1];
int i;
int endOffset;
int beginOffset = 0;
for (i = 0; i < result.length - 1; i++) {
endOffset = levelInfo[i * 2 + 1];
result[i] = text.substring(beginOffset, endOffset);
beginOffset = endOffset;
}
endOffset = text.length();
result[i] = text.substring(beginOffset, endOffset);
return result;
}
class SegmentLookahead implements FlowUtilities.LookAhead {
private int seg = -1;
private String segs[];
private int[] width;
private final int trailingBorderSize;
SegmentLookahead(String segs[], int trailingBorderSize) {
this.segs = segs;
this.trailingBorderSize = trailingBorderSize;
}
public int getWidth() {
if (width == null) {
width = new int[1];
int startingIndex = seg + 1;
TextFlow textFlow = (TextFlow) getFlowFigure();
if (startingIndex == segs.length) {
width[0] += trailingBorderSize;
getContext().getWidthLookahead(textFlow, width);
} else {
String rest = segs[startingIndex];
for (int k = startingIndex + 1; k < segs.length; k++)
rest += segs[k];
if (!textFlow.addLeadingWordWidth(rest, width)) {
width[0] += trailingBorderSize;
getContext().getWidthLookahead(textFlow, width);
}
}
}
return width[0];
}
public void setIndex(int value) {
this.seg = value;
width = null;
}
}
/**
* @see org.eclipse.draw2d.text.FlowFigureLayout#layout()
*/
protected void layout() {
TextFlow textFlow = (TextFlow) getFlowFigure();
int offset = 0;
FlowContext context = getContext();
List fragments = textFlow.getFragments();
Font font = textFlow.getFont();
int fragIndex = 0;
int advance = 0;
TextFragmentBox fragment;
int levelInfo[] = (textFlow.getBidiInfo() == null) ? new int[] { -1 }
: textFlow.getBidiInfo().levelInfo;
String segment, segments[] = getSegments(textFlow.getText(), levelInfo);
FlowBorder border = null;
if (textFlow.getBorder() instanceof FlowBorder)
border = (FlowBorder) textFlow.getBorder();
SegmentLookahead lookahead = new SegmentLookahead(segments,
border == null ? 0 : border.getRightMargin());
int seg;
if (border != null) {
fragment = getFragment(fragIndex++, fragments);
fragment.setBidiLevel(levelInfo[0]);
fragment.setTruncated(false);
fragment.offset = fragment.length = -1;
fragment.setWidth(border.getLeftMargin()
+ border.getInsets(textFlow).left);
if (context.getRemainingLineWidth() < fragment.getWidth()
+ lookahead.getWidth())
context.endLine();
context.addToCurrentLine(fragment);
}
FlowUtilities flowUtilities = textFlow.getFlowUtilities();
for (seg = 0; seg < segments.length; seg++) {
segment = segments[seg];
lookahead.setIndex(seg);
do {
fragment = getFragment(fragIndex++, fragments);
fragment.offset = offset;
fragment.setBidiLevel(levelInfo[seg * 2]);
advance = flowUtilities.wrapFragmentInContext(fragment,
segment, context, lookahead, font, wrappingStyle);
segment = segment.substring(advance);
offset += advance;
if ((segment.length() > 0 || fragment.length < advance)
|| fragment.isTruncated())
context.endLine();
} while (segment.length() > 0
|| (!fragment.isTruncated() && fragment.length < advance));
}
if (border != null) {
fragment = getFragment(fragIndex++, fragments);
fragment.setBidiLevel(levelInfo[0]);
fragment.setTruncated(false);
fragment.offset = fragment.length = -1;
fragment.setWidth(border.getRightMargin()
+ border.getInsets(textFlow).right);
context.addToCurrentLine(fragment);
}
// Remove the remaining unused fragments.
while (fragIndex < fragments.size())
fragments.remove(fragments.size() - 1);
}
}