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