| /******************************************************************************* |
| * 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); |
| } |
| |
| } |