154
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2007 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 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwt.internal.BidiUtil;
|
|
14
|
|
15
|
|
16 import dwt.DWT;
|
|
17 import dwt.graphics.GC;
|
|
18 import dwt.internal.win32.OS;
|
|
19
|
|
20 import tango.util.Convert;
|
|
21 import dwt.dwthelper.utils;
|
|
22 import dwt.dwthelper.Integer;
|
|
23 import dwt.dwthelper.Runnable;
|
|
24
|
|
25 /*
|
|
26 * Wraps Win32 API used to bidi enable the StyledText widget.
|
|
27 */
|
|
28 public class BidiUtil {
|
|
29
|
|
30 // Keyboard language ids
|
|
31 public static const int KEYBOARD_NON_BIDI = 0;
|
|
32 public static const int KEYBOARD_BIDI = 1;
|
|
33
|
|
34 // bidi flag
|
|
35 static int isBidiPlatform_ = -1;
|
|
36
|
|
37 // getRenderInfo flag values
|
|
38 public static const int CLASSIN = 1;
|
|
39 public static const int LINKBEFORE = 2;
|
|
40 public static const int LINKAFTER = 4;
|
|
41
|
|
42 // variables used for providing a listener mechanism for keyboard language
|
|
43 // switching
|
|
44 static Runnable[HWND] languageMap;
|
|
45 static Runnable[HWND] keyMap;
|
|
46 static WNDPROC[HWND] oldProcMap;
|
|
47 /*
|
|
48 * This code is intentionally commented. In order
|
|
49 * to support CLDC, .class cannot be used because
|
|
50 * it does not compile on some Java compilers when
|
|
51 * they are targeted for CLDC.
|
|
52 */
|
|
53 // static Callback callback = new Callback (BidiUtil.class, "windowProc", 4);
|
|
54 static const char[] CLASS_NAME = "org.eclipse.swt.internal.BidiUtil"; //$NON-NLS-1$
|
|
55 // static this() {
|
|
56 // try {
|
|
57 // callback = new Callback (Class.forName (CLASS_NAME), "windowProc", 4); //$NON-NLS-1$
|
|
58 // if (callback.getAddress () is 0) DWT.error (DWT.ERROR_NO_MORE_CALLBACKS);
|
|
59 // } catch (ClassNotFoundException e) {}
|
|
60 // }
|
|
61
|
|
62 // GetCharacterPlacement constants
|
|
63 static const int GCP_REORDER = 0x0002;
|
|
64 static const int GCP_GLYPHSHAPE = 0x0010;
|
|
65 static const int GCP_LIGATE = 0x0020;
|
|
66 static const int GCP_CLASSIN = 0x00080000;
|
|
67 static const byte GCPCLASS_ARABIC = 2;
|
|
68 static const byte GCPCLASS_HEBREW = 2;
|
|
69 static const byte GCPCLASS_LOCALNUMBER = 4;
|
|
70 static const byte GCPCLASS_LATINNUMBER = 5;
|
|
71 static const int GCPGLYPH_LINKBEFORE = 0x8000;
|
|
72 static const int GCPGLYPH_LINKAFTER = 0x4000;
|
|
73 // ExtTextOut constants
|
|
74 static const int ETO_CLIPPED = 0x4;
|
|
75 static const int ETO_GLYPH_INDEX = 0x0010;
|
|
76 // Windows primary language identifiers
|
|
77 static const int LANG_ARABIC = 0x01;
|
|
78 static const int LANG_HEBREW = 0x0d;
|
|
79 // code page identifiers
|
|
80 static const char[] CD_PG_HEBREW = "1255"; //$NON-NLS-1$
|
|
81 static const char[] CD_PG_ARABIC = "1256"; //$NON-NLS-1$
|
|
82 // ActivateKeyboard constants
|
|
83 static const int HKL_NEXT = 1;
|
|
84 static const int HKL_PREV = 0;
|
|
85
|
|
86 /*
|
|
87 * Public character class constants are the same as Windows
|
|
88 * platform constants.
|
|
89 * Saves conversion of class array in getRenderInfo to arbitrary
|
|
90 * constants for now.
|
|
91 */
|
|
92 public static const int CLASS_HEBREW = GCPCLASS_ARABIC;
|
|
93 public static const int CLASS_ARABIC = GCPCLASS_HEBREW;
|
|
94 public static const int CLASS_LOCALNUMBER = GCPCLASS_LOCALNUMBER;
|
|
95 public static const int CLASS_LATINNUMBER = GCPCLASS_LATINNUMBER;
|
|
96 public static const int REORDER = GCP_REORDER;
|
|
97 public static const int LIGATE = GCP_LIGATE;
|
|
98 public static const int GLYPHSHAPE = GCP_GLYPHSHAPE;
|
|
99
|
|
100 /**
|
|
101 * Adds a language listener. The listener will get notified when the language of
|
|
102 * the keyboard changes (via Alt-Shift on Win platforms). Do this by creating a
|
|
103 * window proc for the Control so that the window messages for the Control can be
|
|
104 * monitored.
|
|
105 * <p>
|
|
106 *
|
|
107 * @param hwnd the handle of the Control that is listening for keyboard language
|
|
108 * changes
|
|
109 * @param runnable the code that should be executed when a keyboard language change
|
|
110 * occurs
|
|
111 */
|
|
112 public static void addLanguageListener (HWND hwnd, Runnable runnable) {
|
|
113 languageMap[hwnd] = runnable;
|
|
114 subclass(hwnd);
|
|
115 }
|
|
116 /**
|
|
117 * Proc used for OS.EnumSystemLanguageGroups call during isBidiPlatform test.
|
|
118 */
|
|
119 static extern(Windows) int EnumSystemLanguageGroupsProc(uint lpLangGrpId, wchar* lpLangGrpIdString, wchar* lpLangGrpName, uint options, int lParam) {
|
|
120 if (lpLangGrpId is OS.LGRPID_HEBREW) {
|
|
121 isBidiPlatform_ = 1;
|
|
122 return 0;
|
|
123 }
|
|
124 if (lpLangGrpId is OS.LGRPID_ARABIC) {
|
|
125 isBidiPlatform_ = 1;
|
|
126 return 0;
|
|
127 }
|
|
128 return 1;
|
|
129 }
|
|
130 /**
|
|
131 * Wraps the ExtTextOut function.
|
|
132 * <p>
|
|
133 *
|
|
134 * @param gc the gc to use for rendering
|
|
135 * @param renderBuffer the glyphs to render as an array of characters
|
|
136 * @param renderDx the width of each glyph in renderBuffer
|
|
137 * @param x x position to start rendering
|
|
138 * @param y y position to start rendering
|
|
139 */
|
|
140 public static void drawGlyphs(GC gc, wchar[] renderBuffer, int[] renderDx, int x, int y) {
|
|
141 int length_ = renderDx.length;
|
|
142
|
|
143 if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
|
|
144 if (OS.GetLayout (gc.handle) !is 0) {
|
|
145 reverse(renderDx);
|
|
146 renderDx[length_-1]--; //fixes bug 40006
|
|
147 reverse(renderBuffer);
|
|
148 }
|
|
149 }
|
|
150 // render transparently to avoid overlapping segments. fixes bug 40006
|
|
151 int oldBkMode = OS.SetBkMode(gc.handle, OS.TRANSPARENT);
|
|
152 OS.ExtTextOutW(gc.handle, x, y, ETO_GLYPH_INDEX , null, renderBuffer.ptr, renderBuffer.length, renderDx.ptr);
|
|
153 OS.SetBkMode(gc.handle, oldBkMode);
|
|
154 }
|
|
155 /**
|
|
156 * Return ordering and rendering information for the given text. Wraps the GetFontLanguageInfo
|
|
157 * and GetCharacterPlacement functions.
|
|
158 * <p>
|
|
159 *
|
|
160 * @param gc the GC to use for measuring of this line, input parameter
|
|
161 * @param text text that bidi data should be calculated for, input parameter
|
|
162 * @param order an array of integers representing the visual position of each character in
|
|
163 * the text array, output parameter
|
|
164 * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW,
|
|
165 * LOCALNUMBER) of each character in the text array, input/output parameter
|
|
166 * @param dx an array of integers representing the pixel width of each glyph in the returned
|
|
167 * glyph buffer, output parameter
|
|
168 * @param flags an integer representing rendering flag information, input parameter
|
|
169 * @param offsets text segments that should be measured and reordered separately, input
|
|
170 * parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details.
|
|
171 * @return buffer with the glyphs that should be rendered for the given text
|
|
172 */
|
|
173 public static char[] getRenderInfo(GC gc, char[] text, int[] order, byte[] classBuffer, int[] dx, int flags, int [] offsets) {
|
|
174 auto fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
|
|
175 auto hHeap = OS.GetProcessHeap();
|
|
176 int[8] lpCs;
|
|
177 int cs = OS.GetTextCharset(gc.handle);
|
|
178 bool isRightOriented = false;
|
|
179 if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
|
|
180 isRightOriented = OS.GetLayout(gc.handle) !is 0;
|
|
181 }
|
|
182 OS.TranslateCharsetInfo( cast(uint*)cs, cast(CHARSETINFO*)lpCs.ptr, OS.TCI_SRCCHARSET);
|
|
183 TCHAR[] textBuffer = StrToTCHARs(lpCs[1], text, false);
|
|
184 int byteCount = textBuffer.length;
|
|
185 bool linkBefore = (flags & LINKBEFORE) is LINKBEFORE;
|
|
186 bool linkAfter = (flags & LINKAFTER) is LINKAFTER;
|
|
187
|
|
188 GCP_RESULTS result;
|
|
189 result.lStructSize = GCP_RESULTS.sizeof;
|
|
190 result.nGlyphs = byteCount;
|
|
191 auto lpOrder = result.lpOrder = cast(uint*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
|
|
192 auto lpDx = result.lpDx = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
|
|
193 auto lpClass = result.lpClass = cast(char*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
|
|
194 auto lpGlyphs = result.lpGlyphs = cast(wchar*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 2);
|
|
195
|
|
196 // set required dwFlags
|
|
197 int dwFlags = 0;
|
|
198 int glyphFlags = 0;
|
|
199 // Always reorder. We assume that if we are calling this function we're
|
|
200 // on a platform that supports bidi. Fixes 20690.
|
|
201 dwFlags |= GCP_REORDER;
|
|
202 if ((fontLanguageInfo & GCP_LIGATE) is GCP_LIGATE) {
|
|
203 dwFlags |= GCP_LIGATE;
|
|
204 glyphFlags |= 0;
|
|
205 }
|
|
206 if ((fontLanguageInfo & GCP_GLYPHSHAPE) is GCP_GLYPHSHAPE) {
|
|
207 dwFlags |= GCP_GLYPHSHAPE;
|
|
208 if (linkBefore) {
|
|
209 glyphFlags |= GCPGLYPH_LINKBEFORE;
|
|
210 }
|
|
211 if (linkAfter) {
|
|
212 glyphFlags |= GCPGLYPH_LINKAFTER;
|
|
213 }
|
|
214 }
|
|
215 byte[] lpGlyphs2;
|
|
216 if (linkBefore || linkAfter) {
|
|
217 lpGlyphs2 = new byte[2];
|
|
218 lpGlyphs2[0]=cast(byte)glyphFlags;
|
|
219 lpGlyphs2[1]=cast(byte)(glyphFlags >> 8);
|
|
220 }
|
|
221 else {
|
|
222 lpGlyphs2 = [cast(byte) glyphFlags];
|
|
223 }
|
|
224 OS.MoveMemory(result.lpGlyphs, lpGlyphs2.ptr, lpGlyphs2.length);
|
|
225
|
|
226 if ((flags & CLASSIN) is CLASSIN) {
|
|
227 // set classification values for the substring
|
|
228 dwFlags |= GCP_CLASSIN;
|
|
229 OS.MoveMemory(result.lpClass, classBuffer.ptr, classBuffer.length);
|
|
230 }
|
|
231
|
|
232 wchar[] glyphBuffer = new wchar[result.nGlyphs];
|
|
233 int glyphCount = 0;
|
|
234 for (int i=0; i<offsets.length-1; i++) {
|
|
235 int offset = offsets [i];
|
|
236 int length_ = offsets [i+1] - offsets [i];
|
|
237
|
|
238 // The number of glyphs expected is <= length (segment length);
|
|
239 // the actual number returned may be less in case of Arabic ligatures.
|
|
240 result.nGlyphs = length_;
|
|
241 TCHAR[] textBuffer2 = StrToTCHARs(lpCs[1], text.substring(offset, offset + length_), false);
|
|
242 OS.GetCharacterPlacement(gc.handle, textBuffer2.ptr, textBuffer2.length, 0, &result, dwFlags);
|
|
243
|
|
244 if (dx !is null) {
|
|
245 int [] dx2 = new int [result.nGlyphs];
|
|
246 OS.MoveMemory(dx2.ptr, result.lpDx, dx2.length * 4);
|
|
247 if (isRightOriented) {
|
|
248 reverse(dx2);
|
|
249 }
|
|
250 System.arraycopy (dx2, 0, dx, glyphCount, dx2.length);
|
|
251 }
|
|
252 if (order !is null) {
|
|
253 int [] order2 = new int [length_];
|
|
254 OS.MoveMemory(order2.ptr, result.lpOrder, order2.length * 4);
|
|
255 translateOrder(order2, glyphCount, isRightOriented);
|
|
256 System.arraycopy (order2, 0, order, offset, length_);
|
|
257 }
|
|
258 if (classBuffer !is null) {
|
|
259 byte [] classBuffer2 = new byte [length_];
|
|
260 OS.MoveMemory(classBuffer2.ptr, result.lpClass, classBuffer2.length);
|
|
261 System.arraycopy (classBuffer2, 0, classBuffer, offset, length_);
|
|
262 }
|
|
263 wchar[] glyphBuffer2 = new wchar[result.nGlyphs];
|
|
264 OS.MoveMemory(glyphBuffer2.ptr, result.lpGlyphs, glyphBuffer2.length * 2);
|
|
265 if (isRightOriented) {
|
|
266 reverse(glyphBuffer2);
|
|
267 }
|
|
268 System.arraycopy (glyphBuffer2, 0, glyphBuffer, glyphCount, glyphBuffer2.length);
|
|
269 glyphCount += glyphBuffer2.length;
|
|
270
|
|
271 // We concatenate successive results of calls to GCP.
|
|
272 // For Arabic, it is the only good method since the number of output
|
|
273 // glyphs might be less than the number of input characters.
|
|
274 // This assumes that the whole line is built by successive adjacent
|
|
275 // segments without overlapping.
|
|
276 result.lpOrder += length_ * 4;
|
|
277 result.lpDx += length_ * 4;
|
|
278 result.lpClass += length_;
|
|
279 result.lpGlyphs += glyphBuffer2.length * 2;
|
|
280 }
|
|
281
|
|
282 /* Free the memory that was allocated. */
|
|
283 OS.HeapFree(hHeap, 0, lpGlyphs);
|
|
284 OS.HeapFree(hHeap, 0, lpClass);
|
|
285 OS.HeapFree(hHeap, 0, lpDx);
|
|
286 OS.HeapFree(hHeap, 0, lpOrder);
|
|
287 return WCHARsToStr(glyphBuffer);
|
|
288 }
|
|
289 /**
|
|
290 * Return bidi ordering information for the given text. Does not return rendering
|
|
291 * information (e.g., glyphs, glyph distances). Use this method when you only need
|
|
292 * ordering information. Doing so will improve performance. Wraps the
|
|
293 * GetFontLanguageInfo and GetCharacterPlacement functions.
|
|
294 * <p>
|
|
295 *
|
|
296 * @param gc the GC to use for measuring of this line, input parameter
|
|
297 * @param text text that bidi data should be calculated for, input parameter
|
|
298 * @param order an array of integers representing the visual position of each character in
|
|
299 * the text array, output parameter
|
|
300 * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW,
|
|
301 * LOCALNUMBER) of each character in the text array, input/output parameter
|
|
302 * @param flags an integer representing rendering flag information, input parameter
|
|
303 * @param offsets text segments that should be measured and reordered separately, input
|
|
304 * parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details.
|
|
305 */
|
|
306 public static void getOrderInfo(GC gc, char[] text, int[] order, byte[] classBuffer, int flags, int [] offsets) {
|
|
307 int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
|
|
308 auto hHeap = OS.GetProcessHeap();
|
|
309 int[8] lpCs;
|
|
310 int cs = OS.GetTextCharset(gc.handle);
|
|
311 OS.TranslateCharsetInfo( cast(uint*) cs, cast(CHARSETINFO*)lpCs.ptr, OS.TCI_SRCCHARSET);
|
|
312 TCHAR[] textBuffer = StrToTCHARs(lpCs[1], text, false);
|
|
313 int byteCount = textBuffer.length;
|
|
314 bool isRightOriented = false;
|
|
315 if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
|
|
316 isRightOriented = OS.GetLayout(gc.handle) !is 0;
|
|
317 }
|
|
318
|
|
319 GCP_RESULTS result;
|
|
320 result.lStructSize = GCP_RESULTS.sizeof;
|
|
321 result.nGlyphs = byteCount;
|
|
322 auto lpOrder = result.lpOrder = cast(uint*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
|
|
323 auto lpClass = result.lpClass = cast(char*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
|
|
324
|
|
325 // set required dwFlags, these values will affect how the text gets rendered and
|
|
326 // ordered
|
|
327 int dwFlags = 0;
|
|
328 // Always reorder. We assume that if we are calling this function we're
|
|
329 // on a platform that supports bidi. Fixes 20690.
|
|
330 dwFlags |= GCP_REORDER;
|
|
331 if ((fontLanguageInfo & GCP_LIGATE) is GCP_LIGATE) {
|
|
332 dwFlags |= GCP_LIGATE;
|
|
333 }
|
|
334 if ((fontLanguageInfo & GCP_GLYPHSHAPE) is GCP_GLYPHSHAPE) {
|
|
335 dwFlags |= GCP_GLYPHSHAPE;
|
|
336 }
|
|
337 if ((flags & CLASSIN) is CLASSIN) {
|
|
338 // set classification values for the substring, classification values
|
|
339 // can be specified on input
|
|
340 dwFlags |= GCP_CLASSIN;
|
|
341 OS.MoveMemory(result.lpClass, classBuffer.ptr, classBuffer.length);
|
|
342 }
|
|
343
|
|
344 int glyphCount = 0;
|
|
345 for (int i=0; i<offsets.length-1; i++) {
|
|
346 int offset = offsets [i];
|
|
347 int length_ = offsets [i+1] - offsets [i];
|
|
348 // The number of glyphs expected is <= length (segment length);
|
|
349 // the actual number returned may be less in case of Arabic ligatures.
|
|
350 result.nGlyphs = length_;
|
|
351 TCHAR[] textBuffer2 = StrToTCHARs(lpCs[1], text.substring(offset, offset + length_), false);
|
|
352 OS.GetCharacterPlacement(gc.handle, textBuffer2.ptr, textBuffer2.length, 0, &result, dwFlags);
|
|
353
|
|
354 if (order !is null) {
|
|
355 int [] order2 = new int [length_];
|
|
356 OS.MoveMemory(order2.ptr, result.lpOrder, order2.length * 4);
|
|
357 translateOrder(order2, glyphCount, isRightOriented);
|
|
358 System.arraycopy (order2, 0, order, offset, length_);
|
|
359 }
|
|
360 if (classBuffer !is null) {
|
|
361 byte [] classBuffer2 = new byte [length_];
|
|
362 OS.MoveMemory(classBuffer2.ptr, result.lpClass, classBuffer2.length);
|
|
363 System.arraycopy (classBuffer2, 0, classBuffer, offset, length_);
|
|
364 }
|
|
365 glyphCount += result.nGlyphs;
|
|
366
|
|
367 // We concatenate successive results of calls to GCP.
|
|
368 // For Arabic, it is the only good method since the number of output
|
|
369 // glyphs might be less than the number of input characters.
|
|
370 // This assumes that the whole line is built by successive adjacent
|
|
371 // segments without overlapping.
|
|
372 result.lpOrder += length_ * 4;
|
|
373 result.lpClass += length_;
|
|
374 }
|
|
375
|
|
376 /* Free the memory that was allocated. */
|
|
377 OS.HeapFree(hHeap, 0, lpClass);
|
|
378 OS.HeapFree(hHeap, 0, lpOrder);
|
|
379 }
|
|
380 /**
|
|
381 * Return bidi attribute information for the font in the specified gc.
|
|
382 * <p>
|
|
383 *
|
|
384 * @param gc the gc to query
|
|
385 * @return bitwise OR of the REORDER, LIGATE and GLYPHSHAPE flags
|
|
386 * defined by this class.
|
|
387 */
|
|
388 public static int getFontBidiAttributes(GC gc) {
|
|
389 int fontStyle = 0;
|
|
390 int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
|
|
391 if (((fontLanguageInfo & GCP_REORDER) !is 0)) {
|
|
392 fontStyle |= REORDER;
|
|
393 }
|
|
394 if (((fontLanguageInfo & GCP_LIGATE) !is 0)) {
|
|
395 fontStyle |= LIGATE;
|
|
396 }
|
|
397 if (((fontLanguageInfo & GCP_GLYPHSHAPE) !is 0)) {
|
|
398 fontStyle |= GLYPHSHAPE;
|
|
399 }
|
|
400 return fontStyle;
|
|
401 }
|
|
402 /**
|
|
403 * Return the active keyboard language type.
|
|
404 * <p>
|
|
405 *
|
|
406 * @return an integer representing the active keyboard language (KEYBOARD_BIDI,
|
|
407 * KEYBOARD_NON_BIDI)
|
|
408 */
|
|
409 public static int getKeyboardLanguage() {
|
|
410 int layout = cast(int) OS.GetKeyboardLayout(0);
|
|
411 // only interested in low 2 bytes, which is the primary
|
|
412 // language identifier
|
|
413 layout = layout & 0x000000FF;
|
|
414 if (layout is LANG_HEBREW) return KEYBOARD_BIDI;
|
|
415 if (layout is LANG_ARABIC) return KEYBOARD_BIDI;
|
|
416 // return non-bidi for all other languages
|
|
417 return KEYBOARD_NON_BIDI;
|
|
418 }
|
|
419 /**
|
|
420 * Return the languages that are installed for the keyboard.
|
|
421 * <p>
|
|
422 *
|
|
423 * @return integer array with an entry for each installed language
|
|
424 */
|
|
425 static void*[] getKeyboardLanguageList() {
|
|
426 int maxSize = 10;
|
|
427 void*[] tempList = new void*[maxSize];
|
|
428 int size = OS.GetKeyboardLayoutList(maxSize, tempList.ptr);
|
|
429 void*[] list = new void*[size];
|
|
430 System.arraycopy(tempList, 0, list, 0, size);
|
|
431 return list;
|
|
432 }
|
|
433 /**
|
|
434 * Return whether or not the platform supports a bidi language. Determine this
|
|
435 * by looking at the languages that are installed.
|
|
436 * <p>
|
|
437 *
|
|
438 * @return true if bidi is supported, false otherwise. Always
|
|
439 * false on Windows CE.
|
|
440 */
|
|
441 public static bool isBidiPlatform() {
|
|
442 if (OS.IsWinCE) return false;
|
|
443 if (isBidiPlatform_ !is -1) return isBidiPlatform_ is 1; // already set
|
|
444
|
|
445 isBidiPlatform_ = 0;
|
|
446
|
|
447 // The following test is a workaround for bug report 27629. On WinXP,
|
|
448 // both bidi and complex script (e.g., Thai) languages must be installed
|
|
449 // at the same time. Since the bidi platform calls do not support
|
|
450 // double byte characters, there is no way to run Eclipse using the
|
|
451 // complex script languages on XP, so constrain this test to answer true
|
|
452 // only if a bidi input language is defined. Doing so will allow complex
|
|
453 // script languages to work (e.g., one can install bidi and complex script
|
|
454 // languages, but only install the Thai keyboard).
|
|
455 if (!isKeyboardBidi()) return false;
|
|
456
|
|
457 //Callback callback = null;
|
|
458 //try {
|
|
459 //callback = new Callback (Class.forName (CLASS_NAME), "EnumSystemLanguageGroupsProc", 5); //$NON-NLS-1$
|
|
460 //int lpEnumSystemLanguageGroupsProc = callback.getAddress ();
|
|
461 //if (lpEnumSystemLanguageGroupsProc is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
|
|
462 OS.EnumSystemLanguageGroups(&EnumSystemLanguageGroupsProc, OS.LGRPID_INSTALLED, 0);
|
|
463 //callback.dispose ();
|
|
464 //} catch (ClassNotFoundException e) {
|
|
465 //if (callback !is null) callback.dispose();
|
|
466 //}
|
|
467 if (isBidiPlatform_ is 1) return true;
|
|
468 // need to look at system code page for NT & 98 platforms since EnumSystemLanguageGroups is
|
|
469 // not supported for these platforms
|
|
470 char[] codePage = to!(char[])(OS.GetACP());
|
|
471 if (CD_PG_ARABIC==/*eq*/codePage || CD_PG_HEBREW==/*eq*/codePage) {
|
|
472 isBidiPlatform_ = 1;
|
|
473 }
|
|
474 return isBidiPlatform_ is 1;
|
|
475 }
|
|
476 /**
|
|
477 * Return whether or not the keyboard supports input of a bidi language. Determine this
|
|
478 * by looking at the languages that are installed for the keyboard.
|
|
479 * <p>
|
|
480 *
|
|
481 * @return true if bidi is supported, false otherwise.
|
|
482 */
|
|
483 public static bool isKeyboardBidi() {
|
|
484 void*[] list = getKeyboardLanguageList();
|
|
485 for (int i=0; i<list.length; i++) {
|
|
486 int id = cast(int)list[i] & 0x000000FF;
|
|
487 if ((id is LANG_ARABIC) || (id is LANG_HEBREW)) {
|
|
488 return true;
|
|
489 }
|
|
490 }
|
|
491 return false;
|
|
492 }
|
|
493 /**
|
|
494 * Removes the specified language listener.
|
|
495 * <p>
|
|
496 *
|
|
497 * @param hwnd the handle of the Control that is listening for keyboard language changes
|
|
498 */
|
|
499 public static void removeLanguageListener (HWND hwnd) {
|
|
500 languageMap.remove(hwnd);
|
|
501 unsubclass(hwnd);
|
|
502 }
|
|
503 /**
|
|
504 * Switch the keyboard language to the specified language type. We do
|
|
505 * not distinguish between multiple bidi or multiple non-bidi languages, so
|
|
506 * set the keyboard to the first language of the given type.
|
|
507 * <p>
|
|
508 *
|
|
509 * @param language integer representing language. One of
|
|
510 * KEYBOARD_BIDI, KEYBOARD_NON_BIDI.
|
|
511 */
|
|
512 public static void setKeyboardLanguage(int language) {
|
|
513 // don't switch the keyboard if it doesn't need to be
|
|
514 if (language is getKeyboardLanguage()) return;
|
|
515
|
|
516 if (language is KEYBOARD_BIDI) {
|
|
517 // get the list of active languages
|
|
518 void*[] list = getKeyboardLanguageList();
|
|
519 // set to first bidi language
|
|
520 for (int i=0; i<list.length; i++) {
|
|
521 int id = cast(int)list[i] & 0x000000FF;
|
|
522 if ((id is LANG_ARABIC) || (id is LANG_HEBREW)) {
|
|
523 OS.ActivateKeyboardLayout(list[i], 0);
|
|
524 return;
|
|
525 }
|
|
526 }
|
|
527 } else {
|
|
528 // get the list of active languages
|
|
529 void*[] list = getKeyboardLanguageList();
|
|
530 // set to the first non-bidi language (anything not
|
|
531 // Hebrew or Arabic)
|
|
532 for (int i=0; i<list.length; i++) {
|
|
533 int id = cast(int)list[i] & 0x000000FF;
|
|
534 if ((id !is LANG_HEBREW) && (id !is LANG_ARABIC)) {
|
|
535 OS.ActivateKeyboardLayout(list[i], 0);
|
|
536 return;
|
|
537 }
|
|
538 }
|
|
539 }
|
|
540 }
|
|
541 /**
|
|
542 * Sets the orientation (writing order) of the specified control. Text will
|
|
543 * be right aligned for right to left writing order.
|
|
544 * <p>
|
|
545 *
|
|
546 * @param hwnd the handle of the Control to change the orientation of
|
|
547 * @param orientation one of DWT.RIGHT_TO_LEFT or DWT.LEFT_TO_RIGHT
|
|
548 * @return true if the orientation was changed, false if the orientation
|
|
549 * could not be changed
|
|
550 */
|
|
551 public static bool setOrientation (HWND hwnd, int orientation) {
|
|
552 if (OS.IsWinCE) return false;
|
|
553 if (OS.WIN32_VERSION < OS.VERSION(4, 10)) return false;
|
|
554 int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
|
|
555 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) {
|
|
556 bits |= OS.WS_EX_LAYOUTRTL;
|
|
557 } else {
|
|
558 bits &= ~OS.WS_EX_LAYOUTRTL;
|
|
559 }
|
|
560 OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits);
|
|
561 return true;
|
|
562 }
|
|
563 /**
|
|
564 * Override the window proc.
|
|
565 *
|
|
566 * @param hwnd control to override the window proc of
|
|
567 */
|
|
568 static void subclass(HWND hwnd) {
|
|
569 HWND key = hwnd;
|
|
570 if ( ( key in oldProcMap ) is null) {
|
|
571 int oldProc = OS.GetWindowLong(hwnd, OS.GWL_WNDPROC);
|
|
572 oldProcMap[key] = cast(WNDPROC)oldProc;
|
|
573 WNDPROC t = &windowProc; // test signature
|
|
574 OS.SetWindowLong(hwnd, OS.GWL_WNDPROC, cast(int) &windowProc);
|
|
575 }
|
|
576 }
|
|
577 /**
|
|
578 * Reverse the character array. Used for right orientation.
|
|
579 *
|
|
580 * @param charArray character array to reverse
|
|
581 */
|
|
582 static void reverse(wchar[] charArray) {
|
|
583 int length_ = charArray.length;
|
|
584 for (int i = 0; i <= (length_ - 1) / 2; i++) {
|
|
585 wchar tmp = charArray[i];
|
|
586 charArray[i] = charArray[length_ - 1 - i];
|
|
587 charArray[length_ - 1 - i] = tmp;
|
|
588 }
|
|
589 }
|
|
590 /**
|
|
591 * Reverse the integer array. Used for right orientation.
|
|
592 *
|
|
593 * @param intArray integer array to reverse
|
|
594 */
|
|
595 static void reverse(int[] intArray) {
|
|
596 int length_ = intArray.length;
|
|
597 for (int i = 0; i <= (length_ - 1) / 2; i++) {
|
|
598 int tmp = intArray[i];
|
|
599 intArray[i] = intArray[length_ - 1 - i];
|
|
600 intArray[length_ - 1 - i] = tmp;
|
|
601 }
|
|
602 }
|
|
603 /**
|
|
604 * Adjust the order array so that it is relative to the start of the line. Also reverse the order array if the orientation
|
|
605 * is to the right.
|
|
606 *
|
|
607 * @param orderArray integer array of order values to translate
|
|
608 * @param glyphCount number of glyphs that have been processed for the current line
|
|
609 * @param isRightOriented flag indicating whether or not current orientation is to the right
|
|
610 */
|
|
611 static void translateOrder(int[] orderArray, int glyphCount, bool isRightOriented) {
|
|
612 int maxOrder = 0;
|
|
613 int length_ = orderArray.length;
|
|
614 if (isRightOriented) {
|
|
615 for (int i=0; i<length_; i++) {
|
|
616 maxOrder = Math.max(maxOrder, orderArray[i]);
|
|
617 }
|
|
618 }
|
|
619 for (int i=0; i<length_; i++) {
|
|
620 if (isRightOriented) orderArray[i] = maxOrder - orderArray[i];
|
|
621 orderArray [i] += glyphCount;
|
|
622 }
|
|
623 }
|
|
624 /**
|
|
625 * Remove the overridden the window proc.
|
|
626 *
|
|
627 * @param hwnd control to remove the window proc override for
|
|
628 */
|
|
629 static void unsubclass(HWND hwnd) {
|
|
630 HWND key = hwnd;
|
|
631 if (( key in languageMap ) is null && ( key in keyMap ) is null) {
|
|
632 WNDPROC proc;
|
|
633 if( auto p = key in oldProcMap ){
|
|
634 proc = *p;
|
|
635 oldProcMap.remove( key );
|
|
636 }
|
|
637 if (proc is null) return;
|
|
638 OS.SetWindowLong(hwnd, OS.GWL_WNDPROC, cast(int)proc );
|
|
639 }
|
|
640 }
|
|
641 /**
|
|
642 * Window proc to intercept keyboard language switch event (WS_INPUTLANGCHANGE)
|
|
643 * and widget orientation changes.
|
|
644 * Run the Control's registered runnable when the keyboard language is switched.
|
|
645 *
|
|
646 * @param hwnd handle of the control that is listening for the keyboard language
|
|
647 * change event
|
|
648 * @param msg window message
|
|
649 */
|
|
650 static extern(Windows) int windowProc (HWND hwnd, uint msg, uint wParam, int lParam) {
|
|
651 HWND key = hwnd;
|
|
652 switch (msg) {
|
|
653 case 0x51 /*OS.WM_INPUTLANGCHANGE*/:
|
|
654 Runnable runnable = languageMap[key];
|
|
655 if (runnable !is null) runnable.run ();
|
|
656 break;
|
155
|
657 default:
|
154
|
658 }
|
|
659 auto oldProc = oldProcMap[key];
|
|
660 return OS.CallWindowProc ( oldProc, hwnd, msg, wParam, lParam);
|
|
661 }
|
|
662
|
|
663 }
|