ime editing in edit control

Aric Stewart aric at codeweavers.com
Fri Dec 16 07:46:07 CST 2005


Make the edit control ime aware and implement inline editing. Makes for 
a significantly better user experience for CJK users.

-------------- next part --------------
Index: dlls/user/edit.c
===================================================================
RCS file: /home/wine/wine/dlls/user/edit.c,v
retrieving revision 1.43
diff -u -r1.43 edit.c
--- dlls/user/edit.c	23 Nov 2005 19:29:20 -0000	1.43
+++ dlls/user/edit.c	16 Dec 2005 13:34:06 -0000
@@ -53,6 +53,7 @@
 #include "winnt.h"
 #include "wownt32.h"
 #include "win.h"
+#include "imm.h"
 #include "wine/winbase16.h"
 #include "wine/winuser16.h"
 #include "wine/unicode.h"
@@ -151,6 +152,12 @@
 				   	   or EM_SETHANDLE16 */
 	HLOCAL hloc32A;			/* alias for ANSI control receiving EM_GETHANDLE
 				   	   or EM_SETHANDLE */
+    /*
+     * IME Data
+     */
+    UINT composition_len;   /* lenght of composition, 0 == no composition */
+    int composition_start;  /* the character position for the composition */
+    INT tmDescent;          /* the Descent for the font.  Needed to draw Underline*/
 } EDITSTATE;
 
 
@@ -280,6 +287,8 @@
 static LRESULT	EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos);
 static void	EDIT_UpdateText(EDITSTATE *es, LPRECT rc, BOOL bErase);
 static void	EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase);
+static BOOL EDIT_IsImeMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
+        EDITSTATE *es);
 
 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -438,6 +447,12 @@
 
 	if (es && (msg != WM_DESTROY)) EDIT_LockBuffer(es);
 
+    if (EDIT_IsImeMessage(hwnd, msg, wParam, lParam, es))
+    {
+	    if (es) EDIT_UnlockBuffer(es, FALSE);
+        return TRUE;
+    }
+    
 	switch (msg) {
 	case EM_GETSEL16:
 		wParam = 0;
@@ -1577,7 +1592,7 @@
 		rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
 	else
 		rc->top = es->format_rect.top;
-	rc->bottom = rc->top + es->line_height;
+	rc->bottom = rc->top + es->line_height + es->tmDescent;
 	rc->left = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
 	rc->right = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
 }
@@ -2188,6 +2203,9 @@
 {
 	COLORREF BkColor;
 	COLORREF TextColor;
+    LOGFONTW underline_font;
+    HFONT    hUnderline = (HFONT)NULL;
+    HFONT   old_font = (HFONT)NULL;
 	INT ret;
 	INT li;
 	INT BkMode;
@@ -2199,9 +2217,20 @@
 	BkColor = GetBkColor(dc);
 	TextColor = GetTextColor(dc);
 	if (rev) {
-		SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
-		SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
-		SetBkMode( dc, OPAQUE);
+        if (es->composition_len == 0)
+        {
+		    SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
+		    SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+		    SetBkMode( dc, OPAQUE);
+        }
+        else
+        {
+            HFONT current = GetCurrentObject(dc,OBJ_FONT);
+            GetObjectW(current,sizeof(LOGFONTW),&underline_font);
+            underline_font.lfUnderline = TRUE;
+            hUnderline = CreateFontIndirectW(&underline_font);
+            old_font = SelectObject(dc,hUnderline);
+        }
 	}
 	li = EDIT_EM_LineIndex(es, line);
 	if (es->style & ES_MULTILINE) {
@@ -2216,9 +2245,19 @@
 			HeapFree(GetProcessHeap(), 0, text);
 	}
 	if (rev) {
-		SetBkColor(dc, BkColor);
-		SetTextColor(dc, TextColor);
-		SetBkMode( dc, BkMode);
+        if (es->composition_len == 0)
+        {
+		    SetBkColor(dc, BkColor);
+		    SetTextColor(dc, TextColor);
+		    SetBkMode( dc, BkMode);
+        }
+        else
+        {
+            if (old_font)
+                SelectObject(dc,old_font);
+            if (hUnderline)
+                DeleteObject(hUnderline);
+        }
 	}
 	return ret;
 }
@@ -4871,6 +4910,7 @@
 	if (font)
 		old_font = SelectObject(dc, font);
 	GetTextMetricsW(dc, &tm);
+    es->tmDescent = tm.tmDescent;
 	es->line_height = tm.tmHeight;
 	es->char_width = tm.tmAveCharWidth;
 	if (font)
@@ -5216,3 +5256,184 @@
     }
     InvalidateRect(es->hwndSelf, rc, bErase);
 }
+
+/********************************************************************
+ * 
+ * The Following code is to handle inline editing from IMEs
+ */
+
+static void EDIT_GetCompositionStr(HWND hwnd, LPARAM CompFlag, EDITSTATE *es)
+{
+    DWORD dwBufLen;
+    LPWSTR lpCompStr = NULL;
+    HIMC hIMC;
+    LPSTR lpCompStrAttr = NULL;
+    DWORD dwBufLenAttr;
+
+    if (!(hIMC = ImmGetContext(hwnd)))
+        return;
+
+    dwBufLen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
+
+    if (dwBufLen <= 0)
+    {
+        ImmReleaseContext(hwnd, hIMC);
+        return;
+    }
+
+    lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen);
+    if (!lpCompStr)
+    {
+        ERR("Unable to allocate IME CompositionString\n");
+        ImmReleaseContext(hwnd,hIMC);
+        return;
+    }
+
+    ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, dwBufLen);
+    lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
+
+    if (CompFlag & GCS_COMPATTR)
+    {
+        dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
+        if (dwBufLenAttr)
+        {
+            dwBufLenAttr ++;
+            lpCompStrAttr = HeapAlloc(GetProcessHeap(),0,dwBufLenAttr);
+            if (!lpCompStrAttr)
+            {
+                ERR("Unable to allocate IME Attribute String\n");
+                HeapFree(GetProcessHeap(),0,lpCompStr);
+                ImmReleaseContext(hwnd,hIMC);
+                return;
+            }
+            ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr, 
+                    dwBufLenAttr);
+            lpCompStrAttr[dwBufLenAttr] = 0;
+        }
+        else
+            lpCompStrAttr = NULL;
+    }
+
+    /* check for change in composition start */
+    if (es->selection_end < es->composition_start)
+        es->composition_start = es->selection_end;
+    
+    /* replace existing selection string */
+    es->selection_start = es->composition_start;
+
+    if (es->composition_len > 0)
+        es->selection_end = es->composition_start + es->composition_len;
+    else
+        es->selection_end = es->selection_start;
+
+    EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, TRUE, TRUE);
+    es->composition_len = abs(es->composition_start - es->selection_end);
+
+    es->selection_start = es->composition_start;
+    es->selection_end = es->selection_start + es->composition_len;
+
+    HeapFree(GetProcessHeap(),0,lpCompStrAttr);
+    HeapFree(GetProcessHeap(),0,lpCompStr);
+    ImmReleaseContext(hwnd,hIMC);
+}
+
+static void EDIT_GetResultStr(HWND hwnd, EDITSTATE *es)
+{
+    DWORD dwBufLen;
+    LPWSTR lpResultStr;
+    HIMC    hIMC;
+
+    if ( !(hIMC = ImmGetContext(hwnd)))
+        return;
+
+    dwBufLen = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
+    if (dwBufLen <= 0)
+    {
+        ImmReleaseContext(hwnd, hIMC);
+        return;
+    }
+
+    lpResultStr = HeapAlloc(GetProcessHeap(),0, dwBufLen);
+    if (!lpResultStr)
+    {
+        ERR("Unable to alloc buffer for IME string\n");
+        ImmReleaseContext(hwnd, hIMC);
+        return;
+    }
+
+    ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, dwBufLen);
+    lpResultStr[dwBufLen/sizeof(WCHAR)] = 0;
+
+    /* check for change in composition start */
+    if (es->selection_end < es->composition_start)
+        es->composition_start = es->selection_end;
+
+    es->selection_start = es->composition_start;
+    es->selection_end = es->composition_start + es->composition_len;
+    EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, TRUE, TRUE);
+    es->composition_start = es->selection_end;
+    es->composition_len = 0;
+
+    HeapFree(GetProcessHeap(),0,lpResultStr);
+    ImmReleaseContext(hwnd, hIMC);
+}
+
+static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es)
+{
+    if (CompFlag & GCS_RESULTSTR)
+        EDIT_GetResultStr(hwnd,es);
+    if (CompFlag & GCS_COMPSTR)
+        EDIT_GetCompositionStr(hwnd, CompFlag, es);
+}
+
+static BOOL EDIT_IsImeMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
+        EDITSTATE *es)
+{
+    static const WCHAR empty_stringW[] = {0};
+
+    switch (msg)
+    {
+        case WM_IME_SETCONTEXT:
+            break;
+
+        case WM_IME_STARTCOMPOSITION:
+            /* 
+             * FIXME in IME: This message is not always sent like it should be
+             */
+            if (es->selection_start != es->selection_end)
+                EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
+            es->composition_start = es->selection_end;
+            es->composition_len = 0;
+            break;
+
+        case WM_IME_COMPOSITION:
+            if (es->composition_len == 0)
+            {
+                if (es->selection_start != es->selection_end)
+                    EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
+
+                es->composition_start = es->selection_end;
+            }
+            
+            EDIT_ImeComposition(hwnd,lParam,es);
+            break;
+
+        case WM_IME_ENDCOMPOSITION:
+            es->composition_len= 0;
+            break;
+
+        case WM_IME_COMPOSITIONFULL:
+            break;
+
+        case WM_IME_SELECT:
+            break;
+
+        case WM_IME_CONTROL:
+            break;
+
+        default:
+            return FALSE;
+    }
+
+    return TRUE;
+}
Index: dlls/user/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/user/Makefile.in,v
retrieving revision 1.102
diff -u -r1.102 Makefile.in
--- dlls/user/Makefile.in	21 Sep 2005 14:23:54 -0000	1.102
+++ dlls/user/Makefile.in	16 Dec 2005 13:34:06 -0000
@@ -6,6 +6,7 @@
 MODULE    = user32.dll
 IMPORTLIB = libuser32.$(IMPLIBEXT)
 IMPORTS   = gdi32 advapi32 kernel32 ntdll
+DELAYIMPORTS = imm32
 EXTRALIBS = $(LIBUNICODE)
 
 SPEC_SRCS16 = \


More information about the wine-patches mailing list