JDK-4955581 : [Win] Caret is positioned incorrectly while using Chinese QuanPin Input Method
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt:i18n
  • Affected Version: 6
  • Priority: P5
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2003-11-18
  • Updated: 2017-07-31
  • Resolved: 2017-07-31
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
Name: ks84122			Date: 11/17/2003


This is reproducible on Win 2000 with Simplified Chinese PRC, QuanPin IME:

When using Simplified Chinese QuanPin input method with mouse selection,  caret is positioned  at the click location in the IME window.
Steps to reproduce:
1. Create a JEditorPane in a Java application.
2. Run the application on Chinese Windows.
3. Input 10-20 lines of any text into the JEditorPane.
4. Move caret to the second line, turn on  QuanPin IME  and type "ji", an IME window will list multiple choices of entries, use mouse to select  an entry(not the first one). If  second windows come out and list more specific choices, use mouse to continue the selection until  a word is inserted into the JEditoePane.

//exp:  the caret is positioned at the input place at the second line.
//act:  The caret is moved to the location of the last click in the IME window.
        and the text between the input place at the second line and the 
        last click in the IME Window is selected
(Review ID: 216748) 
======================================================================

Comments
EVALUATION The proposed fix for this IM is as follows. The popup window of the QuanPin input method should disappear on MOUSE_RELEASE, not on MOUSE_PRESS.
19-10-2007

EVALUATION The last approach has also introduced a high negative impact on DnD. In fact if we replace the DRAG event by the MOVE event would be considered by DnD as the mouse no more in dragging state and it lost RELEASE somewhere. Initially we had some doubts related with this defect. If the components is moving under the mouse cursor, then the coordinates of the mouse are about to change because it always has the relative per-component coordinates. Moving a frame around shows that the mouseevents still coming (DRAG or MOVE depending on the mouse button state). Also, press the BUTTON1 in a browser and scroll it with the wheel. The result is - selected text in a browser. I consider it as not a defect for AWT. This problem should be addressed to the im team to fix this particular input method itself.
18-10-2007

SUGGESTED FIX The last version of the fix is here: http://sa.sfbay.sun.com/projects/awt_data/7/4955581/
18-10-2007

SUGGESTED FIX Here is tested fix. --- /net/karanar/export/dav/mustang53_pit//webrev/src/windows/native/sun/windows/awt_Component.h 2005-09-21 17:02:59.000000000 +0400 *** 803,812 **** --- 803,813 ---- jobject cause; }; void ReleaseDCList(HWND hwnd, DCList &list); void MoveDCToPassiveList(HDC hDC); + void convertCoordsToClient(HWND hwnd, int &x, int &y); jlong nowMillisUTC(); jlong nowMillisUTC(DWORD event_offset); #include "ObjectList.h" --- /net/karanar/export/dav/mustang53_pit//webrev/src/windows/native/sun/windows/awt_Component.cpp 2005-09-21 17:02:53.000000000 +0400 *** 1587,1596 **** --- 1587,1599 ---- case WM_AWT_MOUSEENTER: case WM_AWT_MOUSEEXIT: { DWORD curPos = ::GetMessagePos(); POINT myPos; + //save absolute coordinates to send in WmMouseMove and avoid client-to-screen transformation there. + int absX = GET_X_LPARAM(curPos); + int absY = GET_Y_LPARAM(curPos); myPos.x = GET_X_LPARAM(curPos); myPos.y = GET_Y_LPARAM(curPos); ::ScreenToClient(GetHWnd(), &myPos); switch(switchMessage) { case WM_AWT_MOUSEENTER: *** 1601,1611 **** LEFT_BUTTON); break; case WM_LBUTTONUP: mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y, LEFT_BUTTON); break; case WM_MOUSEMOVE: ! mr = WmMouseMove(static_cast<UINT>(wParam), myPos.x, myPos.y); break; case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y, MIDDLE_BUTTON); break; case WM_RBUTTONDOWN: --- 1604,1614 ---- LEFT_BUTTON); break; case WM_LBUTTONUP: mr = WmMouseUp(static_cast<UINT>(wParam), myPos.x, myPos.y, LEFT_BUTTON); break; case WM_MOUSEMOVE: ! mr = WmMouseMove(static_cast<UINT>(wParam), absX, absY); break; case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y, MIDDLE_BUTTON); break; case WM_RBUTTONDOWN: *** 2451,2460 **** --- 2454,2466 ---- static jlong lastTime = 0; static int lastClickX = 0; static int lastClickY = 0; static int lastButton = 0; static int clickCount = 0; + static POINT lastMouseClickPoint; + + static BOOL mouseButtonIsStillPressed = FALSE; // A static method that makes the clickCount available in the derived classes // overriding WmMouseDown(). int AwtComponent::GetClickCount() { *** 2473,2492 **** { clickCount++; } else { clickCount = 1; lastClickWnd = this; lastButton = button; lastClickX = x; lastClickY = y; } lastTime = now; m_dragged = FALSE; MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); - SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y, GetJavaModifiers(), clickCount, JNI_FALSE, GetButton(button), &msg); /* * NOTE: this call is intentionally placed after all other code, --- 2479,2502 ---- { clickCount++; } else { clickCount = 1; lastClickWnd = this; + mouseButtonIsStillPressed = TRUE; lastButton = button; lastClickX = x; lastClickY = y; } lastTime = now; m_dragged = FALSE; + lastMouseClickPoint.x = lastClickX; + lastMouseClickPoint.y = lastClickY; + ::ClientToScreen(GetHWnd(), &lastMouseClickPoint); + MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y, GetJavaModifiers(), clickCount, JNI_FALSE, GetButton(button), &msg); /* * NOTE: this call is intentionally placed after all other code, *** 2567,2594 **** if ( (flags & ALL_MK_BUTTONS) != 0 ) { // 4731797 - if mouse is only dragged a short distance // (less than SM_CXDRAG/SM_CYDRAG), MOUSE_CLICKED event should be // sent on the release. if (!m_dragged) { ! int diffX = abs(x-lastClickX); ! int diffY = abs(y-lastClickY); if (diffX > ::GetSystemMetrics(SM_CXDRAG)/2 || diffY > ::GetSystemMetrics(SM_CYDRAG)/2) { m_dragged = TRUE; } } // fix for 5039416 REGRESSION: Extra mouse click dispatched after press-drag- release sequence. // shouldn't send DRAGGED event if mouse in the smudge area ! if (m_dragged) { MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_DRAGGED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); } } else { MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_MOVED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); --- 2577,2607 ---- if ( (flags & ALL_MK_BUTTONS) != 0 ) { // 4731797 - if mouse is only dragged a short distance // (less than SM_CXDRAG/SM_CYDRAG), MOUSE_CLICKED event should be // sent on the release. + if (!m_dragged) { ! int diffX = abs(x - lastMouseClickPoint.x); ! int diffY = abs(y - lastMouseClickPoint.y); if (diffX > ::GetSystemMetrics(SM_CXDRAG)/2 || diffY > ::GetSystemMetrics(SM_CYDRAG)/2) { m_dragged = TRUE; } } // fix for 5039416 REGRESSION: Extra mouse click dispatched after press-drag- release sequence. // shouldn't send DRAGGED event if mouse in the smudge area ! if (m_dragged && mouseButtonIsStillPressed == TRUE) { ! convertCoordsToClient(GetHWnd(), x, y); MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_DRAGGED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); } } else { + convertCoordsToClient(GetHWnd(), x, y); MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_MOVED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); *** 2596,2609 **** --- 2609,2635 ---- } return mrConsume; } + void convertCoordsToClient(HWND hwnd, int &x, int &y){ + POINT localCoords; + localCoords.x = x; + localCoords.y = y; + ::ScreenToClient(hwnd, &localCoords); + x = localCoords.x; + y = localCoords.y; + } + MsgRouting AwtComponent::WmMouseExit(UINT flags, int x, int y) { SendMouseEvent(java_awt_event_MouseEvent_MOUSE_EXITED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE); + //Fix 4955581. Leaving the component with unpressed mouse buttons. + if (::GetCapture() == NULL) { + mouseButtonIsStillPressed = FALSE; + } sm_cursorOn = NULL; return mrConsume; /* Don't pass our synthetic event on! */ } MsgRouting AwtComponent::WmMouseWheel(UINT flags, int x, int y, *** 4906,4915 **** --- 4932,4943 ---- void AwtComponent::ReleaseDragCapture(UINT flags) { if ((::GetCapture() == GetHWnd()) && ((flags & ALL_MK_BUTTONS) == 0)) { // user has released all buttons, so release the capture ::ReleaseCapture(); + //Fix 4955581. + mouseButtonIsStillPressed = FALSE; } } void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y, jint modifiers, jint clickCount,
06-10-2005

SUGGESTED FIX *** /net/aquila/export/dav/mustang51//webrev/src/windows/native/sun/windows/awt_Component.cpp- 2005-09-06 16:32:26.000000000 +0400 --- /net/aquila/export/dav/mustang51//webrev/src/windows/native/sun/windows/awt_Component.cpp 2005-09-06 16:32:26.000000000 +0400 *** 2437,2446 **** --- 2437,2447 ---- static jlong lastTime = 0; static int lastClickX = 0; static int lastClickY = 0; static int lastButton = 0; static int clickCount = 0; + static POINT lastMouseClickPoint; // A static method that makes the clickCount available in the derived classes // overriding WmMouseDown(). int AwtComponent::GetClickCount() { *** 2466,2475 **** --- 2467,2480 ---- lastClickY = y; } lastTime = now; m_dragged = FALSE; + lastMouseClickPoint.x = lastClickX; + lastMouseClickPoint.y = lastClickY; + ::ClientToScreen(GetHWnd(), &lastMouseClickPoint); + MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y, GetJavaModifiers(), clickCount, JNI_FALSE, *** 2538,2573 **** MsgRouting AwtComponent::WmMouseMove(UINT flags, int x, int y) { static AwtComponent* lastComp = NULL; static int lastX = 0; static int lastY = 0; ! /* * Only report mouse move and drag events if a move or drag * actually happened -- Windows sends a WM_MOUSEMOVE in case the * app wants to modify the cursor. */ if (lastComp != this || x != lastX || y != lastY) { lastComp = this; lastX = x; lastY = y; if ( (flags & ALL_MK_BUTTONS) != 0 ) { // 4731797 - if mouse is only dragged a short distance // (less than SM_CXDRAG/SM_CYDRAG), MOUSE_CLICKED event should be // sent on the release. if (!m_dragged) { ! int diffX = abs(x-lastClickX); ! int diffY = abs(y-lastClickY); if (diffX > ::GetSystemMetrics(SM_CXDRAG)/2 || diffY > ::GetSystemMetrics(SM_CYDRAG)/2) { m_dragged = TRUE; } } // fix for 5039416 REGRESSION: Extra mouse click dispatched after press-drag- release sequence. // shouldn't send DRAGGED event if mouse in the smudge area ! if (m_dragged) { MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_DRAGGED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); --- 2543,2582 ---- MsgRouting AwtComponent::WmMouseMove(UINT flags, int x, int y) { static AwtComponent* lastComp = NULL; static int lastX = 0; static int lastY = 0; ! POINT curMouseCoordsPoint; /* * Only report mouse move and drag events if a move or drag * actually happened -- Windows sends a WM_MOUSEMOVE in case the * app wants to modify the cursor. */ if (lastComp != this || x != lastX || y != lastY) { + curMouseCoordsPoint.x = x; + curMouseCoordsPoint.y = y; + ::ClientToScreen(GetHWnd(), &curMouseCoordsPoint); lastComp = this; lastX = x; lastY = y; if ( (flags & ALL_MK_BUTTONS) != 0 ) { // 4731797 - if mouse is only dragged a short distance // (less than SM_CXDRAG/SM_CYDRAG), MOUSE_CLICKED event should be // sent on the release. + if (!m_dragged) { ! int diffX = abs(curMouseCoordsPoint.x - lastMouseClickPoint.x); ! int diffY = abs(curMouseCoordsPoint.y - lastMouseClickPoint.y); if (diffX > ::GetSystemMetrics(SM_CXDRAG)/2 || diffY > ::GetSystemMetrics(SM_CYDRAG)/2) { m_dragged = TRUE; } } // fix for 5039416 REGRESSION: Extra mouse click dispatched after press-drag- release sequence. // shouldn't send DRAGGED event if mouse in the smudge area ! if (m_dragged && lastClickWnd != NULL) { MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); SendMouseEvent(java_awt_event_MouseEvent_MOUSE_DRAGGED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE, java_awt_event_MouseEvent_NOBUTTON, &msg); *** 2586,2595 **** --- 2595,2608 ---- MsgRouting AwtComponent::WmMouseExit(UINT flags, int x, int y) { SendMouseEvent(java_awt_event_MouseEvent_MOUSE_EXITED, nowMillisUTC(), x, y, GetJavaModifiers(), 0, JNI_FALSE); + //if we leave component with uncaptured mouse then nullify lastClickWnd + if (::GetCapture() == NULL) { + lastClickWnd = NULL; + } sm_cursorOn = NULL; return mrConsume; /* Don't pass our synthetic event on! */ } MsgRouting AwtComponent::WmMouseWheel(UINT flags, int x, int y, *** 4865,4874 **** --- 4878,4888 ---- void AwtComponent::ReleaseDragCapture(UINT flags) { if ((::GetCapture() == GetHWnd()) && ((flags & ALL_MK_BUTTONS) == 0)) { // user has released all buttons, so release the capture ::ReleaseCapture(); + lastClickWnd = NULL; } } void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y, jint modifiers, jint clickCount,
08-09-2005

EVALUATION The situation is if we have two java windows and one of them hides itself just after MousePress then the second one if it lies under the first one will probably receive MouseDragged event even there is no actual mouse movements at all. This does occur on Windows only and also happen with one java window and any native window over it. This happen because we calculate X and Y axises differences between two separate coordinates: when user pressed mouse over one window and when user releases mouse over another one. This actually uncover the problem as in each case we use different coordinate systems. They are just relate to different windows and the differences between them are big enough to think that user moves mouse. To solve this we may use coordinates on the screen instead of the windows' one. Another problem is the case when a native window is involved. There is a prevClick variable used and seems it contain wrong values after mouse release. This happen because actual mouse press occur not on java window but on native one and as I said lastClick variable contains coordinates from previous press. This might also be fixed by using an event when mouse leaves java window and by nullifyin lastClickWnd var.
08-09-2005

EVALUATION On Windows when user releases Mouse then JDK shared code generates MouseDragged event. MouseListener hooked on JTextArea doesn't receive it though. It looks like awt issue.
05-09-2005

EVALUATION After further evaluation we noticed that the initiator of selection probably not a MouseRelease but MouseEntered. This affect JTextArea, JEditorPane at least.
01-09-2005

EVALUATION Implementing this behaviour will very likely break DnD and other parts of JDK. The better way is to give a chance to fix it to IM team.
23-08-2005

SUGGESTED FIX --- Container.java 2005-08-23 12:09:24.000000000 +0400 *************** *** 81,86 **** --- 81,88 ---- */ int ncomponents; + transient static Component mouseEventTargetHW = null; + /** * The components in this container. * @see #add *** 2001,2006 **** --- 2015,2041 ---- * @param e the event */ void dispatchEventImpl(AWTEvent e) { + if (e instanceof MouseEvent) { + MouseEvent me = (MouseEvent)e; + switch (me.getID()) { + case MouseEvent.MOUSE_PRESSED: + mouseEventTargetHW = (Component)(me.getSource()); + break; + case MouseEvent.MOUSE_RELEASED: + synchronized (getTreeLock()) { + Component mouseOver = getHeavyweightContainer().getMouseEventTarget(me.getX(), me.getY(), + Container.INCLUDE_SELF); + if (mouseOver != mouseEventTargetHW) { + mouseEventTargetHW = null; + return; + } + }// synchronized + break; + default: + break; + } + } + if ((dispatcher != null) && dispatcher.dispatchEvent(e)) { // event was sent to a lightweight component. The // native-produced event sent to the native container
23-08-2005

EVALUATION Should store target when MOUSE_PRESSED occur and then check if MOUSE_RELEASED has the same source and if they are different we just skip this event.
22-08-2005

EVALUATION Actually I see MOUSE_RELEASED but not MOUSE_DRAGGED on JEditorPane after IM window close. I'm trying to reproduce the same with pure AWT. I'm using AWT toplevels like Window, Dialog and Frame and always getting last mouse event on the correct Frame. The scenarion is something like this: 1) Frame1 owns TextArea. 2) Another toplevel (Window) positioned over this Frame1. 3) Window closes (doing hide() ) after user MOUSEPRESS. I expect that sequenced MOUSERELEASE will come to underlying Frame1 but it always comes to Window. But doing window.dispose() in step 2) leads to sending MOUSE_RELEASED on Frame1. So the reason of this bug in how IM window react on MOUSE_PRESS.
22-08-2005

EVALUATION When the look up choice panel of the input method is closed by a mouse click, MouseMotionListener.mouseDragged() is issued, even though the user is not actually dragging the mouse pointer. This causes Swing's DefaultCaret.moveCaret() and it selects the text between the current caret position and the position nearest to the mouse pointer. So in this case, mouseDragged() should not be issued. ###@###.### 2003-11-18 Name: rpR10076 Date: 11/19/2003 Will investigate for dragonfly. ###@###.### ====================================================================== This looks similar to 5039416. There was a problem when windows JDK implementation always posted MouseMoved or MouseDragged events even if user moved mouse for a short distance. User may not notice that movement though. Should verify that this bug has already fixed by 5039416 in JDK6.0b38. src/windows/native/sun/windows/awt_Component.cpp ###@###.### 2005-06-03 10:02:41 GMT It is still reproducible with 5039416 fix. When I press mouse on the last menu with all available characters I see that there is a selection in JEditorPane appear and caret positions into wrong place. It's not occur if JEditorPane is small enough and mouse press occur outside of JFrame containing this editor. ###@###.### 2005-06-16 11:53:26 GMT
16-06-2005

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang
02-09-2004