1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.internal;
19 import org.eclipse.swt.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.internal.win32.*;
22 import org.eclipse.swt.widgets.*;
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.
28 public class BidiUtil {
30 // Keyboard language ids
31 public static final int KEYBOARD_NON_BIDI = 0;
32 public static final int KEYBOARD_BIDI = 1;
35 static int isBidiPlatform = -1;
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;
42 // variables used for providing a listener mechanism for keyboard language
44 static Map<LONG, Runnable> languageMap = new HashMap<> ();
45 static Map<LONG, LONG> oldProcMap = new HashMap<> ();
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.
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;
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) {}
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;
88 * Public character class constants are the same as Windows
90 * Saves conversion of class array in getRenderInfo to arbitrary
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;
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
108 * @param hwnd the handle of the Control that is listening for keyboard language
110 * @param runnable the code that should be executed when a keyboard language change
113 public static void addLanguageListener (long hwnd, Runnable runnable) {
114 languageMap.put(new LONG(hwnd), runnable);
117 public static void addLanguageListener (Control control, Runnable runnable) {
118 addLanguageListener(control.handle, runnable);
121 * Proc used for OS.EnumSystemLanguageGroups call during isBidiPlatform test.
123 static long EnumSystemLanguageGroupsProc(long lpLangGrpId, long lpLangGrpIdString, long lpLangGrpName, long options, long lParam) {
124 if ((int)lpLangGrpId == OS.LGRPID_HEBREW) {
128 if ((int)lpLangGrpId == OS.LGRPID_ARABIC) {
135 * Wraps the ExtTextOut function.
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
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) {
148 renderDx[length-1]--; //fixes bug 40006
149 reverse(renderBuffer);
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);
157 * Return ordering and rendering information for the given text. Wraps the GetFontLanguageInfo
158 * and GetCharacterPlacement functions.
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
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;
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);
191 // set required dwFlags
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;
201 if ((fontLanguageInfo & GCP_GLYPHSHAPE) == GCP_GLYPHSHAPE) {
202 dwFlags |= GCP_GLYPHSHAPE;
204 glyphFlags |= GCPGLYPH_LINKBEFORE;
207 glyphFlags |= GCPGLYPH_LINKAFTER;
211 if (linkBefore || linkAfter) {
212 lpGlyphs2 = new byte[2];
213 lpGlyphs2[0]=(byte)glyphFlags;
214 lpGlyphs2[1]=(byte)(glyphFlags >> 8);
217 lpGlyphs2 = new byte[] {(byte) glyphFlags};
219 OS.MoveMemory(result.lpGlyphs, lpGlyphs2, lpGlyphs2.length);
221 if ((flags & CLASSIN) == CLASSIN) {
222 // set classification values for the substring
223 dwFlags |= GCP_CLASSIN;
224 OS.MoveMemory(result.lpClass, classBuffer, classBuffer.length);
227 char[] glyphBuffer = new char[result.nGlyphs];
229 for (int i=0; i<offsets.length-1; i++) {
230 int offset = offsets [i];
231 int length = offsets [i+1] - offsets [i];
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);
240 int [] dx2 = new int [result.nGlyphs];
241 OS.MoveMemory(dx2, result.lpDx, dx2.length * 4);
242 if (isRightOriented) {
245 System.arraycopy (dx2, 0, dx, glyphCount, dx2.length);
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);
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);
258 char[] glyphBuffer2 = new char[result.nGlyphs];
259 OS.MoveMemory(glyphBuffer2, result.lpGlyphs, glyphBuffer2.length * 2);
260 if (isRightOriented) {
261 reverse(glyphBuffer2);
263 System.arraycopy (glyphBuffer2, 0, glyphBuffer, glyphCount, glyphBuffer2.length);
264 glyphCount += glyphBuffer2.length;
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;
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);
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.
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.
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;
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);
314 // set required dwFlags, these values will affect how the text gets rendered and
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;
323 if ((fontLanguageInfo & GCP_GLYPHSHAPE) == GCP_GLYPHSHAPE) {
324 dwFlags |= GCP_GLYPHSHAPE;
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);
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);
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);
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);
354 glyphCount += result.nGlyphs;
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;
365 /* Free the memory that was allocated. */
366 OS.HeapFree(hHeap, 0, lpClass);
367 OS.HeapFree(hHeap, 0, lpOrder);
370 * Return bidi attribute information for the font in the specified gc.
373 * @param gc the gc to query
374 * @return bitwise OR of the REORDER, LIGATE and GLYPHSHAPE flags
375 * defined by this class.
377 public static int getFontBidiAttributes(GC gc) {
379 int fontLanguageInfo = OS.GetFontLanguageInfo(gc.handle);
380 if (((fontLanguageInfo & GCP_REORDER) != 0)) {
381 fontStyle |= REORDER;
383 if (((fontLanguageInfo & GCP_LIGATE) != 0)) {
386 if (((fontLanguageInfo & GCP_GLYPHSHAPE) != 0)) {
387 fontStyle |= GLYPHSHAPE;
392 * Return the active keyboard language type.
395 * @return an integer representing the active keyboard language (KEYBOARD_BIDI,
398 public static int getKeyboardLanguage() {
399 long layout = OS.GetKeyboardLayout(0);
400 return isBidiLang(layout) ? KEYBOARD_BIDI : KEYBOARD_NON_BIDI;
403 * Return the languages that are installed for the keyboard.
406 * @return integer array with an entry for each installed language
408 static long[] getKeyboardLanguageList() {
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);
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;
421 * Return whether or not the platform supports a bidi language. Determine this
422 * by looking at the languages that are installed.
425 * @return true if bidi is supported, false otherwise. Always
426 * false on Windows CE.
428 public static boolean isBidiPlatform() {
429 if (isBidiPlatform != -1) return isBidiPlatform == 1; // already set
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;
443 Callback callback = null;
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);
450 } catch (ClassNotFoundException e) {
451 //callback can only be null at this point
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)) {
460 return isBidiPlatform == 1;
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.
467 * @return true if bidi is supported, false otherwise.
469 public static boolean isKeyboardBidi() {
470 long[] list = getKeyboardLanguageList();
471 for (int i=0; i<list.length; i++) {
472 if (isBidiLang(list[i])) {
479 * Removes the specified language listener.
482 * @param hwnd the handle of the Control that is listening for keyboard language changes
484 public static void removeLanguageListener (long hwnd) {
485 languageMap.remove(new LONG(hwnd));
488 public static void removeLanguageListener (Control control) {
489 removeLanguageListener(control.handle);
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).
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
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'};
511 * "Wide" version of win32 API can also run even on non-Unicode Windows,
512 * hence need for OS.IsUnicode check here.
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);
527 OS.GetCharacterPlacement(hdc, rtlProbe, rtlProbe.length, 0, result, OS.GCP_REORDER);
528 OS.MoveMemory(order, result.lpOrder, 4);
530 textDirection = SWT.RIGHT_TO_LEFT;
534 OS.GetCharacterPlacement(hdc, ltrProbe, ltrProbe.length, 0, result, OS.GCP_REORDER);
535 OS.MoveMemory(order, result.lpOrder + 4, 4);
538 OS.GetCharacterPlacement(hdc, numberProbe, numberProbe.length, 0, result, OS.GCP_REORDER);
539 OS.MoveMemory(order, result.lpOrder, 4);
541 textDirection = SWT.LEFT_TO_RIGHT;
546 OS.ReleaseDC (0, hdc);
547 OS.HeapFree(hHeap, 0, lpOrder);
548 return textDirection;
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.
557 * @param language integer representing language. One of
558 * KEYBOARD_BIDI, KEYBOARD_NON_BIDI.
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);
572 * Sets the orientation (writing order) of the specified control. Text will
573 * be right aligned for right to left writing order.
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
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;
586 bits &= ~OS.WS_EX_LAYOUTRTL;
588 OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits);
591 public static boolean setOrientation (Control control, int orientation) {
592 return setOrientation(control.handle, orientation);
595 * Override the window proc.
597 * @param hwnd control to override the window proc of
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());
608 * Reverse the character array. Used for right orientation.
610 * @param charArray character array to reverse
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;
621 * Reverse the integer array. Used for right orientation.
623 * @param intArray integer array to reverse
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;
634 * Adjust the order array so that it is relative to the start of the line. Also reverse the order array if the orientation
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
641 static void translateOrder(int[] orderArray, int glyphCount, boolean isRightOriented) {
643 int length = orderArray.length;
644 if (isRightOriented) {
645 for (int i=0; i<length; i++) {
646 maxOrder = Math.max(maxOrder, orderArray[i]);
649 for (int i=0; i<length; i++) {
650 if (isRightOriented) orderArray[i] = maxOrder - orderArray[i];
651 orderArray [i] += glyphCount;
655 * Remove the overridden the window proc.
657 * @param hwnd control to remove the window proc override for
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);
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.
672 * @param hwnd handle of the control that is listening for the keyboard language
674 * @param msg window message
676 static long windowProc (long hwnd, long msg, long wParam, long lParam) {
677 LONG key = new LONG (hwnd);
679 case OS.WM_INPUTLANGCHANGE:
680 Runnable runnable = languageMap.get (key);
681 if (runnable != null) runnable.run ();
684 LONG oldProc = oldProcMap.get(key);
685 return OS.CallWindowProc (oldProc.value, hwnd, (int)msg, wParam, lParam);