root/dwt/internal/BidiUtil.d

Revision 237:e2affbeb686d, 25.7 kB (checked in by Frank Benoit <benoit@tionex.de>, 6 months ago)

Making tango.sys.win32.Types and dwt.internal.win32.WINTYPES to match common declaration. Make ansi charactars of type ubyte.

Line 
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 dwt.widgets.Control;
21 import tango.util.Convert;
22 import dwt.dwthelper.utils;
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 String 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 String CD_PG_HEBREW = "1255"; //$NON-NLS-1$
81     static const String 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 public static void addLanguageListener (Control control, Runnable runnable) {
117     addLanguageListener(control.handle, runnable);
118 }
119 /**
120  * Proc used for OS.EnumSystemLanguageGroups call during isBidiPlatform test.
121  */
122 static extern(Windows) int EnumSystemLanguageGroupsProc(uint lpLangGrpId, wchar* lpLangGrpIdString, wchar* lpLangGrpName, uint options, int lParam) {
123     if (lpLangGrpId is OS.LGRPID_HEBREW) {
124         isBidiPlatform_ = 1;
125         return 0;
126     }
127     if (lpLangGrpId is OS.LGRPID_ARABIC) {
128         isBidiPlatform_ = 1;
129         return 0;
130     }
131     return 1;
132 }
133 /**
134  * Wraps the ExtTextOut function.
135  * <p>
136  *
137  * @param gc the gc to use for rendering
138  * @param renderBuffer the glyphs to render as an array of characters
139  * @param renderDx the width of each glyph in renderBuffer
140  * @param x x position to start rendering
141  * @param y y position to start rendering
142  */
143 public static void drawGlyphs(GC gc, wchar[] renderBuffer, int[] renderDx, int x, int y) {
144     int length_ = renderDx.length;
145
146     if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
147         if (OS.GetLayout (gc.handle) !is 0) {
148             reverse(renderDx);
149             renderDx[length_-1]--;               //fixes bug 40006
150             reverse(renderBuffer);
151         }
152     }
153     // render transparently to avoid overlapping segments. fixes bug 40006
154     int oldBkMode = OS.SetBkMode(gc.handle, OS.TRANSPARENT);
155     OS.ExtTextOutW(gc.handle, x, y, ETO_GLYPH_INDEX , null, renderBuffer.ptr, renderBuffer.length, renderDx.ptr);
156     OS.SetBkMode(gc.handle, oldBkMode);
157 }
158 /**
159  * Return ordering and rendering information for the given text.  Wraps the GetFontLanguageInfo
160  * and GetCharacterPlacement functions.
161  * <p>
162  *
163  * @param gc the GC to use for measuring of this line, input parameter
164  * @param text text that bidi data should be calculated for, input parameter
165  * @param order an array of integers representing the visual position of each character in
166  *  the text array, output parameter
167  * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW,
168  *  LOCALNUMBER) of each character in the text array, input/output parameter
169  * @param dx an array of integers representing the pixel width of each glyph in the returned
170  *  glyph buffer, output parameter
171  * @param flags an integer representing rendering flag information, input parameter
172  * @param offsets text segments that should be measured and reordered separately, input
173  *  parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details.
174  * @return buffer with the glyphs that should be rendered for the given text
175  */
176 public static char[] getRenderInfo(GC gc, String text, int[] order, byte[] classBuffer, int[] dx, int flags, int [] offsets) {
177     auto fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
178     auto hHeap = OS.GetProcessHeap();
179     int[8] lpCs;
180     int cs = OS.GetTextCharset(gc.handle);
181     bool isRightOriented = false;
182     if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
183         isRightOriented = OS.GetLayout(gc.handle) !is 0;
184     }
185     OS.TranslateCharsetInfo( cast(uint*)cs, cast(CHARSETINFO*)lpCs.ptr, OS.TCI_SRCCHARSET);
186     TCHAR[] textBuffer = StrToTCHARs(lpCs[1], text, false);
187     int byteCount = textBuffer.length;
188     bool linkBefore = (flags & LINKBEFORE) is LINKBEFORE;
189     bool linkAfter = (flags & LINKAFTER) is LINKAFTER;
190
191     GCP_RESULTS result;
192     result.lStructSize = GCP_RESULTS.sizeof;
193     result.nGlyphs = byteCount;
194     auto lpOrder = result.lpOrder = cast(uint*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
195     auto lpDx = result.lpDx = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
196     auto lpClass = result.lpClass = cast(CHAR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
197     auto lpGlyphs = result.lpGlyphs = cast(wchar*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 2);
198
199     // set required dwFlags
200     int dwFlags = 0;
201     int glyphFlags = 0;
202     // Always reorder.  We assume that if we are calling this function we're
203     // on a platform that supports bidi.  Fixes 20690.
204     dwFlags |= GCP_REORDER;
205     if ((fontLanguageInfo & GCP_LIGATE) is GCP_LIGATE) {
206         dwFlags |= GCP_LIGATE;
207         glyphFlags |= 0;
208     }
209     if ((fontLanguageInfo & GCP_GLYPHSHAPE) is GCP_GLYPHSHAPE) {
210         dwFlags |= GCP_GLYPHSHAPE;
211         if (linkBefore) {
212             glyphFlags |= GCPGLYPH_LINKBEFORE;
213         }
214         if (linkAfter) {
215             glyphFlags |= GCPGLYPH_LINKAFTER;
216         }
217     }
218     byte[] lpGlyphs2;
219     if (linkBefore || linkAfter) {
220         lpGlyphs2 = new byte[2];
221         lpGlyphs2[0]=cast(byte)glyphFlags;
222         lpGlyphs2[1]=cast(byte)(glyphFlags >> 8);
223     }
224     else {
225         lpGlyphs2 = [cast(byte) glyphFlags];
226     }
227     OS.MoveMemory(result.lpGlyphs, lpGlyphs2.ptr, lpGlyphs2.length);
228
229     if ((flags & CLASSIN) is CLASSIN) {
230         // set classification values for the substring
231         dwFlags |= GCP_CLASSIN;
232         OS.MoveMemory(result.lpClass, classBuffer.ptr, classBuffer.length);
233     }
234
235     wchar[] glyphBuffer = new wchar[result.nGlyphs];
236     int glyphCount = 0;
237     for (int i=0; i<offsets.length-1; i++) {
238         int offset = offsets [i];
239         int length_ = offsets [i+1] - offsets [i];
240
241         // The number of glyphs expected is <= length (segment length);
242         // the actual number returned may be less in case of Arabic ligatures.
243         result.nGlyphs = length_;
244         TCHAR[] textBuffer2 = StrToTCHARs(lpCs[1], text.substring(offset, offset + length_), false);
245         OS.GetCharacterPlacement(gc.handle, textBuffer2.ptr, textBuffer2.length, 0, &result, dwFlags);
246
247         if (dx !is null) {
248             int [] dx2 = new int [result.nGlyphs];
249             OS.MoveMemory(dx2.ptr, result.lpDx, dx2.length * 4);
250             if (isRightOriented) {
251                 reverse(dx2);
252             }
253             System.arraycopy (dx2, 0, dx, glyphCount, dx2.length);
254         }
255         if (order !is null) {
256             int [] order2 = new int [length_];
257             OS.MoveMemory(order2.ptr, result.lpOrder, order2.length * 4);
258             translateOrder(order2, glyphCount, isRightOriented);
259             System.arraycopy (order2, 0, order, offset, length_);
260         }
261         if (classBuffer !is null) {
262             byte [] classBuffer2 = new byte [length_];
263             OS.MoveMemory(classBuffer2.ptr, result.lpClass, classBuffer2.length);
264             System.arraycopy (classBuffer2, 0, classBuffer, offset, length_);
265         }
266         wchar[] glyphBuffer2 = new wchar[result.nGlyphs];
267         OS.MoveMemory(glyphBuffer2.ptr, result.lpGlyphs, glyphBuffer2.length * 2);
268         if (isRightOriented) {
269             reverse(glyphBuffer2);
270         }
271         System.arraycopy (glyphBuffer2, 0, glyphBuffer, glyphCount, glyphBuffer2.length);
272         glyphCount += glyphBuffer2.length;
273
274         // We concatenate successive results of calls to GCP.
275         // For Arabic, it is the only good method since the number of output
276         // glyphs might be less than the number of input characters.
277         // This assumes that the whole line is built by successive adjacent
278         // segments without overlapping.
279         result.lpOrder += length_ * 4;
280         result.lpDx += length_ * 4;
281         result.lpClass += length_;
282         result.lpGlyphs += glyphBuffer2.length * 2;
283     }
284
285     /* Free the memory that was allocated. */
286     OS.HeapFree(hHeap, 0, lpGlyphs);
287     OS.HeapFree(hHeap, 0, lpClass);
288     OS.HeapFree(hHeap, 0, lpDx);
289     OS.HeapFree(hHeap, 0, lpOrder);
290     return WCHARsToStr(glyphBuffer);
291 }
292 /**
293  * Return bidi ordering information for the given text.  Does not return rendering
294  * information (e.g., glyphs, glyph distances).  Use this method when you only need
295  * ordering information.  Doing so will improve performance.  Wraps the
296  * GetFontLanguageInfo and GetCharacterPlacement functions.
297  * <p>
298  *
299  * @param gc the GC to use for measuring of this line, input parameter
300  * @param text text that bidi data should be calculated for, input parameter
301  * @param order an array of integers representing the visual position of each character in
302  *  the text array, output parameter
303  * @param classBuffer an array of integers representing the type (e.g., ARABIC, HEBREW,
304  *  LOCALNUMBER) of each character in the text array, input/output parameter
305  * @param flags an integer representing rendering flag information, input parameter
306  * @param offsets text segments that should be measured and reordered separately, input
307  *  parameter. See org.eclipse.swt.custom.BidiSegmentEvent for details.
308  */
309 public static void getOrderInfo(GC gc, String text, int[] order, byte[] classBuffer, int flags, int [] offsets) {
310     int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
311     auto hHeap = OS.GetProcessHeap();
312     int[8] lpCs;
313     int cs = OS.GetTextCharset(gc.handle);
314     OS.TranslateCharsetInfo( cast(uint*) cs, cast(CHARSETINFO*)lpCs.ptr, OS.TCI_SRCCHARSET);
315     TCHAR[] textBuffer = StrToTCHARs(lpCs[1], text, false);
316     int byteCount = textBuffer.length;
317     bool isRightOriented = false;
318     if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) {
319         isRightOriented = OS.GetLayout(gc.handle) !is 0;
320     }
321
322     GCP_RESULTS result;
323     result.lStructSize = GCP_RESULTS.sizeof;
324     result.nGlyphs = byteCount;
325     auto lpOrder = result.lpOrder = cast(uint*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount * 4);
326     auto lpClass = result.lpClass = cast(CHAR*) OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
327
328     // set required dwFlags, these values will affect how the text gets rendered and
329     // ordered
330     int dwFlags = 0;
331     // Always reorder.  We assume that if we are calling this function we're
332     // on a platform that supports bidi.  Fixes 20690.
333     dwFlags |= GCP_REORDER;
334     if ((fontLanguageInfo & GCP_LIGATE) is GCP_LIGATE) {
335         dwFlags |= GCP_LIGATE;
336     }
337     if ((fontLanguageInfo & GCP_GLYPHSHAPE) is GCP_GLYPHSHAPE) {
338         dwFlags |= GCP_GLYPHSHAPE;
339     }
340     if ((flags & CLASSIN) is CLASSIN) {
341         // set classification values for the substring, classification values
342         // can be specified on input
343         dwFlags |= GCP_CLASSIN;
344         OS.MoveMemory(result.lpClass, classBuffer.ptr, classBuffer.length);
345     }
346
347     int glyphCount = 0;
348     for (int i=0; i<offsets.length-1; i++) {
349         int offset = offsets [i];
350         int length_ = offsets [i+1] - offsets [i];
351         // The number of glyphs expected is <= length (segment length);
352         // the actual number returned may be less in case of Arabic ligatures.
353         result.nGlyphs = length_;
354         TCHAR[] textBuffer2 = StrToTCHARs(lpCs[1], text.substring(offset, offset + length_), false);
355         OS.GetCharacterPlacement(gc.handle, textBuffer2.ptr, textBuffer2.length, 0, &result, dwFlags);
356
357         if (order !is null) {
358             int [] order2 = new int [length_];
359             OS.MoveMemory(order2.ptr, result.lpOrder, order2.length * 4);
360             translateOrder(order2, glyphCount, isRightOriented);
361             System.arraycopy (order2, 0, order, offset, length_);
362         }
363         if (classBuffer !is null) {
364             byte [] classBuffer2 = new byte [length_];
365             OS.MoveMemory(classBuffer2.ptr, result.lpClass, classBuffer2.length);
366             System.arraycopy (classBuffer2, 0, classBuffer, offset, length_);
367         }
368         glyphCount += result.nGlyphs;
369
370         // We concatenate successive results of calls to GCP.
371         // For Arabic, it is the only good method since the number of output
372         // glyphs might be less than the number of input characters.
373         // This assumes that the whole line is built by successive adjacent
374         // segments without overlapping.
375         result.lpOrder += length_ * 4;
376         result.lpClass += length_;
377     }
378
379     /* Free the memory that was allocated. */
380     OS.HeapFree(hHeap, 0, lpClass);
381     OS.HeapFree(hHeap, 0, lpOrder);
382 }
383 /**
384  * Return bidi attribute information for the font in the specified gc.
385  * <p>
386  *
387  * @param gc the gc to query
388  * @return bitwise OR of the REORDER, LIGATE and GLYPHSHAPE flags
389  *  defined by this class.
390  */
391 public static int getFontBidiAttributes(GC gc) {
392     int fontStyle = 0;
393     int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
394     if (((fontLanguageInfo & GCP_REORDER) !is 0)) {
395         fontStyle |= REORDER;
396     }
397     if (((fontLanguageInfo & GCP_LIGATE) !is 0)) {
398         fontStyle |= LIGATE;
399     }
400     if (((fontLanguageInfo & GCP_GLYPHSHAPE) !is 0)) {
401         fontStyle |= GLYPHSHAPE;
402     }
403     return fontStyle;
404 }
405 /**
406  * Return the active keyboard language type.
407  * <p>
408  *
409  * @return an integer representing the active keyboard language (KEYBOARD_BIDI,
410  *  KEYBOARD_NON_BIDI)
411  */
412 public static int getKeyboardLanguage() {
413     int layout = cast(int) OS.GetKeyboardLayout(0);
414     int langID = OS.PRIMARYLANGID(OS.LOWORD(layout));
415     if (langID is LANG_HEBREW) return KEYBOARD_BIDI;
416     if (langID is LANG_ARABIC) return KEYBOARD_BIDI;
417     // return non-bidi for all other languages
418     return KEYBOARD_NON_BIDI;
419 }
420 /**
421  * Return the languages that are installed for the keyboard.
422  * <p>
423  *
424  * @return integer array with an entry for each installed language
425  */
426 static void*[] getKeyboardLanguageList() {
427     int maxSize = 10;
428     void*[] tempList = new void*[maxSize];
429     int size = OS.GetKeyboardLayoutList(maxSize, tempList.ptr);
430     void*[] list = new void*[size];
431     System.arraycopy(tempList, 0, list, 0, size);
432     return list;
433 }
434 /**
435  * Return whether or not the platform supports a bidi language.  Determine this
436  * by looking at the languages that are installed.
437  * <p>
438  *
439  * @return true if bidi is supported, false otherwise. Always
440  *  false on Windows CE.
441  */
442 public static bool isBidiPlatform() {
443     if (OS.IsWinCE) return false;
444     if (isBidiPlatform_ !is -1) return isBidiPlatform_ is 1; // already set
445
446     isBidiPlatform_ = 0;
447
448     // The following test is a workaround for bug report 27629. On WinXP,
449     // both bidi and complex script (e.g., Thai) languages must be installed
450     // at the same time.  Since the bidi platform calls do not support
451     // double byte characters, there is no way to run Eclipse using the
452     // complex script languages on XP, so constrain this test to answer true
453     // only if a bidi input language is defined.  Doing so will allow complex
454     // script languages to work (e.g., one can install bidi and complex script
455     // languages, but only install the Thai keyboard).
456     if (!isKeyboardBidi()) return false;
457
458     //Callback callback = null;
459     //try {
460         //callback = new Callback (Class.forName (CLASS_NAME), "EnumSystemLanguageGroupsProc", 5); //$NON-NLS-1$
461         //int lpEnumSystemLanguageGroupsProc = callback.getAddress ();
462         //if (lpEnumSystemLanguageGroupsProc is 0) DWT.error(DWT.ERROR_NO_MORE_CALLBACKS);
463         OS.EnumSystemLanguageGroups(&EnumSystemLanguageGroupsProc, OS.LGRPID_INSTALLED, 0);
464         //callback.dispose ();
465     //} catch (ClassNotFoundException e) {
466         //if (callback !is null) callback.dispose();
467     //}
468     if (isBidiPlatform_ is 1) return true;
469     // need to look at system code page for NT & 98 platforms since EnumSystemLanguageGroups is
470     // not supported for these platforms
471     String codePage = to!(String)(OS.GetACP());
472     if (CD_PG_ARABIC==/*eq*/codePage || CD_PG_HEBREW==/*eq*/codePage) {
473         isBidiPlatform_ = 1;
474     }
475     return isBidiPlatform_ is 1;
476 }
477 /**
478  * Return whether or not the keyboard supports input of a bidi language.  Determine this
479  * by looking at the languages that are installed for the keyboard.
480  * <p>
481  *
482  * @return true if bidi is supported, false otherwise.
483  */
484 public static bool isKeyboardBidi() {
485     void*[] list = getKeyboardLanguageList();
486     for (int i=0; i<list.length; i++) {
487         int id = OS.PRIMARYLANGID(OS.LOWORD( cast(int) list[i]));
488         if ((id is LANG_ARABIC) || (id is LANG_HEBREW)) {
489             return true;
490         }
491     }
492     return false;
493 }
494 /**
495  * Removes the specified language listener.
496  * <p>
497  *
498  * @param hwnd the handle of the Control that is listening for keyboard language changes
499  */
500 public static void removeLanguageListener (HWND hwnd) {
501     languageMap.remove(hwnd);
502     unsubclass(hwnd);
503 }
504 public static void removeLanguageListener (Control control) {
505     removeLanguageListener(control.handle);
506 }
507 /**
508  * Switch the keyboard language to the specified language type.  We do
509  * not distinguish between multiple bidi or multiple non-bidi languages, so
510  * set the keyboard to the first language of the given type.
511  * <p>
512  *
513  * @param language integer representing language. One of
514  *  KEYBOARD_BIDI, KEYBOARD_NON_BIDI.
515  */
516 public static void setKeyboardLanguage(int language) {
517     // don't switch the keyboard if it doesn't need to be
518     if (language is getKeyboardLanguage()) return;
519
520     if (language is KEYBOARD_BIDI) {
521         // get the list of active languages
522         void*[] list = getKeyboardLanguageList();
523         // set to first bidi language
524         for (int i=0; i<list.length; i++) {
525             int id = OS.PRIMARYLANGID(OS.LOWORD( cast(int) list[i]));
526             if ((id is LANG_ARABIC) || (id is LANG_HEBREW)) {
527                 OS.ActivateKeyboardLayout(list[i], 0);
528                 return;
529             }
530         }
531     } else {
532         // get the list of active languages
533         void*[] list = getKeyboardLanguageList();
534         // set to the first non-bidi language (anything not
535         // Hebrew or Arabic)
536         for (int i=0; i<list.length; i++) {
537             int id = OS.PRIMARYLANGID(OS.LOWORD( cast(int) list[i]));
538             if ((id !is LANG_HEBREW) && (id !is LANG_ARABIC)) {
539                 OS.ActivateKeyboardLayout(list[i], 0);
540                 return;
541             }
542         }
543     }
544 }
545 /**
546  * Sets the orientation (writing order) of the specified control. Text will
547  * be right aligned for right to left writing order.
548  * <p>
549  *
550  * @param hwnd the handle of the Control to change the