To build Chinese IME Samples, please just type NMAKE command
instead of BUILD command.

excutable binary files for phonetic IME -
   1. phon\phon.ime     (copy it to system directory)
   2. phon.tbl          (it is under system directory)
   3. phonptr.tbl       (it is under system directory)
   4. phoncode.tbl      (it is under system directory)

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), 大
   (offset 8 to 10), and ㄕˊ (oofset 10 to 14) are four 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, 10,
   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), 大
   (ATTR_CONVERTED - 2), and ㄕˊ (ATTR_INPUT - 0) can be marked
   with different attribute as following.

   我們都是大ㄕˊ
   22222222220000

   The attribute is BYE array 2,2,2,2,2,2,2,2,2,2,0,0,0,0.

4. When are the ATTR_CONVERTED and ATTR_TARGET_CONVERTED
   used?

   For example, end user keep typing -

               1
   0   4   8   2
   我們都是大食帶
   22222222222222

   and end user mark 大食帶 (on mark time, the attribute change to
   ATTR_TARGET_CONVERTED - 1)

                 1
   0   4   8     4
   我們都是大食帶
   22222222111111

   End user press enter key to reconvert and cursor might at the end
   of the sentence -

                 1
   0   4   8     4
   我們都是大時代
   22222222222222

   OK, only the clause with cursor could be ATTR_TARGET_CONVERTED.

5. If the IME do not provide the correct information. The IME aware
   application 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 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.

   5) On end user change font, RichEdit need to set the LOGFONT by
      ImmSetCompositionFont.


Thank you
   J. J. Lee (李家鈞)

我們都是大時代的兒女，共同為更美好的中文環境而奮鬥。
