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