Project

General

Profile

Download (7.77 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 * IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.draw2d.text;
12

    
13
//import com.ibm.icu.text.Bidi;
14

    
15
import java.util.ArrayList;
16
import java.util.List;
17

    
18
import org.eclipse.draw2d.rap.swt.SWT;
19
import org.eclipse.swt.graphics.TextLayout;
20

    
21
/**
22
 * A helper class for a BlockFlow that does Bidi evaluation of all the text in
23
 * that block.
24
 * <p>
25
 * WARNING: This class is for INTERNAL use only.
26
 * 
27
 * @author Pratik Shah
28
 * @since 3.1
29
 */
30
public final class BidiProcessor {
31

    
32
    /*
33
	 * Workaround for Mac OS. AWT DLL cannot start properly on carbon.
34
	 * Waiting for bug 82104
35
	 */
36
	private static final boolean isMacOS = SWT.getPlatform().equals("carbon") || SWT.getPlatform().equals("cocoa"); //$NON-NLS-1$ //$NON-NLS-2$
37

    
38
	/**
39
	 * A helper class to hold information about contributions made to this
40
	 * processor.
41
	 * 
42
	 * @author Pratik Shah
43
	 * @since 3.1
44
	 */
45
	private static class BidiEntry {
46
		int begin, end;
47
		FlowFigure fig;
48

    
49
		BidiEntry(FlowFigure fig, int offset, int length) {
50
			this.fig = fig;
51
			this.begin = offset;
52
			this.end = offset + length;
53
		}
54
	}
55

    
56
	/**
57
	 * A singleton instance.
58
	 */
59
	public static final BidiProcessor INSTANCE = new BidiProcessor();
60

    
61
	private StringBuffer bidiText;
62
	private List list = new ArrayList();
63
	private int orientation = SWT.LEFT_TO_RIGHT;
64

    
65
	private BidiProcessor() {
66
	}
67

    
68
	/**
69
	 * Records a String contribution for this bidi context. Contributions are
70
	 * concatenated (in the order that they were contributed) to make the final
71
	 * String which will determine the bidi info for all contributors.
72
	 * 
73
	 * @param fig
74
	 *            the figure that is contributing the given text
75
	 * @param str
76
	 *            the text contributed by the given figure
77
	 * @see #addControlChar(char)
78
	 */
79
	public void add(FlowFigure fig, String str) {
80
		// We are currently tracking empty contributions ("")
81
		list.add(new BidiEntry(fig, bidiText.length(), str.length()));
82
		bidiText.append(str);
83
	}
84

    
85
	/**
86
	 * Records a character contribution for this bidi context. Contributions are
87
	 * concatenated (in the order that they were contributed) to make the final
88
	 * String which will determine the bidi info for all contributors.
89
	 * 
90
	 * @param fig
91
	 *            the figure that is contributing the given text
92
	 * @param c
93
	 *            the character being added
94
	 * @see #addControlChar(char)
95
	 */
96
	public void add(FlowFigure fig, char c) {
97
		list.add(new BidiEntry(fig, bidiText.length(), 1));
98
		bidiText.append(c);
99
	}
100

    
101
	/**
102
	 * This methods allows FlowFigures to contribute text that may effect the
103
	 * bidi evaluation, but is not text that is visible on the screen. The bidi
104
	 * level of such text is reported back to the contributing figure.
105
	 * 
106
	 * @param c
107
	 *            the control character
108
	 */
109
	public void addControlChar(char c) {
110
		bidiText.append(c);
111
	}
112

    
113
	/**
114
	 * Breaks the given int array into bidi levels for each figure based on
115
	 * their contributed text, and assigns those levels to each figure. Also
116
	 * determines if shaping needs to occur between figures and sets the
117
	 * appendJoiner, prependJoiner accordingly.
118
	 * 
119
	 * @param levels
120
	 *            the calculated levels of all the text in the block
121
	 */
122
	private void assignResults(int[] levels) {
123
		BidiEntry prevEntry = null, entry = null;
124
		BidiInfo prevInfo = null, info = null;
125
		int end = 2, start = 0;
126
		for (int i = 0; i < list.size(); i++) {
127
			entry = (BidiEntry) list.get(i);
128
			info = new BidiInfo();
129

    
130
			while (levels[end] < entry.end)
131
				end += 2;
132

    
133
			int levelInfo[];
134
			if (end == start) {
135
				levelInfo = new int[1];
136
				if (prevInfo != null)
137
					levelInfo[0] = prevInfo.levelInfo[prevInfo.levelInfo.length - 1];
138
				else
139
					levelInfo[0] = (orientation == SWT.LEFT_TO_RIGHT) ? 0 : 1;
140
			} else {
141
				levelInfo = new int[end - start - 1];
142
				System.arraycopy(levels, start + 1, levelInfo, 0,
143
						levelInfo.length);
144
			}
145
			for (int j = 1; j < levelInfo.length; j += 2)
146
				levelInfo[j] -= entry.begin;
147
			info.levelInfo = levelInfo;
148

    
149
			// Compare current and previous for joiners, and commit previous
150
			// BidiInfo.
151
			if (prevEntry != null) {
152
				if (// if we started in the middle of a level run
153
				levels[start] < entry.begin
154
				// and the level run is odd
155
						&& levels[start + 1] % 2 == 1
156
						// and the first character of this figure is Arabic
157
						&& isJoiner(entry.begin)
158
						// and the last character of the previous figure was
159
						// Arabic
160
						&& isPrecedingJoiner(entry.begin))
161
					prevInfo.trailingJoiner = info.leadingJoiner = true;
162
				prevEntry.fig.setBidiInfo(prevInfo);
163
			}
164
			prevEntry = entry;
165
			prevInfo = info;
166
			if (entry.end == levels[end])
167
				start = end;
168
			else
169
				start = end - 2;
170
		}
171
		if (entry != null)
172
			entry.fig.setBidiInfo(info);
173
	}
174

    
175
	private boolean isJoiner(int begin) {
176
		return begin < bidiText.length()
177
				&& isJoiningCharacter(bidiText.charAt(begin));
178
	}
179

    
180
	/**
181
	 * @param the
182
	 *            character to be evaluated
183
	 * @return <code>true</code> if the given character is Arabic or ZWJ
184
	 */
185
	private boolean isJoiningCharacter(char c) {
186
		return Character.getDirectionality(c) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
187
				|| c == BidiChars.ZWJ;
188
	}
189

    
190
	private boolean isPrecedingJoiner(int begin) {
191
		return begin > 0 && isJoiningCharacter(bidiText.charAt(begin - 1));
192
	}
193

    
194
	/**
195
	 * Processes the contributed text, determines the Bidi levels, and assigns
196
	 * them to the FlowFigures that made thet contributions. This class is for
197
	 * INTERNAL use only. Shaping of visually contiguous Arabic characters that
198
	 * are split in different figures is also handled. This method will do
199
	 * nothing if the contributed text does not require Bidi evaluation. All
200
	 * contributions are discarded at the end of this method.
201
	 */
202
	public void process() {
203
		try {
204
			if (bidiText.length() == 0 || isMacOS)
205
				return;
206
			char[] chars = new char[bidiText.length()];
207
			bidiText.getChars(0, bidiText.length(), chars, 0);
208

    
209
			//Not supported in RAP
210
			// if (orientation != SWT.RIGHT_TO_LEFT
211
			// && !Bidi.requiresBidi(chars, 0, chars.length - 1))
212
			// return;
213

    
214
			int[] levels = new int[15];
215
			TextLayout layout = FlowUtilities.getTextLayout();
216

    
217
			layout.setOrientation(orientation);
218
			layout.setText(bidiText.toString());
219
			int j = 0, offset, prevLevel = -1;
220
			for (offset = 0; offset < chars.length; offset++) {
221
				int newLevel = layout.getLevel(offset);
222
				if (newLevel != prevLevel) {
223
					if (j + 3 > levels.length) {
224
						int temp[] = levels;
225
						levels = new int[levels.length * 2 + 1];
226
						System.arraycopy(temp, 0, levels, 0, temp.length);
227
					}
228
					levels[j++] = offset;
229
					levels[j++] = newLevel;
230
					prevLevel = newLevel;
231
				}
232
			}
233
			levels[j++] = offset;
234

    
235
			if (j != levels.length) {
236
				int[] newLevels = new int[j];
237
				System.arraycopy(levels, 0, newLevels, 0, j);
238
				levels = newLevels;
239
			}
240
			assignResults(levels);
241

    
242
			// reset the orientation of the layout, in case it was set to RTL
243
			layout.setOrientation(SWT.LEFT_TO_RIGHT);
244
		} finally {
245
			// will cause the fields to be reset for the next string to be
246
			// processed
247
			bidiText = null;
248
			list.clear();
249
		}
250
	}
251

    
252
	/**
253
	 * Sets the paragraph embedding. The given orientation will be used on
254
	 * TextLayout when determining the Bidi levels.
255
	 * 
256
	 * @param newOrientation
257
	 *            SWT.LEFT_TO_RIGHT or SWT.RIGHT_TO_LEFT
258
	 */
259
	public void setOrientation(int newOrientation) {
260
		bidiText = new StringBuffer();
261
		list.clear();
262
		orientation = newOrientation;
263
	}
264

    
265
}
(4-4/31)