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