281 lines
9.7 KiB
Plaintext
281 lines
9.7 KiB
Plaintext
1. What is the difference between reading string (CompReadStr) and
|
|
composition string (CompStr)?
|
|
|
|
The reading string only consist with basic symbols for this IME.
|
|
For exmaple, the reading string of phonetic IME is bo po mo fo.
|
|
|
|
The composition string is the string that IME want to show in
|
|
the composition window. For example, the composition string of
|
|
phonetic IME can be the following for some advance phonetic
|
|
IME -
|
|
|
|
11111
|
|
012345678901234
|
|
我們都是大ㄕˊ
|
|
|
|
2. For this advance phonetic IME, what is the clause information?
|
|
我們 (offset 0 to 4 in this string), 都是 (offset 4 to 8), and
|
|
大ㄕˊ (offset 8 to 14) are three clauses to this IME. (It may
|
|
be different ways deciding a clause to other IME) And the clause
|
|
informtion to this IME is DWORD array 0, 4, 8, and 14.
|
|
|
|
3. What is the attribute for these three clause?
|
|
The attribute for one clause should be the same. For this IME,
|
|
我們 (ATTR_CONVERTED - 2), 都是 (ATTR_CONVERTED - 2), and 大ㄕˊ
|
|
(ATTR_INPUT - 0) can be marked with different attribute as
|
|
following.
|
|
|
|
我們都是大ㄕˊ
|
|
22222222000000
|
|
|
|
The attribute is BYE array 2,2,2,2,2,2,2,2,0,0,0,0,0,0.
|
|
|
|
4. When are the ATTR_TARGET_NOTCONVERTED and ATTR_TARGET_CONVERTED
|
|
used?
|
|
|
|
The _TARGET_ is for the clause which is in the cursor position.
|
|
|
|
Most likely the Chinese should use ATTR_TARGET_CONVERTED but it is
|
|
possible we use ATTR_TARGET_NOTCONVERTED for some prediction
|
|
function.
|
|
|
|
5. If the IME do not provide the correct information. The
|
|
application or DBCS pen window may fail to function.
|
|
|
|
6. On developing Win95 IME, we should alway keep in mind the
|
|
IME conversion engine must carefully seperate with the
|
|
IME UI. Because the advance applications may not use the UI
|
|
provided by the IME, they will draw the IME UI by themself.
|
|
So it is obvious to an IME designer, we should not put IME UI
|
|
window handle into PRIVCONTEXT (data structure in imedefs.h
|
|
of the IME sample code).
|
|
|
|
7. What is the basic message flow?
|
|
1).
|
|
Before application call into GetMessage/PeekMessage, the
|
|
system will call into ImeProcessKey asking whether the IME
|
|
want to eat this key.
|
|
2).
|
|
If the function return TRUE, the system will change the virtual
|
|
key to VK_PROCESSKEY, the application will get WM_KEYDOWN with
|
|
VK_PROCESSKEY.
|
|
3).
|
|
On application calling the TranslateMessage, the system will
|
|
pass the original virtual key and other parameters to the
|
|
ImeToAsciiEx(,lpdwTransBuf,).
|
|
4).
|
|
The IME is responsible to generate WM_IME_STARTCOMPOSITION,
|
|
WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_NOTIFY/
|
|
IMN_OPENCANDIDATE/IMN_CHANGECANIDATE/IMN_CLOSECANDIADTE, ...
|
|
into message buffer - lpdwTransBuf.
|
|
5).
|
|
The application can get these messages (generated by IME) from
|
|
the message loop. If the application do not handle it and
|
|
pass it to default window procedure, the system will pass it
|
|
to the default IME window. Now the IME window has chance to
|
|
handle it.
|
|
|
|
8. What is the IME window.
|
|
An IME window is a window created base on "IME" class. It is
|
|
a system class in user.exe. This window will create an onwee
|
|
window base on the UI class of an IME, system use the ImeInquire
|
|
to get this UI class name from the IME. This UI class provide
|
|
the real UI of the IME. The IME window will pass the UI related
|
|
messages to the ownee UI window and the UI window will provide
|
|
the UI feedback according to these messages for the end user.
|
|
|
|
9. How to switch to an IME or non IME in you application as the
|
|
CTRL-SPACE funtion?
|
|
// (1) switch to a non IME
|
|
hKL = GetKeyboardLayout(0);
|
|
|
|
if (ImmIsIME(hKL)) {
|
|
ImmSimulateHotKey(hAppWnd, IME_THOTKEY_IME_NONIME_TOGGLE);
|
|
}
|
|
|
|
if (ImmIsIME(hKL)) {
|
|
// switch fail handling
|
|
|
|
// P.S. If end user delete all non IMEs and only leave the IMEs
|
|
// in the system, above ImmSimulateHotKey will fail
|
|
}
|
|
|
|
// (2) switch to an IME
|
|
hKL = GetKeyboardLayout(0);
|
|
|
|
if (!ImmIsIME(hKL)) {
|
|
ImmSimulateHotKey(hAppWnd, IME_THOTKEY_IME_NONIME_TOGGLE);
|
|
}
|
|
|
|
if (!ImmIsIME(hKL)) {
|
|
// switch fail handling
|
|
|
|
// P.S. If end user delete all IMEs in the system, above
|
|
// ImmSimulateHotKey will fail
|
|
}
|
|
|
|
10. How to stick to the previos input IME for one specific field in
|
|
your application?
|
|
// Whenever focus out call ...
|
|
hPrevKL = GetKeyboardLayout(0);
|
|
:
|
|
:
|
|
// Whenever focus in call ...
|
|
ActivateKeyboradLayout(hPrevKL, 0);
|
|
|
|
11. How to change the direct switch hot key in IME configuration dialog
|
|
or control panel?
|
|
|
|
void ImeConfigureHotKeyPart(uNewModifiers, uNewVKey, hThisIMEKL)
|
|
{
|
|
DWORD dwHotKeyID;
|
|
DWORD dwAvailableHotKeyID = 0;
|
|
|
|
for (dwHotKeyID = IME_HOTKEY_DSWITCH_FIRST; dwHotKeyID <=
|
|
IME_HOTKEY_DSWITCH_LAST; dwHotKeyID++) {
|
|
BOOL fRet;
|
|
UINT uModifiers;
|
|
UINT uVKey;
|
|
UINT uTargetKL;
|
|
|
|
fRet = ImmGetHotKey(dwHotKeyID, &uModifiers, &uVKey,
|
|
&hTargetKL);
|
|
|
|
if (fRet) {
|
|
if (hTargetKL == hThisIMEKL) {
|
|
// if we want to set the hot key to a new value
|
|
ImmSetHotKey(dwHotKeyID, uNewModifiers, uNewVKey,
|
|
hThisIMEKL);
|
|
return;
|
|
}
|
|
} else if (dwAvailableHotKeyID) {
|
|
// we already find an available ID
|
|
} else {
|
|
dwAvailableHotKeyID = dwHotKeyID;
|
|
}
|
|
}
|
|
|
|
if (!dwAvailableHotKeyID) {
|
|
// no available ID case, Oh! Oh!
|
|
return;
|
|
}
|
|
|
|
// this IME does not have a direct switch hot key before
|
|
|
|
ImmSetHotKey(dwHotKeyID, uNewModifiers, uNewVKey, hThisIMEKL);
|
|
|
|
return;
|
|
}
|
|
|
|
12. An example of using property
|
|
1) Init code of RichEdit
|
|
|
|
hRichEditWnd->fdwProperty = ImmGetProperty(GetKeyboardLayout(0),
|
|
IGP_PROPERTY);
|
|
|
|
if (hRichEditWnd->fdwProperty & IME_PROP_SPECIAL_UI) {
|
|
// set composition position to init caret position
|
|
cpCompForm.dwStyle = CFS_POINT; // or CFS_RECT
|
|
cpCompForm.ptCurrentPos = ptAppCaretPosition;
|
|
// for CFS_RECT dwStyle you need to set cpCompForm.rcArea
|
|
|
|
ImmSetCompositionWindow(hIMC, cpCompForm);
|
|
} else if (hRichEditWnd->fdwProperty & IME_PROP_AT_CARET) {
|
|
// maybe application want to set the init position for
|
|
// candiadate window here
|
|
} else {
|
|
// set composition position to init caret position
|
|
cpCompForm.dwStyle = CFS_POINT; // or CFS_RECT
|
|
cpCompForm.ptCurrentPos = ptAppCaretPosition;
|
|
// for CFS_RECT dwStyle you need to set cpCompForm.rcArea
|
|
|
|
ImmSetCompositionWindow(hIMC, cpCompForm);
|
|
}
|
|
|
|
2) On message of keyboard layout change
|
|
|
|
case WM_INPUTLANGCHQANGE:
|
|
hRichEditWnd->fdwProperty = ImmGetProperty(GetKeyboardLayout(0),
|
|
IGP_PROPERTY);
|
|
|
|
hIMC = ImmGetContext(hRichEditWnd);
|
|
|
|
if (!hIMC) {
|
|
return DefWindowProc();
|
|
}
|
|
|
|
if (hRichEditWnd->fdwProperty & IME_PROP_SPECIAL_UI) {
|
|
// set composition position to init caret position
|
|
cpCompForm.dwStyle = CFS_POINT; // or CFS_RECT
|
|
cpCompForm.ptCurrentPos = ptAppCaretPosition;
|
|
// for CFS_RECT dwStyle you need to set cpCompForm.rcArea
|
|
|
|
ImmSetCompositionWindow(hIMC, cpCompForm);
|
|
} else if (hRichEditWnd->fdwProperty & IME_PROP_AT_CARET) {
|
|
// if you want to set up the candidate window position for
|
|
// an at caret IME, you may need to set here
|
|
|
|
// In the spec, we can set the 4 different candidate window
|
|
// positions for 4 level of candidate windows. Anyway the
|
|
// application may only care about the 1st level of candidate
|
|
// window
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
CANDIDATEFORM cdCandForm;
|
|
|
|
cdCandForm.dwIndex = 0;
|
|
cdCandForm.dwStyle = CFS_CANDIDATEPOS;
|
|
cdCandForm.ptCUrrentPos.x = ptAppWantPosition[i].x;
|
|
cdCandForm.ptCUrrentPos.y = ptAppWantPosition[i].y;
|
|
ImmSetCandidateWindow(hIMC, &cdCandForm);
|
|
}
|
|
} else {
|
|
for (i = 0; i < 4; i++) {
|
|
CANDIDATEFORM cdCandForm;
|
|
|
|
if (!ImmGetCandiadetWindow(hIMC, i, &cdCandForm)) {
|
|
contine;
|
|
}
|
|
|
|
if (cdCandForm.dwStyle == CFS_DEFAULT) {
|
|
contine;
|
|
}
|
|
|
|
cdCandForm.dwStyle = CFS_DEFAULT;
|
|
|
|
ImmSetCandidateWindow(hIMC, &cdCandForm);
|
|
}
|
|
|
|
cpCompForm.dwStyle = CFS_POINT; // or CFS_RECT
|
|
cpCompForm.ptCurrentPos = ptAppCaretPosition;
|
|
// for CFS_RECT dwStyle you also need to set cpCompForm.rcArea field
|
|
|
|
ImmSetCompositionWindow(hIMC, cpCompForm);
|
|
}
|
|
|
|
return DefWindowProc();
|
|
|
|
3) case (all IME related messages handle by RichEdit)
|
|
if (hRichEditWnd->fdwProperty & IME_PROP_SPECIAL_UI) {
|
|
return DefWindowProc();
|
|
} else if (hRichEditWnd->fdwProperty & IME_PROP_AT_CARET) {
|
|
} else {
|
|
return DefWindowProc();
|
|
}
|
|
|
|
// original IME enable code RichEdit already implement
|
|
|
|
4) On caret movement of RichEdit, RichEdit need to set the
|
|
COMPOSITIONFORM to this new caret position by ImmSetCompositionWindow
|
|
for a near caret IME.
|
|
|
|
And for an at caret Chinese IME you need to set the CANDIDATEFORM to
|
|
the composition cursor position by ImmSetCandidateWindow.
|
|
|
|
5) On end user change font, RichEdit need to set the LOGFONT by
|
|
ImmSetCompositionFont.
|
|
|
|
|
|
Thank you
|
|
J. J. Lee (李家鈞)
|