--- old/modules/graphics/src/main/java/com/sun/glass/ui/View.java 2014-11-13 20:25:28.272977480 +0300 +++ new/modules/graphics/src/main/java/com/sun/glass/ui/View.java 2014-11-13 20:25:28.176977480 +0300 @@ -37,6 +37,12 @@ public final static int GESTURE_NO_VALUE = Integer.MAX_VALUE; public final static double GESTURE_NO_DOUBLE_VALUE = Double.NaN; + public final static byte IME_ATTR_INPUT = 0x00; + public final static byte IME_ATTR_TARGET_CONVERTED = 0x01; + public final static byte IME_ATTR_CONVERTED = 0x02; + public final static byte IME_ATTR_TARGET_NOTCONVERTED = 0x03; + public final static byte IME_ATTR_INPUT_ERROR = 0x04; + final static boolean accessible = AccessController.doPrivileged((PrivilegedAction) () -> { String force = System.getProperty("glass.accessible.force"); if (force != null) return Boolean.parseBoolean(force); --- old/modules/graphics/src/main/java/com/sun/glass/ui/gtk/GtkView.java 2014-11-13 20:25:28.564977483 +0300 +++ new/modules/graphics/src/main/java/com/sun/glass/ui/gtk/GtkView.java 2014-11-13 20:25:28.460977482 +0300 @@ -29,6 +29,7 @@ import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.ArrayList; import java.util.Map; final class GtkView extends View { @@ -36,6 +37,7 @@ private boolean imEnabled = false; private boolean isInPreeditMode = false; private final StringBuilder preedit = new StringBuilder(); + private ByteBuffer attributes; private int lastCaret; private native void enableInputMethodEventsImpl(long ptr, boolean enable); @@ -117,13 +119,76 @@ isInPreeditMode = enabled; } - protected void notifyInputMethodDraw(String text, int first, int length, int caret) { + + protected void notifyInputMethodDraw(String text, int first, int length, int caret, byte[] attr) { + int[] boundary = null; + byte[] values = null; + + if (attributes == null ) { + attributes = ByteBuffer.allocate(32); + } + + if (length > 0) { + preedit.replace(first, first + length, ""); + } + if (text != null) { - preedit.replace(first, first + length, text); + preedit.insert(first, text); } else { - preedit.setLength(0); + if (attr == null) { + preedit.setLength(0); + } } - notifyInputMethod(preedit.toString(), null, null, null, 0, caret, 0); + + if (attributes.capacity() < preedit.length()) { + ByteBuffer tmp = ByteBuffer.allocate((int) (preedit.length() * 1.5)); + tmp.put(attributes); + attributes = tmp; + } + + attributes.limit(preedit.length()); + + if (attr != null && attributes.limit() >= (first + attr.length)) { + attributes.position(first); + attributes.put(attr); + } + + if (attributes.limit() > 0) { + ArrayList boundaryList = new ArrayList<>(); + ArrayList valuesList = new ArrayList<>(); + attributes.rewind(); + byte lastAttribute = attributes.get(); + + boundaryList.add(0); + valuesList.add(lastAttribute); + + int i = 1; + while (attributes.hasRemaining()) { + byte a = attributes.get(); + if (lastAttribute != a) { + boundaryList.add(i); + valuesList.add(a); + } + lastAttribute = a; + i++; + } + + boundaryList.add(attributes.limit()); + + boundary = new int[boundaryList.size()]; + i = 0; + for (Integer e : boundaryList) { + boundary[i++] = e; + } + + values = new byte[valuesList.size()]; + i = 0; + for (Byte e: valuesList) { + values[i++] = e; + } + } + + notifyInputMethod(preedit.toString(), boundary, boundary, values, 0, caret, 0); lastCaret = caret; } --- old/modules/graphics/src/main/java/com/sun/glass/ui/mac/MacView.java 2014-11-13 20:25:28.844977485 +0300 +++ new/modules/graphics/src/main/java/com/sun/glass/ui/mac/MacView.java 2014-11-13 20:25:28.748977484 +0300 @@ -30,6 +30,7 @@ import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Map; +import java.util.TreeSet; /** * MacOSX platform implementation class for View. @@ -118,7 +119,8 @@ _hostRemoteLayerId(getNativeLayer(), nativeLayerId); } - protected void notifyInputMethodMac(String str, int attrib, int length, int cursor) { + protected void notifyInputMethodMac(String str, int attrib, int length, + int cursor, int selStart, int selLength) { byte atts[] = new byte[1]; atts[0] = (byte) attrib; int attBounds[] = new int[2]; @@ -129,7 +131,30 @@ notifyInputMethod(str, null, attBounds, atts, length, cursor, 0); } else { // all other cases = just an update, update preview text but do not commit it - notifyInputMethod(str, null, attBounds, atts, 0, cursor, 0); + if (selLength > 0 + && str != null && str.length() > 0 + && selStart >= 0 + && selLength + selStart <= str.length()) { + + TreeSet b = new TreeSet<>(); + b.add(0); + b.add(selStart); + b.add(selStart + selLength); + b.add(str.length()); + + int[] boundary = b.stream().mapToInt(Integer::intValue).toArray(); + byte[] values = new byte[boundary.length - 1]; + + for (int i = 0; i < boundary.length - 1; i++) { + values[i] = (boundary[i] == selStart) + ? IME_ATTR_TARGET_CONVERTED + : IME_ATTR_CONVERTED; + } + + notifyInputMethod(str, boundary, boundary, values, 0, cursor, 0); + } else { + notifyInputMethod(str, null, attBounds, atts, 0, cursor, 0); + } } } } --- old/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java 2014-11-13 20:25:29.124977487 +0300 +++ new/modules/graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java 2014-11-13 20:25:29.024977486 +0300 @@ -460,13 +460,6 @@ } } - // TODO: Define these somewhere else. It is Windows specific for now. - private final static byte ATTR_INPUT = 0x00; - private final static byte ATTR_TARGET_CONVERTED = 0x01; - private final static byte ATTR_CONVERTED = 0x02; - private final static byte ATTR_TARGET_NOTCONVERTED = 0x03; - private final static byte ATTR_INPUT_ERROR = 0x04; - private static byte inputMethodEventAttrValue(int pos, int[] attrBoundary, byte[] attrValue) { if (attrBoundary != null) { for (int current = 0; current < attrBoundary.length-1; current++) { @@ -476,7 +469,7 @@ } } } - return ATTR_INPUT_ERROR; + return View.IME_ATTR_INPUT_ERROR; } private static ObservableList inputMethodEventComposed( @@ -502,17 +495,17 @@ InputMethodHighlight highlight; switch (inputMethodEventAttrValue(clauseBoundary[current], attrBoundary, attrValue)) { - case ATTR_TARGET_CONVERTED: + case View.IME_ATTR_TARGET_CONVERTED: highlight = InputMethodHighlight.SELECTED_CONVERTED; break; - case ATTR_CONVERTED: + case View.IME_ATTR_CONVERTED: highlight = InputMethodHighlight.UNSELECTED_CONVERTED; break; - case ATTR_TARGET_NOTCONVERTED: + case View.IME_ATTR_TARGET_NOTCONVERTED: highlight = InputMethodHighlight.SELECTED_RAW; break; - case ATTR_INPUT: - case ATTR_INPUT_ERROR: + case View.IME_ATTR_INPUT: + case View.IME_ATTR_INPUT_ERROR: default: highlight = InputMethodHighlight.UNSELECTED_RAW; break; --- old/modules/graphics/src/main/native-glass/gtk/glass_general.cpp 2014-11-13 20:25:29.408977489 +0300 +++ new/modules/graphics/src/main/native-glass/gtk/glass_general.cpp 2014-11-13 20:25:29.316977488 +0300 @@ -218,7 +218,7 @@ clazz = env->FindClass("com/sun/glass/ui/gtk/GtkView"); if (env->ExceptionCheck()) return JNI_ERR; - jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III)V"); + jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III[B)V"); if (env->ExceptionCheck()) return JNI_ERR; jViewNotifyInputMethodCaret = env->GetMethodID(clazz, "notifyInputMethodCaret", "(III)V"); if (env->ExceptionCheck()) return JNI_ERR; --- old/modules/graphics/src/main/native-glass/gtk/glass_general.h 2014-11-13 20:25:29.688977491 +0300 +++ new/modules/graphics/src/main/native-glass/gtk/glass_general.h 2014-11-13 20:25:29.592977490 +0300 @@ -146,7 +146,7 @@ extern jmethodID jViewNotifyDragLeave; //com.sun.glass.ui.View#notifyDragLeave ()V extern jmethodID jViewNotifyScroll; //com.sun.glass.ui.View#notifyScroll (IIIIDDIIIIIDD)V extern jmethodID jViewNotifyInputMethod; //com.sun.glass.ui.View#notifyInputMethod (Ljava/lang/String;[I[I[BIII)V - extern jmethodID jViewNotifyInputMethodDraw; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodDraw (Ljava/lang/String;III)V + extern jmethodID jViewNotifyInputMethodDraw; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodDraw (Ljava/lang/String;III[B)V extern jmethodID jViewNotifyInputMethodCaret; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodCaret (III)V extern jmethodID jViewNotifyPreeditMode; //com.sun.glass.ui.gtk.GtkView#notifyPreeditMode (Z)V extern jmethodID jViewNotifyMenu; //com.sun.glass.ui.View#notifyMenu (IIIIZ)V --- old/modules/graphics/src/main/native-glass/gtk/glass_window_ime.cpp 2014-11-13 20:25:29.964977493 +0300 +++ new/modules/graphics/src/main/native-glass/gtk/glass_window_ime.cpp 2014-11-13 20:25:29.872977492 +0300 @@ -22,6 +22,8 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +#include "com_sun_glass_ui_View.h" #include "glass_window.h" #include "glass_general.h" #include "glass_gtkcompat.h" @@ -145,22 +147,45 @@ XIMPreeditDrawCallbackStruct *data = (XIMPreeditDrawCallbackStruct*) call; jstring text = NULL; - if (data->text != NULL && data->text->string.multi_byte != NULL) { - if (data->text->encoding_is_wchar) { - size_t csize = wcstombs(NULL, data->text->string.wide_char, 0); - char *ctext = new char[csize + 1]; - wcstombs(ctext, data->text->string.wide_char, csize + 1); - text = mainEnv->NewStringUTF(ctext); - EXCEPTION_OCCURED(mainEnv); - delete[] ctext; - } else { - text = mainEnv->NewStringUTF(data->text->string.multi_byte); - EXCEPTION_OCCURED(mainEnv); + jbyteArray attr = NULL; + + if (data->text != NULL) { + if (data->text->string.multi_byte) { + if (data->text->encoding_is_wchar) { + size_t csize = wcstombs(NULL, data->text->string.wide_char, 0); + char *ctext = new char[csize + 1]; + wcstombs(ctext, data->text->string.wide_char, csize + 1); + text = mainEnv->NewStringUTF(ctext); + delete[] ctext; + CHECK_JNI_EXCEPTION(mainEnv); + } else { + text = mainEnv->NewStringUTF(data->text->string.multi_byte); + CHECK_JNI_EXCEPTION(mainEnv); + } + } + + if (XIMFeedback* fb = data->text->feedback) { + attr = mainEnv->NewByteArray(data->text->length); + CHECK_JNI_EXCEPTION(mainEnv) + jbyte v[data->text->length]; + for (int i = 0; i < data->text->length; i++) { + if (fb[i] & XIMReverse) { + v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_NOTCONVERTED; + } else if (fb[i] & XIMHighlight) { + v[i] = com_sun_glass_ui_View_IME_ATTR_TARGET_CONVERTED; + } else if (fb[i] & XIMUnderline) { + v[i] = com_sun_glass_ui_View_IME_ATTR_CONVERTED; + } else { + v[i] = com_sun_glass_ui_View_IME_ATTR_INPUT; + } + } + mainEnv->SetByteArrayRegion(attr, 0, data->text->length, v); + CHECK_JNI_EXCEPTION(mainEnv) } } mainEnv->CallVoidMethod((jobject)client, jViewNotifyInputMethodDraw, - text, data->chg_first, data->chg_length, data->caret); + text, data->chg_first, data->chg_length, data->caret, attr); CHECK_JNI_EXCEPTION(mainEnv) } --- old/modules/graphics/src/main/native-glass/mac/GlassView.m 2014-11-13 20:25:30.240977495 +0300 +++ new/modules/graphics/src/main/native-glass/mac/GlassView.m 2014-11-13 20:25:30.144977494 +0300 @@ -149,7 +149,7 @@ if (jViewNotifyInputMethodMac == NULL) { jclass jMacViewClass = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacView" withEnv:env]; - jViewNotifyInputMethodMac = (*env)->GetMethodID(env, jMacViewClass, "notifyInputMethodMac", "(Ljava/lang/String;III)V"); + jViewNotifyInputMethodMac = (*env)->GetMethodID(env, jMacViewClass, "notifyInputMethodMac", "(Ljava/lang/String;IIIII)V"); if ((*env)->ExceptionCheck(env)) return; } --- old/modules/graphics/src/main/native-glass/mac/GlassView3D.m 2014-11-13 20:25:30.520977497 +0300 +++ new/modules/graphics/src/main/native-glass/mac/GlassView3D.m 2014-11-13 20:25:30.424977496 +0300 @@ -688,7 +688,7 @@ { IMLOG("insertText called with string: %s", [aString UTF8String]); if ([self->nsAttrBuffer length] > 0 || [aString length] > 1) { - [self->_delegate notifyInputMethod:aString attr:4 length:(int)[aString length] cursor:(int)[aString length] ]; + [self->_delegate notifyInputMethod:aString attr:4 length:(int)[aString length] cursor:(int)[aString length] selectedRange: NSMakeRange(NSNotFound, 0)]; self->shouldProcessKeyEvent = NO; } else { self->shouldProcessKeyEvent = YES; @@ -706,7 +706,7 @@ NSAttributedString *attrString = (isAttributedString ? (NSAttributedString *)aString : nil); NSString *incomingString = (isAttributedString ? [aString string] : aString); IMLOG("setMarkedText called, attempt to set string to %s", [incomingString UTF8String]); - [self->_delegate notifyInputMethod:incomingString attr:1 length:0 cursor:(int)[incomingString length] ]; + [self->_delegate notifyInputMethod:incomingString attr:1 length:0 cursor:(int)[incomingString length] selectedRange:selectionRange ]; self->nsAttrBuffer = (attrString == nil ? [self->nsAttrBuffer initWithString:incomingString] : [self->nsAttrBuffer initWithAttributedString: attrString]); self->shouldProcessKeyEvent = NO; @@ -717,7 +717,7 @@ IMLOG("unmarkText called\n"); if (self->nsAttrBuffer != nil && self->nsAttrBuffer.length != 0) { self->nsAttrBuffer = [self->nsAttrBuffer initWithString:@""]; - [self->_delegate notifyInputMethod:@"" attr:4 length:0 cursor:0 ]; + [self->_delegate notifyInputMethod:@"" attr:4 length:0 cursor:0 selectedRange: NSMakeRange(NSNotFound, 0)]; } self->shouldProcessKeyEvent = YES; } --- old/modules/graphics/src/main/native-glass/mac/GlassViewDelegate.h 2014-11-13 20:25:30.800977499 +0300 +++ new/modules/graphics/src/main/native-glass/mac/GlassViewDelegate.h 2014-11-13 20:25:30.704977498 +0300 @@ -90,7 +90,7 @@ - (void)exitFullscreenWithAnimate:(BOOL)animate; - (void)sendJavaFullScreenEvent:(BOOL)entered withNativeWidget:(BOOL)isNative; -- (void)notifyInputMethod:(id)aString attr:(int)attr length:(int)length cursor:(int)cursor; +- (void)notifyInputMethod:(id)aString attr:(int)attr length:(int)length cursor:(int)cursor selectedRange:(NSRange)selectionRange; - (NSRect)getInputMethodCandidatePosRequest:(int)pos; - (void)setFrameOrigin:(NSPoint)newOrigin; --- old/modules/graphics/src/main/native-glass/mac/GlassViewDelegate.m 2014-11-13 20:25:31.068977501 +0300 +++ new/modules/graphics/src/main/native-glass/mac/GlassViewDelegate.m 2014-11-13 20:25:30.972977500 +0300 @@ -983,13 +983,13 @@ return YES; } -- (void)notifyInputMethod:(id) aString attr:(int)attr length:(int)length cursor:(int)cursor +- (void)notifyInputMethod:(id) aString attr:(int)attr length:(int)length cursor:(int)cursor selectedRange:(NSRange)selectionRange { if ([NSThread isMainThread] == YES) { GET_MAIN_JENV; jstring jStr = (*env)->NewStringUTF(env, [aString UTF8String]); - (*env)->CallVoidMethod(env, self->jView, jViewNotifyInputMethodMac, jStr, attr, length, cursor); + (*env)->CallVoidMethod(env, self->jView, jViewNotifyInputMethodMac, jStr, attr, length, cursor, selectionRange.location, selectionRange.length); GLASS_CHECK_EXCEPTION(env); } }