Compui.pas
上传用户:wen198501
上传日期:2013-04-01
资源大小:335k
文件大小:25k
源码类别:

输入法编程

开发平台:

Delphi

  1.  {******************************************************}
  2.  {                                                      }
  3.  {    Copyright (c) 1990-1999 Microsoft Corporation     }
  4.  {                                                      }
  5.  {    Module Name:  Compui.c ->>  Compui.pas            }
  6.  {                                                      }
  7.  {    Translator:  Liu_mazi@126.com, 2005-11-17         }
  8.  {                                                      }
  9.  {******************************************************}
  10. unit Compui;
  11. {$I Define.Inc}
  12. interface
  13. uses Windows;
  14. function GetCompWnd(hUIWnd: hWnd): hWnd;  // 返回Composition窗口句柄
  15. function CompWndProc(hCompWnd: hWnd; uMsg: uInt; wParam: WParam; lParam: LParam): LResult; stdcall; // Composition窗口回调
  16. procedure ShowComp(hUIWnd: hWnd; nShowCompCmd: Integer);  // 显示隐藏Composition窗口
  17. procedure StartComp(hUIWnd: hWnd);  // 打开Composition窗口
  18. procedure EndComp(hUIWnd: hWnd);    // 隐藏Composition窗口
  19. procedure MoveDefaultCompPosition(hUIWnd: hWnd); // the default comp position need to near the caret
  20. implementation
  21. uses Messages, ImmDev, ImeDefs, Data, Ui, Uisubs, Statusui, Candui, LogText;
  22.   // 返回Composition窗口句柄
  23. function GetCompWnd(hUIWnd: hWnd): hWnd;
  24. var
  25.   hUIPrivate: HGlobal;
  26.   lpUIPrivate: PUIPriv;
  27. begin
  28.   Result := 0;
  29.   hUIPrivate := GetWindowLong(hUIWnd, IMMGWLP_PRIVATE);
  30.   if (hUIPrivate = 0) then Exit;     // can not darw candidate window
  31.   lpUIPrivate := GlobalLock(hUIPrivate);
  32.   if (lpUIPrivate = nil) then Exit;  // can not draw candidate window
  33.   Result := lpUIPrivate.hCompWnd;
  34.   GlobalUnlock(hUIPrivate);
  35. end;
  36.   // fit in lazy operation or not
  37. function FitInLazyOperation(lpptOrg, lpptNearCaret: PPoint; lprcInputRect: PRect; uEsc: uInt): Bool;
  38. var
  39.   ptDelta, ptTol: TPoint;
  40.   rcUIRect, rcInterRect: TRect;
  41. begin
  42.   Result := False;
  43.   ptDelta.x := lpptOrg.x - lpptNearCaret.x;
  44.   if (ptDelta.x < 0) then ptDelta.x := - ptDelta.x;
  45.   ptTol.x := sImeG.iParaTol * ncUIEsc[uEsc].iParaFacX + sImeG.iPerpTol * ncUIEsc[uEsc].iPerpFacX;
  46.   if (ptTol.x < 0) then ptTol.x := - ptTol.x;
  47.   if (ptDelta.x > ptTol.x) then Exit;
  48.   ptDelta.y := lpptOrg.y - lpptNearCaret.y;
  49.   if (ptDelta.y < 0) then ptDelta.y := - ptDelta.y;
  50.   ptTol.y := sImeG.iParaTol * ncUIEsc[uEsc].iParaFacY + sImeG.iPerpTol * ncUIEsc[uEsc].iPerpFacY;
  51.   if (ptTol.y < 0) then ptTol.y := - ptTol.y;
  52.   if (ptDelta.y > ptTol.y) then Exit;
  53.   // build up the UI rectangle (composition window)
  54.   rcUIRect.Left := lpptOrg.x;
  55.   rcUIRect.Top := lpptOrg.y;
  56.   rcUIRect.Right := rcUIRect.Left + sImeL.xCompWi;
  57.   rcUIRect.Bottom := rcUIRect.Top + sImeL.yCompHi;
  58.   if IntersectRect(rcInterRect, rcUIRect, lprcInputRect^) then Exit;
  59.   Result := True;
  60. end;
  61.   // decide a near caret position according to the caret position
  62. procedure GetNearCaretPosition(lpptFont: PPoint; uEsc, uRot: uInt; lpptCaret, lpptNearCaret: PPoint; fFlags: DWord);
  63. var
  64.   lFontSize, xWidthUI, yHeightUI, xBorder, yBorder: Integer;
  65. begin
  66.   if ((uEsc + uRot) and $0001) <> 0 then lFontSize := lpptFont.x else lFontSize := lpptFont.y;
  67.   xWidthUI  := sImeL.xCompWi;
  68.   yHeightUI := sImeL.yCompHi;
  69.   xBorder   := sImeL.cxCompBorder;
  70.   yBorder   := sImeL.cyCompBorder;
  71.   if (fFlags and NEAR_CARET_FIRST_TIME) <> 0 then
  72.   begin
  73.     lpptNearCaret.x := lpptCaret.x +
  74.       lFontSize * ncUIEsc[uEsc].iLogFontFacX +
  75.       sImeG.iPara * ncUIEsc[uEsc].iParaFacX +
  76.       sImeG.iPerp * ncUIEsc[uEsc].iPerpFacX;
  77.     if (ptInputEsc[uEsc].x >= 0) then
  78.       Inc(lpptNearCaret.x, xBorder * 2)
  79.     else
  80.       Dec(lpptNearCaret.x, xWidthUI - xBorder * 2);
  81.     lpptNearCaret.y := lpptCaret.y +
  82.       lFontSize * ncUIEsc[uEsc].iLogFontFacY +
  83.       sImeG.iPara * ncUIEsc[uEsc].iParaFacY +
  84.       sImeG.iPerp * ncUIEsc[uEsc].iPerpFacY;
  85.     if (ptInputEsc[uEsc].y >= 0) then
  86.       Inc(lpptNearCaret.y, yBorder * 2)
  87.     else
  88.       Dec(lpptNearCaret.y, yHeightUI - yBorder * 2);
  89.   end else
  90.   begin
  91.     lpptNearCaret.x := lpptCaret.x +
  92.       lFontSize * ncAltUIEsc[uEsc].iLogFontFacX +
  93.       sImeG.iPara * ncAltUIEsc[uEsc].iParaFacX +
  94.       sImeG.iPerp * ncAltUIEsc[uEsc].iPerpFacX;
  95.     if (ptAltInputEsc[uEsc].x >= 0) then
  96.       Inc(lpptNearCaret.x, xBorder * 2)
  97.     else
  98.       Dec(lpptNearCaret.x, xWidthUI - xBorder * 2);
  99.     lpptNearCaret.y := lpptCaret.y +
  100.       lFontSize * ncAltUIEsc[uEsc].iLogFontFacY +
  101.       sImeG.iPara * ncAltUIEsc[uEsc].iParaFacY +
  102.       sImeG.iPerp * ncAltUIEsc[uEsc].iPerpFacY;
  103.     if (ptAltInputEsc[uEsc].y >= 0) then
  104.       Inc(lpptNearCaret.y, yBorder * 2)
  105.     else
  106.       Dec(lpptNearCaret.y, yHeightUI - yBorder * 2);
  107.   end;
  108.   if (lpptNearCaret.x < sImeG.rcWorkArea.Left) then
  109.   begin
  110.     lpptNearCaret.x := sImeG.rcWorkArea.Left;
  111.   end else
  112.     if (lpptNearCaret.x + xWidthUI > sImeG.rcWorkArea.Right) then
  113.     begin
  114.       lpptNearCaret.x := sImeG.rcWorkArea.Right - xWidthUI;
  115.     end;
  116.   if (lpptNearCaret.y < sImeG.rcWorkArea.Top) then
  117.   begin
  118.     lpptNearCaret.y := sImeG.rcWorkArea.Top
  119.   end else
  120.     if (lpptNearCaret.y + yHeightUI > sImeG.rcWorkArea.Bottom) then
  121.     begin
  122.       lpptNearCaret.y := sImeG.rcWorkArea.Bottom - yHeightUI;
  123.     end;
  124. end;
  125.  // IME adjust position according to composition form
  126. function AdjustCompPosition(lpIMC: PInputContext; lpptOrg: PPoint; lpptNew: PPoint): Bool;
  127. var
  128.   ptNearCaret, ptOldNearCaret, ptFont: TPoint;
  129.   uEsc, uRot, uTmp: uInt;
  130.   rcUIRect, rcInputRect, rcInterRect: TRect;
  131. begin
  132.   Result := False;
  133.   if (lpIMC = nil) then Exit;
  134.   // we need to adjust according to font attribute
  135.   if (lpIMC.lfFont.A.lfWidth > 0) then
  136.     ptFont.x := lpIMC.lfFont.A.lfWidth * 2
  137.   else
  138.     if (lpIMC.lfFont.A.lfWidth < 0) then
  139.       ptFont.x := - lpIMC.lfFont.A.lfWidth * 2
  140.     else
  141.       if (lpIMC.lfFont.A.lfHeight > 0) then
  142.         ptFont.x := lpIMC.lfFont.A.lfHeight
  143.       else
  144.         if (lpIMC.lfFont.A.lfHeight < 0) then
  145.           ptFont.x := - lpIMC.lfFont.A.lfHeight
  146.         else
  147.           ptFont.x := sImeL.yCompHi;
  148.   if (lpIMC.lfFont.A.lfHeight > 0) then
  149.     ptFont.y := lpIMC.lfFont.A.lfHeight
  150.   else
  151.     if (lpIMC.lfFont.A.lfHeight < 0) then
  152.       ptFont.y := -lpIMC.lfFont.A.lfHeight
  153.     else
  154.       ptFont.y := ptFont.x;
  155.   // if the input char is too big, we don't need to consider so much
  156.   if (ptFont.x > sImeL.yCompHi * 8) then ptFont.x := sImeL.yCompHi * 8;
  157.   if (ptFont.y > sImeL.yCompHi * 8) then ptFont.y := sImeL.yCompHi * 8;
  158.   if (ptFont.x < sImeG.xChiCharWi) then ptFont.x := sImeG.xChiCharWi;
  159.   if (ptFont.y < sImeG.yChiCharHi) then ptFont.y := sImeG.yChiCharHi;
  160.   // -450 to 450 index 0
  161.   // 450 to 1350 index 1
  162.   // 1350 to 2250 index 2
  163.   // 2250 to 3150 index 3
  164.   uEsc := (lpIMC.lfFont.A.lfEscapement + 450) div 900 mod 4;
  165.   uRot := (lpIMC.lfFont.A.lfOrientation + 450) div 900 mod 4;
  166.   // decide the input rectangle
  167.   rcInputRect.Left := lpptNew.x;
  168.   rcInputRect.Top := lpptNew.y;
  169.   // build up an input rectangle from escapemment
  170.   rcInputRect.Right := rcInputRect.Left + ptFont.x * ptInputEsc[uEsc].x;
  171.   rcInputRect.Bottom := rcInputRect.Top + ptFont.y * ptInputEsc[uEsc].y;
  172.   // be a normal rectangle, not a negative rectangle
  173.   if (rcInputRect.Left > rcInputRect.Right) then
  174.   begin
  175.     uTmp := rcInputRect.Left;
  176.     rcInputRect.Left := rcInputRect.Right;
  177.     rcInputRect.Right := uTmp;
  178.   end;
  179.   if (rcInputRect.Top > rcInputRect.Bottom) then
  180.   begin
  181.     uTmp := rcInputRect.Top;
  182.     rcInputRect.Top := rcInputRect.Bottom;
  183.     rcInputRect.Bottom := uTmp;
  184.   end;
  185.   GetNearCaretPosition(@ptFont, uEsc, uRot, lpptNew, @ptNearCaret, NEAR_CARET_FIRST_TIME);
  186.   // 1st, use the adjust point build up the new suggest UI rectangle (composition window)
  187.   rcUIRect.Left := ptNearCaret.x;
  188.   rcUIRect.Top := ptNearCaret.y;
  189.   rcUIRect.Right := rcUIRect.Left + sImeL.xCompWi;
  190.   rcUIRect.Bottom := rcUIRect.Top + sImeL.yCompHi;
  191.   ptOldNearCaret := ptNearCaret;
  192.   // OK, no intersect between the near caret position and input char
  193.   if IntersectRect(rcInterRect, rcUIRect, rcInputRect) then
  194.   begin
  195.   end else
  196.     if FitInLazyOperation(lpptOrg, @ptNearCaret, @rcInputRect, uEsc) then
  197.     begin
  198.       Result := False;  // happy ending!!!, don't change position
  199.       Exit;
  200.     end else
  201.     begin
  202.       lpptOrg^ := ptNearCaret;
  203.       Result := True;  // happy ending!!
  204.       Exit;
  205.     end;
  206.   // unhappy case
  207.   GetNearCaretPosition(@ptFont, uEsc, uRot, lpptNew, @ptNearCaret, 0);
  208.   // build up the new suggest UI rectangle (composition window)
  209.   rcUIRect.Left := ptNearCaret.x;
  210.   rcUIRect.Top := ptNearCaret.y;
  211.   rcUIRect.Right := rcUIRect.Left + sImeL.xCompWi;
  212.   rcUIRect.Bottom := rcUIRect.Top + sImeL.yCompHi;
  213.   // OK, no intersect between the adjust position and input char
  214.   if IntersectRect(rcInterRect, rcUIRect, rcInputRect) then
  215.   begin
  216.   end else
  217.     if FitInLazyOperation(lpptOrg, @ptNearCaret, @rcInputRect, uEsc) then
  218.     begin
  219.       Result := False;
  220.       Exit;
  221.     end else
  222.     begin
  223.       lpptOrg^ := ptNearCaret;
  224.       Result := True;
  225.       Exit;
  226.     end;
  227.   lpptOrg^ := ptOldNearCaret;
  228.   Result := True;
  229. end;
  230.   // 设置Composition窗口位置
  231. procedure SetCompPosition(hCompWnd: hWnd; _hIMC: hIMC; lpIMC: PInputContext);
  232. var
  233.   ptWnd, ptSTWPos, ptNew: TPoint;
  234.   hCandWnd: hWnd;
  235.   fChange: Bool; // = FALSE
  236.   Comp_CandWndLen: Integer;
  237. begin
  238.   fChange := False;
  239.   // Composition窗口当前位置
  240.   ptWnd.x := 0;
  241.   ptWnd.y := 0;
  242.   ClientToScreen(hCompWnd, ptWnd);
  243.   Dec(ptWnd.x, sImeL.cxCompBorder);
  244.   Dec(ptWnd.y, sImeL.cyCompBorder);
  245.   // 光标跟随 = False
  246.   if (sImeG.IC_Trace = 0) then
  247.   begin
  248.     ImmGetStatusWindowPos(_hIMC, @ptSTWPos);
  249.     // reset status window for LINE_UI(FIX_UI)
  250.     if (uStartComp <> 0) then
  251.     begin
  252.       Comp_CandWndLen := 0;
  253.       Inc(Comp_CandWndLen, sImeL.xCompWi + UI_MARGIN);
  254.       if (uOpenCand <> 0) then Inc(Comp_CandWndLen, sImeG.xCandWi + UI_MARGIN);
  255.       if (ptSTWPos.x + sImeG.xStatusWi + Comp_CandWndLen > sImeG.rcWorkArea.Right) then
  256.         ptSTWPos.x := sImeG.rcWorkArea.Right - sImeG.xStatusWi - Comp_CandWndLen;
  257.       SetWindowPos(GetStatusWnd(GetWindow(hCompWnd, GW_OWNER)), 0, ptSTWPos.x,
  258.         ptSTWPos.y, 0, 0, SWP_NOACTIVATE or SWP_NOCOPYBITS or SWP_NOSIZE or SWP_NOZORDER);
  259.       ImmSetStatusWindowPos(_hIMC, PPoint(@ptSTWPos));
  260.     end;
  261.     ptWnd.x := ptSTWPos.x + sImeG.xStatusWi + UI_MARGIN;
  262.     ptWnd.y := ptSTWPos.y;
  263.     lpIMC.cfCompForm.ptCurrentPos := ptWnd;
  264.     ScreenToClient(lpIMC.hWnd, lpIMC.cfCompForm.ptCurrentPos);
  265.     fChange := True;
  266.   end else
  267.     if ((lpIMC.cfCompForm.dwStyle and CFS_FORCE_POSITION) <> 0) then
  268.     begin
  269.       ptNew.x := lpIMC.cfCompForm.ptCurrentPos.x;
  270.       ptNew.y := lpIMC.cfCompForm.ptCurrentPos.y;
  271.       ClientToScreen(lpIMC.hWnd, ptNew);
  272.       if (ptWnd.x <> ptNew.x) then
  273.       begin
  274.         ptWnd.x := ptNew.x;
  275.         fChange := True;
  276.       end;
  277.       if (ptWnd.y <> ptNew.y) then
  278.       begin
  279.         ptWnd.y := ptNew.y;
  280.         fChange := True;
  281.       end;
  282.       if (fChange) then
  283.       begin
  284.         Dec(ptWnd.x, sImeL.cxCompBorder);
  285.         Dec(ptWnd.y, sImeL.cyCompBorder);
  286.       end;
  287.     end else
  288.       if (lpIMC.cfCompForm.dwStyle <> CFS_DEFAULT) then
  289.       begin
  290.         ptNew.x := lpIMC.cfCompForm.ptCurrentPos.x;
  291.         ptNew.y := lpIMC.cfCompForm.ptCurrentPos.y;
  292.         ClientToScreen(lpIMC.hWnd, ptNew);
  293.         fChange := AdjustCompPosition(lpIMC, @ptWnd, @ptNew);
  294.       end else
  295.       begin
  296.         ImmGetStatusWindowPos(_hIMC, @ptSTWPos);
  297.         
  298.         ptNew.x := ptSTWPos.x + sImeG.xStatusWi + UI_MARGIN;
  299.         if (ptNew.x + sImeL.xCompWi + UI_MARGIN + sImeG.xCandWi >= sImeG.rcWorkArea.Right) then
  300.         begin
  301.           ptNew.x := ptSTWPos.x - sImeL.xCompWi - UI_MARGIN;
  302.         end;
  303.         ptNew.y := ptSTWPos.y;
  304.         if (ptWnd.x <> ptNew.x) then
  305.         begin
  306.           ptWnd.x := ptNew.x;
  307.           fChange := True;
  308.         end;
  309.         if (ptWnd.y <> ptNew.y) then
  310.         begin
  311.           ptWnd.y := ptNew.y;
  312.           fChange := True;
  313.         end;
  314.         if (fChange) then
  315.         begin
  316.           lpIMC.cfCompForm.ptCurrentPos := ptNew;
  317.           ScreenToClient(lpIMC.hWnd, lpIMC.cfCompForm.ptCurrentPos);
  318.         end;
  319.       end;
  320.   if (fChange = False) then Exit; // 没有变化
  321.   SetWindowPos(hCompWnd, 0, ptWnd.x, ptWnd.y, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOZORDER);
  322.   // 调整Candidate窗口位置 ..
  323.   hCandWnd := GetCandWnd(GetWindow(hCompWnd, GW_OWNER));
  324.   if IsWindow(hCandWnd) then Exit;
  325.   CalcCandPos(_hIMC, GetWindow(hCompWnd, GW_OWNER), @ptWnd);
  326.   ScreenToClient(lpIMC.hWnd, ptWnd);
  327.   lpIMC.cfCandForm[0].dwStyle := CFS_CANDIDATEPOS;
  328.   lpIMC.cfCandForm[0].ptCurrentPos := ptWnd;
  329.   if (IsWindowVisible(hCandWnd) = False) then Exit;
  330.   PostMessage(hCandWnd, WM_IME_NOTIFY, IMN_SETCANDIDATEPOS, 1);
  331. end;
  332.   // IMN_SETCOMPOSITIONWINDOW处理
  333. procedure SetCompWindow(hCompWnd: hWnd);
  334. var
  335.   _hIMC: hIMC;
  336.   lpIMC: PInputContext;
  337.   hUIWnd: hWnd;
  338. begin
  339.   hUIWnd := GetWindow(hCompWnd, GW_OWNER);
  340.   if (IsWindow(hUIWnd) = False) then Exit;
  341.   _hIMC := GetWindowLong(hUIWnd, IMMGWLP_IMC);
  342.   if (_hIMC = 0) then Exit;
  343.   lpIMC := ImmLockIMC(_hIMC);
  344.   if (lpIMC = nil) then Exit;
  345.   SetCompPosition(hCompWnd, _hIMC, lpIMC);
  346.   ImmUnlockIMC(_hIMC);
  347. end;
  348.   // the default comp position need to near the caret
  349. procedure MoveDefaultCompPosition(hUIWnd: hWnd);
  350. var
  351.   _hIMC: hIMC;
  352.   lpIMC: PInputContext;
  353.   hCompWnd: hWnd;
  354. begin
  355.   hCompWnd := GetCompWnd(hUIWnd);
  356.   if (IsWindow(hCompWnd) = False) then Exit;
  357.   _hIMC := GetWindowLong(hUIWnd, IMMGWLP_IMC);
  358.   if (_hIMC = 0) then Exit;
  359.   lpIMC := ImmLockIMC(_hIMC);
  360.   if (lpIMC = nil) then Exit;
  361.   if ((lpIMC.cfCompForm.dwStyle and CFS_FORCE_POSITION) = 0) then
  362.   begin
  363.     SetCompPosition(hCompWnd, _hIMC, lpIMC);
  364.   end;
  365.   ImmUnlockIMC(_hIMC);
  366. end;
  367.   // 显示隐藏Composition窗口
  368. procedure ShowComp(hUIWnd: hWnd; nShowCompCmd: Integer);
  369. var
  370.   hUIPrivate: HGlobal;
  371.   lpUIPrivate: PUIPriv;
  372. begin
  373.   hUIPrivate := GetWindowLong(hUIWnd, IMMGWLP_PRIVATE);
  374.   if (hUIPrivate = 0) then Exit;
  375.   lpUIPrivate := GlobalLock(hUIPrivate);
  376.   if (lpUIPrivate = nil) then Exit;
  377.   if (lpUIPrivate.nShowCompCmd = nShowCompCmd) then Exit; // **
  378.   if (nShowCompCmd = SW_HIDE) then
  379.     lpUIPrivate.fdwSetContext := lpUIPrivate.fdwSetContext and (not ISC_HIDE_COMP_WINDOW);
  380.   if IsWindow(lpUIPrivate.hCompWnd) then
  381.   begin
  382.     if (nShowCompCmd = SW_HIDE) then uStartComp := 0 else uStartComp := 1;
  383.     ShowWindow(lpUIPrivate.hCompWnd, nShowCompCmd);
  384.     lpUIPrivate.nShowCompCmd := nShowCompCmd;
  385.   end;
  386.   GlobalUnlock(hUIPrivate);
  387. end;
  388.   // "打开"Composition窗口
  389. procedure StartComp(hUIWnd: hWnd);
  390. var
  391.   _hIMC: hIMC;
  392.   hUIPrivate: HGlobal;
  393.   lpIMC: PInputContext;
  394.   lpUIPrivate: PUIPriv;
  395. begin
  396.   _hIMC := GetWindowLong(hUIWnd, IMMGWLP_IMC);
  397.   if (_hIMC = 0) then Exit;
  398.   hUIPrivate := GetWindowLong(hUIWnd, IMMGWLP_PRIVATE);
  399.   if (hUIPrivate = 0) then Exit;
  400.   lpIMC := ImmLockIMC(_hIMC);
  401.   if (lpIMC = nil) then Exit;
  402.   lpUIPrivate := GlobalLock(hUIPrivate);
  403.   if (lpUIPrivate = nil) then // can not draw composition window
  404.   begin
  405.     ImmUnlockIMC(_hIMC);
  406.     Exit;
  407.   end;
  408.   lpUIPrivate.fdwSetContext := lpUIPrivate.fdwSetContext or ISC_SHOWUICOMPOSITIONWINDOW;
  409.   if (IsWindow(lpUIPrivate.hCompWnd) = False) then // 窗口不存在则建立
  410.   begin
  411.     lpUIPrivate.hCompWnd := CreateWindowEx(WS_EX_WINDOWEDGE or WS_EX_DLGMODALFRAME, szCompClassName,
  412.       nil, WS_POPUP or WS_DISABLED, 0, 0, sImeL.xCompWi, sImeL.yCompHi, hUIWnd, 0, HInstance, nil);
  413.     SetWindowLong(lpUIPrivate.hCompWnd, UI_MOVE_OFFSET, WINDOW_NOT_DRAG);
  414.     SetWindowLong(lpUIPrivate.hCompWnd, UI_MOVE_XY, sImeL.nMaxKey);
  415.   end;
  416.   uStartComp := 1;
  417.   // try to set the position of composition UI window near the caret
  418.   SetCompPosition(lpUIPrivate.hCompWnd, _hIMC, lpIMC);
  419.   ImmUnlockIMC(_hIMC);
  420.   ShowComp(hUIWnd, SW_SHOWNOACTIVATE);
  421.   GlobalUnlock(hUIPrivate);
  422. end;
  423.   // "关闭"Composition窗口
  424. procedure EndComp(hUIWnd: hWnd);
  425. begin
  426.   ShowComp(hUIWnd, SW_HIDE);
  427. end;
  428.   // WM_DESTROY消息处理
  429. procedure DestroyCompWindow(hCompWnd: hWnd);
  430. var
  431.   hUIPrivate: HGlobal;
  432.   lpUIPrivate: PUIPriv;
  433. begin
  434.   if (GetWindowLong(hCompWnd, UI_MOVE_OFFSET) <> WINDOW_NOT_DRAG) then // undo the drag border
  435.     DrawDragBorder(hCompWnd, GetWindowLong(hCompWnd, UI_MOVE_XY), GetWindowLong(hCompWnd, UI_MOVE_OFFSET));
  436.   hUIPrivate := GetWindowLong(GetWindow(hCompWnd, GW_OWNER), IMMGWLP_PRIVATE);
  437.   if (hUIPrivate = 0) then Exit;
  438.   lpUIPrivate := GlobalLock(hUIPrivate);
  439.   if (lpUIPrivate = nil) then Exit;
  440.   lpUIPrivate.nShowCompCmd := SW_HIDE;
  441.   lpUIPrivate.hCompWnd := 0;
  442.   GlobalUnlock(hUIPrivate);
  443. end;
  444.   // WM_SETCURSOR消息处理
  445. procedure CompSetCursor(hCompWnd: hWnd; lParam: LParam);
  446. var
  447.   ptCursor: TPoint;
  448.   rcWnd: TRect;
  449. begin
  450.   SetCursor(LoadCursor(0, IDC_SIZEALL));
  451.   if (GetWindowLong(hCompWnd, UI_MOVE_OFFSET) <> WINDOW_NOT_DRAG) then Exit;
  452.   if (HiWord(lParam) <> WM_LBUTTONDOWN) then Exit;
  453.   // start dragging ..
  454.   SystemParametersInfo(SPI_GETWORKAREA, 0, @sImeG.rcWorkArea, 0);
  455.   SetCapture(hCompWnd);
  456.   GetCursorPos(ptCursor);
  457.   SetWindowLong(hCompWnd, UI_MOVE_XY, MakeLong(ptCursor.x, ptCursor.y));
  458.   GetWindowRect(hCompWnd, rcWnd);
  459.   SetWindowLong(hCompWnd, UI_MOVE_OFFSET, MakeLong(ptCursor.x - rcWnd.Left, ptCursor.y - rcWnd.Top));
  460.   DrawDragBorder(hCompWnd, MakeLong(ptCursor.x, ptCursor.y), GetWindowLong(hCompWnd, UI_MOVE_OFFSET));
  461. end;
  462.   // WM_LBUTTONUP消息处理
  463. function CompButtonUp(hCompWnd: hWnd): Bool;
  464. var
  465.   lTmpCursor, lTmpOffset: DWord;
  466.   pt: TPoint;
  467.   hUIWnd: hWnd;
  468.   _hIMC: hIMC;
  469.   lpIMC: PInputContext;
  470. begin
  471.   Result := False;
  472.   if (GetWindowLong(hCompWnd, UI_MOVE_OFFSET) = WINDOW_NOT_DRAG) then Exit; // 非拖拽状态
  473.   lTmpCursor := GetWindowLong(hCompWnd, UI_MOVE_XY);
  474.   lTmpOffset := GetWindowLong(hCompWnd, UI_MOVE_OFFSET);
  475.   // calculate the org by the offset
  476.   pt.x := PSmallPoint(@lTmpCursor).x - PSmallPoint(@lTmpOffset).x;
  477.   pt.y := PSmallPoint(@lTmpCursor).y - PSmallPoint(@lTmpOffset).y;
  478.   DrawDragBorder(hCompWnd, lTmpCursor, lTmpOffset);
  479.   SetWindowLong(hCompWnd, UI_MOVE_OFFSET, WINDOW_NOT_DRAG);
  480.   SetWindowLong(hCompWnd, UI_MOVE_XY, sImeL.nMaxKey);
  481.   ReleaseCapture();
  482.   hUIWnd := GetWindow(hCompWnd, GW_OWNER);
  483.   if (IsWindow(hUIWnd) = False) then Exit;
  484.   _hIMC := GetWindowLong(hUIWnd, IMMGWLP_IMC);
  485.   if (_hIMC = 0) then Exit;
  486.   lpIMC := ImmLockIMC(_hIMC);
  487.   if (lpIMC = nil) then Exit;
  488.   if (pt.x < sImeG.rcWorkArea.Left) then
  489.   begin
  490.     pt.x := sImeG.rcWorkArea.Left;
  491.   end else
  492.     if (pt.x + sImeL.xCompWi > sImeG.rcWorkArea.Right) then
  493.     begin
  494.       pt.x := sImeG.rcWorkArea.Right - sImeL.xCompWi;
  495.     end;
  496.   if (pt.y < sImeG.rcWorkArea.Top) then
  497.   begin
  498.     pt.y := sImeG.rcWorkArea.Top;
  499.   end else
  500.     if (pt.y + sImeL.yCompHi > sImeG.rcWorkArea.Bottom) then
  501.     begin
  502.       pt.y := sImeG.rcWorkArea.Bottom - sImeL.yCompHi;
  503.     end;
  504.   lpIMC.cfCompForm.dwStyle := CFS_FORCE_POSITION;
  505.   lpIMC.cfCompForm.ptCurrentPos.x := pt.x + sImeL.cxCompBorder;
  506.   lpIMC.cfCompForm.ptCurrentPos.y := pt.y + sImeL.cyCompBorder;
  507.   ScreenToClient(lpIMC.hWnd, lpIMC.cfCompForm.ptCurrentPos);
  508.   ImmUnlockIMC(_hIMC);
  509.   // set composition window to the new poosition
  510.   PostMessage(hCompWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONWINDOW, 0);
  511.   
  512.   Result := True;
  513. end;
  514.   // 绘制Composition窗口
  515. procedure PaintCompWindow(hUIWnd: hWnd; hCompWnd: hWnd; _hDC: hDC);
  516. var
  517.   _hIMC: hIMC;
  518.   lpIMC: PInputContext;
  519.   hOldFont: HGdiObj;
  520.   lpCompStr: PCompositionString;
  521.   lpGuideLine: PGuideLine;
  522.   fShowString: DWord;
  523.   lfFont: TLogFont;
  524.   rcWnd: TRect;
  525. begin
  526.   _hIMC := GetWindowLong(hUIWnd, IMMGWLP_IMC);
  527.   if (_hIMC = 0) then Exit;
  528.   lpIMC := ImmLockIMC(_hIMC);
  529.   if (lpIMC = nil) then Exit;
  530.   if (sImeG.fDiffSysCharSet) then
  531.   begin
  532.     hOldFont := GetCurrentObject(_hDC, OBJ_FONT);
  533.     ZeroMemory(@lfFont, SizeOf(lfFont));
  534.     lfFont.lfHeight := - MulDiv(12, GetDeviceCaps(_hDC, LOGPIXELSY), 72);
  535.     lfFont.lfCharSet := NATIVE_CHARSET;
  536.     lStrCpy(lfFont.lfFaceName, '宋体');
  537.     SelectObject(_hDC, CreateFontIndirect(lfFont));
  538.   end else
  539.     hOldFont := 0;
  540.   lpCompStr := ImmLockIMCC(lpIMC.hCompStr);
  541.   if (lpCompStr = nil) then Exit;
  542.   lpGuideLine := ImmLockIMCC(lpIMC.hGuideLine);
  543.   if (lpGuideLine = nil) then Exit;
  544.   // 凹陷边框
  545.   GetClientRect(hCompWnd, rcWnd);
  546.   DrawConcaveRect(_hDC, rcWnd.Left, rcWnd.Top, rcWnd.Right - 1, rcWnd.Bottom - 1);
  547.   SetBkColor(_hDC, RGB($C0, $C0, $C0));
  548.   fShowString := 0;
  549.   if (lpGuideLine = nil) then
  550.   begin
  551.   end else
  552.     if (lpGuideLine.dwLevel = GL_LEVEL_NOGUIDELINE) then
  553.     begin
  554.     end else
  555.       if (lpGuideLine.dwStrLen = 0) then
  556.       begin
  557.         if (lpGuideLine.dwLevel = GL_LEVEL_ERROR) then
  558.         begin
  559.           fShowString := fShowString or IME_STR_ERROR;
  560.         end;
  561.       end else
  562.       begin
  563.         // if there is information string, we will show the information string
  564.         if (lpGuideLine.dwLevel = GL_LEVEL_ERROR) then
  565.         begin
  566.           SetTextColor(_hDC, RGB($FF, 0, 0));    // red text for error
  567.           SetBkColor(_hDC, RGB($80, $80, $80));  // light gray background for error
  568.         end;
  569.         ExtTextOut(_hDC, sImeL.rcCompText.Left, sImeL.rcCompText.Top, ETO_OPAQUE, @sImeL.rcCompText,
  570.           PChar(DWord(lpGuideLine) + lpGuideLine.dwStrOffset), lpGuideLine.dwStrLen, nil);
  571.           
  572.         fShowString := fShowString or IME_STR_SHOWED; // 已显示
  573.       end;
  574.   if (fShowString and IME_STR_SHOWED) <> 0 then
  575.   begin
  576.     // already show it, don't need to show
  577.   end else
  578.     if (lpCompStr.dwCompStrLen > 0) then
  579.     begin
  580.       ExtTextOut(_hDC, sImeL.rcCompText.Left, sImeL.rcCompText.Top, ETO_OPAQUE, @sImeL.rcCompText, nil, 0, nil);
  581.       DrawText(_hDC, PChar(DWord(lpCompStr) + lpCompStr.dwCompStrOffset),
  582.         lpCompStr.dwCompStrLen, sImeL.rcCompText, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
  583.       if (fShowString and IME_STR_ERROR) <> 0 then
  584.       begin
  585.         // red text for error
  586.         SetTextColor(_hDC, RGB($FF, 0, 0));
  587.         // light gray background for error
  588.         SetBkColor(_hDC, RGB($80, $80, $80));
  589.         ExtTextOut(_hDC, sImeL.rcCompText.Left + Integer(lpCompStr.dwCursorPos) * sImeG.xChiCharWi div 2,
  590.           sImeL.rcCompText.Top, ETO_CLIPPED, @sImeL.rcCompText,
  591.           PChar(DWord(lpCompStr) + lpCompStr.dwCompStrOffset + lpCompStr.dwCursorPos),
  592.           lpCompStr.dwCompStrLen - lpCompStr.dwCursorPos, nil);
  593.       end else
  594.         if (lpCompStr.dwCursorPos < lpCompStr.dwCompStrLen) then
  595.         begin
  596.           // light gray background for cursor start
  597.           SetBkColor(_hDC, RGB($80, $80, $80));
  598.           ExtTextOut(_hDC, sImeL.rcCompText.Left + Integer(lpCompStr.dwCursorPos) * sImeG.xChiCharWi div 2,
  599.             sImeL.rcCompText.Top, ETO_CLIPPED, @sImeL.rcCompText,
  600.             PChar(DWord(lpCompStr) + lpCompStr.dwCompStrOffset + lpCompStr.dwCursorPos),
  601.             lpCompStr.dwCompStrLen - lpCompStr.dwCursorPos, nil);
  602.         end else
  603.         begin
  604.         end;
  605.     end else
  606.     begin
  607.       ExtTextOut(_hDC, sImeL.rcCompText.Left, sImeL.rcCompText.Top, ETO_OPAQUE, @sImeL.rcCompText, nil, 0, nil);
  608.     end;
  609.   if (sImeG.fDiffSysCharSet) then DeleteObject(SelectObject(_hDC, hOldFont));
  610.   ImmUnlockIMCC(lpIMC.hGuideLine);
  611.   ImmUnlockIMCC(lpIMC.hCompStr);
  612.   ImmUnlockIMC(_hIMC);
  613. end;
  614.   // Composition窗口回调
  615. function CompWndProc(hCompWnd: hWnd; uMsg: uInt; wParam: WParam; lParam: LParam): LResult; stdcall;
  616. var
  617.   ptCursor: TPoint;
  618.   _hDC: hDC;
  619.   ps: TPaintStruct;
  620. begin
  621.   Result := 0;
  622.   case (uMsg) of
  623.     WM_DESTROY:
  624.       DestroyCompWindow(hCompWnd);
  625.     WM_SETCURSOR:
  626.       CompSetCursor(hCompWnd, lParam);
  627.     WM_MOUSEMOVE:
  628.       if (GetWindowLong(hCompWnd, UI_MOVE_OFFSET) <> WINDOW_NOT_DRAG) then // 拖拽中
  629.       begin
  630.         DrawDragBorder(hCompWnd, GetWindowLong(hCompWnd, UI_MOVE_XY), GetWindowLong(hCompWnd, UI_MOVE_OFFSET));
  631.         GetCursorPos(ptCursor);
  632.         SetWindowLong(hCompWnd, UI_MOVE_XY, MakeLong(ptCursor.x, ptCursor.y));
  633.         DrawDragBorder(hCompWnd, MakeLong(ptCursor.x, ptCursor.y), GetWindowLong(hCompWnd, UI_MOVE_OFFSET));
  634.       end else
  635.         Result := DefWindowProc(hCompWnd, uMsg, wParam, lParam);
  636.     WM_LBUTTONUP:
  637.       if (CompButtonUp(hCompWnd) = False) then Result := DefWindowProc(hCompWnd, uMsg, wParam, lParam);
  638.     WM_IME_NOTIFY:
  639.       if (wParam = IMN_SETCOMPOSITIONWINDOW) then SetCompWindow(hCompWnd);
  640.     WM_PAINT:
  641.     begin
  642.       _hDC := BeginPaint(hCompWnd, ps);
  643.       PaintCompWindow(GetWindow(hCompWnd, GW_OWNER), hCompWnd, _hDC);
  644.       EndPaint(hCompWnd, ps);
  645.     end;
  646.     WM_MOUSEACTIVATE:
  647.       Result := MA_NOACTIVATE;
  648.     else
  649.       Result := DefWindowProc(hCompWnd, uMsg, wParam, lParam);
  650.   end;
  651. end;
  652. end.