| /******************************************************************************* |
| * Copyright (c) 2004, 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 com.ibm.icu.text.Bidi; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.TextLayout; |
| |
| /** |
| * A helper class for a BlockFlow that does Bidi evaluation of all the text in |
| * that block. |
| * <p> |
| * WARNING: This class is for INTERNAL use only. |
| * |
| * @author Pratik Shah |
| * @since 3.1 |
| */ |
| public final class BidiProcessor { |
| |
| /** |
| * A helper class to hold information about contributions made to this |
| * processor. |
| * |
| * @author Pratik Shah |
| * @since 3.1 |
| */ |
| private static class BidiEntry { |
| int begin, end; |
| FlowFigure fig; |
| |
| BidiEntry(FlowFigure fig, int offset, int length) { |
| this.fig = fig; |
| this.begin = offset; |
| this.end = offset + length; |
| } |
| } |
| |
| /** |
| * A singleton instance. |
| */ |
| public static final BidiProcessor INSTANCE = new BidiProcessor(); |
| |
| private StringBuffer bidiText; |
| private List list = new ArrayList(); |
| private int orientation = SWT.LEFT_TO_RIGHT; |
| |
| private BidiProcessor() { |
| } |
| |
| /** |
| * Records a String contribution for this bidi context. Contributions are |
| * concatenated (in the order that they were contributed) to make the final |
| * String which will determine the bidi info for all contributors. |
| * |
| * @param fig |
| * the figure that is contributing the given text |
| * @param str |
| * the text contributed by the given figure |
| * @see #addControlChar(char) |
| */ |
| public void add(FlowFigure fig, String str) { |
| // We are currently tracking empty contributions ("") |
| list.add(new BidiEntry(fig, bidiText.length(), str.length())); |
| bidiText.append(str); |
| } |
| |
| /** |
| * Records a character contribution for this bidi context. Contributions are |
| * concatenated (in the order that they were contributed) to make the final |
| * String which will determine the bidi info for all contributors. |
| * |
| * @param fig |
| * the figure that is contributing the given text |
| * @param c |
| * the character being added |
| * @see #addControlChar(char) |
| */ |
| public void add(FlowFigure fig, char c) { |
| list.add(new BidiEntry(fig, bidiText.length(), 1)); |
| bidiText.append(c); |
| } |
| |
| /** |
| * This methods allows FlowFigures to contribute text that may effect the |
| * bidi evaluation, but is not text that is visible on the screen. The bidi |
| * level of such text is reported back to the contributing figure. |
| * |
| * @param c |
| * the control character |
| */ |
| public void addControlChar(char c) { |
| bidiText.append(c); |
| } |
| |
| /** |
| * Breaks the given int array into bidi levels for each figure based on |
| * their contributed text, and assigns those levels to each figure. Also |
| * determines if shaping needs to occur between figures and sets the |
| * appendJoiner, prependJoiner accordingly. |
| * |
| * @param levels |
| * the calculated levels of all the text in the block |
| */ |
| private void assignResults(int[] levels) { |
| BidiEntry prevEntry = null, entry = null; |
| BidiInfo prevInfo = null, info = null; |
| int end = 2, start = 0; |
| for (int i = 0; i < list.size(); i++) { |
| entry = (BidiEntry) list.get(i); |
| info = new BidiInfo(); |
| |
| while (levels[end] < entry.end) |
| end += 2; |
| |
| int levelInfo[]; |
| if (end == start) { |
| levelInfo = new int[1]; |
| if (prevInfo != null) |
| levelInfo[0] = prevInfo.levelInfo[prevInfo.levelInfo.length - 1]; |
| else |
| levelInfo[0] = (orientation == SWT.LEFT_TO_RIGHT) ? 0 : 1; |
| } else { |
| levelInfo = new int[end - start - 1]; |
| System.arraycopy(levels, start + 1, levelInfo, 0, |
| levelInfo.length); |
| } |
| for (int j = 1; j < levelInfo.length; j += 2) |
| levelInfo[j] -= entry.begin; |
| info.levelInfo = levelInfo; |
| |
| // Compare current and previous for joiners, and commit previous |
| // BidiInfo. |
| if (prevEntry != null) { |
| if (// if we started in the middle of a level run |
| levels[start] < entry.begin |
| // and the level run is odd |
| && levels[start + 1] % 2 == 1 |
| // and the first character of this figure is Arabic |
| && isJoiner(entry.begin) |
| // and the last character of the previous figure was |
| // Arabic |
| && isPrecedingJoiner(entry.begin)) |
| prevInfo.trailingJoiner = info.leadingJoiner = true; |
| prevEntry.fig.setBidiInfo(prevInfo); |
| } |
| prevEntry = entry; |
| prevInfo = info; |
| if (entry.end == levels[end]) |
| start = end; |
| else |
| start = end - 2; |
| } |
| if (entry != null) |
| entry.fig.setBidiInfo(info); |
| } |
| |
| private boolean isJoiner(int begin) { |
| return begin < bidiText.length() |
| && isJoiningCharacter(bidiText.charAt(begin)); |
| } |
| |
| /** |
| * @param the |
| * character to be evaluated |
| * @return <code>true</code> if the given character is Arabic or ZWJ |
| */ |
| private boolean isJoiningCharacter(char c) { |
| return Character.getDirectionality(c) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC |
| || c == BidiChars.ZWJ; |
| } |
| |
| private boolean isPrecedingJoiner(int begin) { |
| return begin > 0 && isJoiningCharacter(bidiText.charAt(begin - 1)); |
| } |
| |
| /** |
| * Processes the contributed text, determines the Bidi levels, and assigns |
| * them to the FlowFigures that made thet contributions. This class is for |
| * INTERNAL use only. Shaping of visually contiguous Arabic characters that |
| * are split in different figures is also handled. This method will do |
| * nothing if the contributed text does not require Bidi evaluation. All |
| * contributions are discarded at the end of this method. |
| */ |
| public void process() { |
| try { |
| if (bidiText.length() == 0) |
| return; |
| char[] chars = new char[bidiText.length()]; |
| bidiText.getChars(0, bidiText.length(), chars, 0); |
| |
| //UNSUPPORTED - Not supported in RAP |
| // if (orientation != SWT.RIGHT_TO_LEFT |
| // && !Bidi.requiresBidi(chars, 0, chars.length - 1)) |
| // return; |
| |
| int[] levels = new int[15]; |
| TextLayout layout = FlowUtilities.getTextLayout(); |
| |
| layout.setOrientation(orientation); |
| layout.setText(bidiText.toString()); |
| int j = 0, offset, prevLevel = -1; |
| for (offset = 0; offset < chars.length; offset++) { |
| int newLevel = layout.getLevel(offset); |
| if (newLevel != prevLevel) { |
| if (j + 3 > levels.length) { |
| int temp[] = levels; |
| levels = new int[levels.length * 2 + 1]; |
| System.arraycopy(temp, 0, levels, 0, temp.length); |
| } |
| levels[j++] = offset; |
| levels[j++] = newLevel; |
| prevLevel = newLevel; |
| } |
| } |
| levels[j++] = offset; |
| |
| if (j != levels.length) { |
| int[] newLevels = new int[j]; |
| System.arraycopy(levels, 0, newLevels, 0, j); |
| levels = newLevels; |
| } |
| assignResults(levels); |
| |
| // reset the orientation of the layout, in case it was set to RTL |
| layout.setOrientation(SWT.LEFT_TO_RIGHT); |
| } finally { |
| // will cause the fields to be reset for the next string to be |
| // processed |
| bidiText = null; |
| list.clear(); |
| } |
| } |
| |
| /** |
| * Sets the paragraph embedding. The given orientation will be used on |
| * TextLayout when determining the Bidi levels. |
| * |
| * @param newOrientation |
| * SWT.LEFT_TO_RIGHT or SWT.RIGHT_TO_LEFT |
| */ |
| public void setOrientation(int newOrientation) { |
| bidiText = new StringBuffer(); |
| list.clear(); |
| orientation = newOrientation; |
| } |
| |
| } |