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