unit LazSynIMM; {$mode objfpc}{$H+} {off $DEFINE WinIMEDebug} {off $DEFINE WinIMEFullDeferOverwrite} // In Full IME do "overwrite selecton" when IME finishes (normally done at start) {off $DEFINE WinIMEFullOverwriteSkipUndo} // In Full IME undo "overwrite selecton", if IME cancelled interface uses windows, imm, LazSynIMMBase, Classes, SysUtils, Controls, LazLoggerBase, LCLType, LazUTF8, Graphics, SynEditMiscClasses, SynTextDrawer, SynEditPointClasses, SynEditMarkupSelection, SynEditMarkup, SynEditTypes, SynEditKeyCmds, LazSynEditText, SynEditTextBase; type { LazSynWinIme } LazSynWinIme = class(LazSynIme) protected procedure StopIme(Success: Boolean); override; end; { LazSynImeSimple } LazSynImeSimple = class(LazSynWinIme) private FImeBlockSelection: TSynEditSelection; FImeWinX, FImeWinY: Integer; FTextDrawer: TheTextDrawer; procedure SetTextDrawer(AValue: TheTextDrawer); procedure UpdateImeWinXY(aX, aY: Integer; aImc: HIMC = 0; aForce: Boolean = False); procedure UpdateImeWinFont(aImc: HIMC = 0); procedure DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges); procedure DoDrawerFontChanged(Sender: TObject); procedure DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); procedure DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); public constructor Create(AOwner: TSynEditBase); destructor Destroy; override; procedure WMImeNotify(var Msg: TMessage); override; procedure WMImeComposition(var Msg: TMessage); override; procedure WMImeStartComposition(var Msg: TMessage); override; procedure WMImeEndComposition(var Msg: TMessage); override; procedure FocusKilled; override; property TextDrawer: TheTextDrawer read FTextDrawer write SetTextDrawer; end; { LazSynImeFull } LazSynImeFull = class(LazSynWinIme) private FAdjustLeftCharForTargets: Boolean; FLeftPosForTarget, FRightPosForTarget: Integer; FImeBlockSelection, FImeBlockSelection2, FImeBlockSelection3: TSynEditSelection; // TODO: create a custom markup FImeMarkupSelection, FImeMarkupSelection2, FImeMarkupSelection3: TSynEditMarkupSelection; FInImeMsg: Boolean; {$IFnDEF WinIMEFullOverwriteSkipUndo} FUndoStamp1, FUndoStamp2: TSynEditUndoGroup; FNeedUndoOnCancel: Boolean; {$ENDIF} {$IFDEF WinIMEFullDeferOverwrite} FHasPersistLock: Boolean; {$ENDIF} FHadQueryCharPos: boolean; procedure SetImeTempText(const s: string); procedure DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); procedure DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges); procedure EnsureLeftChar; protected procedure StopIme(Success: Boolean); override; public constructor Create(AOwner: TSynEditBase); destructor Destroy; override; procedure WMImeRequest(var Msg: TMessage); override; procedure WMImeNotify(var Msg: TMessage); override; procedure WMImeComposition(var Msg: TMessage); override; procedure WMImeStartComposition(var Msg: TMessage); override; procedure WMImeEndComposition(var Msg: TMessage); override; procedure FocusKilled; override; public property AdjustLeftCharForTargets: Boolean read FAdjustLeftCharForTargets write FAdjustLeftCharForTargets; end; implementation uses SynEdit; { LazSynWinIme } procedure LazSynWinIme.StopIme(Success: Boolean); var imc: HIMC; begin if (not FInCompose) or (not FriendEdit.HandleAllocated) then exit; imc := ImmGetContext(FriendEdit.Handle); if (imc <> 0) then begin if Success then ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0) else ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ImmReleaseContext(FriendEdit.Handle, imc); end; inherited StopIme(Success); end; { LazSynImeSimple } procedure LazSynImeSimple.DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges); begin UpdateImeWinXY(FriendEdit.CaretXPix, FriendEdit.CaretYPix); if Changes * [scCaretX, scCaretY] <> [] then StopIme(False); end; procedure LazSynImeSimple.DoDrawerFontChanged(Sender: TObject); var imc: HIMC; begin if not FriendEdit.HandleAllocated then exit; imc := ImmGetContext(FriendEdit.Handle); if (imc <> 0) then begin UpdateImeWinFont(imc); UpdateImeWinXY(FriendEdit.CaretXPix, FriendEdit.CaretYPix, imc); ImmReleaseContext(FriendEdit.Handle, imc); end; end; procedure LazSynImeSimple.DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); begin StopIme(True); end; procedure LazSynImeSimple.DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin StopIme(True); end; procedure LazSynImeSimple.UpdateImeWinXY(aX, aY: Integer; aImc: HIMC; aForce: Boolean); var cf: CompositionForm; imc: HIMC; begin if not FriendEdit.HandleAllocated then exit; if (not aForce) and (aX = FImeWinX) and (aY = FImeWinY) then exit; FImeWinX := aX; FImeWinY := aY; cf.dwStyle := CFS_POINT; cf.ptCurrentPos := Point(aX, aY); if aImc = 0 then imc := ImmGetContext(FriendEdit.Handle) else imc := aImc; if (imc <> 0) then begin ImmSetCompositionWindow(imc, @cf); if (aImc = 0) then ImmReleaseContext(FriendEdit.Handle, imc); end; end; procedure LazSynImeSimple.SEtTextDrawer(AValue: TheTextDrawer); begin if FTextDrawer = AValue then Exit; if FTextDrawer <> nil then FTextDrawer.UnRegisterOnFontChangeHandler(@DoDrawerFontChanged); FTextDrawer := AValue; if FTextDrawer <> nil then FTextDrawer.RegisterOnFontChangeHandler(@DoDrawerFontChanged); end; procedure LazSynImeSimple.UpdateImeWinFont(aImc: HIMC); var imc: HIMC; logFont: TLogFont; begin if not FriendEdit.HandleAllocated then exit; if aImc = 0 then imc := ImmGetContext(FriendEdit.Handle) else imc := aImc; if (imc <> 0) then begin GetObject(FriendEdit.Font.Handle, SizeOf(TLogFont), @logFont); ImmSetCompositionFontA(imc, @logFont); if (aImc = 0) then ImmReleaseContext(FriendEdit.Handle, imc); end; end; constructor LazSynImeSimple.Create(AOwner: TSynEditBase); begin inherited Create(AOwner); FImeBlockSelection := TSynEditSelection.Create(ViewedTextBuffer, False); FImeBlockSelection.InvalidateLinesMethod := @InvalidateLines; FriendEdit.RegisterStatusChangedHandler(@DoStatusChanged, [scCaretX, scCaretY, scLeftChar, scTopLine, scModified]); FriendEdit.RegisterCommandHandler(@DoOnCommand, nil, [hcfInit]); FriendEdit.RegisterBeforeMouseDownHandler(@DoOnMouse); end; destructor LazSynImeSimple.Destroy; begin TextDrawer := nil; FreeAndNil(FImeBlockSelection); FriendEdit.UnregisterBeforeMouseDownHandler(@DoOnMouse); FriendEdit.UnregisterCommandHandler(@DoOnCommand); FriendEdit.UnRegisterStatusChangedHandler(@DoStatusChanged); inherited Destroy; end; procedure LazSynImeSimple.WMImeNotify(var Msg: TMessage); var imc: HIMC; {$IFDEF WinIMEDebug} s: String; {$ENDIF} begin {$IFDEF WinIMEDebug} case msg.wParam of IMN_CLOSESTATUSWINDOW: s:= 'IMN_CLOSESTATUSWINDOW, '; IMN_OPENSTATUSWINDOW: s:= 'IMN_OPENSTATUSWINDOW, '; IMN_CHANGECANDIDATE: s:= 'IMN_CHANGECANDIDATE, '; IMN_CLOSECANDIDATE: s:= 'IMN_CLOSECANDIDATE, '; IMN_OPENCANDIDATE: s:= 'IMN_OPENCANDIDATE, '; IMN_SETCONVERSIONMODE: s:= 'IMN_SETCONVERSIONMODE, '; IMN_SETSENTENCEMODE: s:= 'IMN_SETSENTENCEMODE, '; IMN_SETOPENSTATUS: s:= 'IMN_SETOPENSTATUS, '; IMN_SETCANDIDATEPOS: s:= 'IMN_SETCANDIDATEPOS, '; IMN_SETCOMPOSITIONFONT: s:= 'IMN_SETCOMPOSITIONFONT, '; IMN_SETCOMPOSITIONWINDOW: s:= 'IMN_SETCOMPOSITIONWINDOW, '; IMN_SETSTATUSWINDOWPOS: s:= 'IMN_SETSTATUSWINDOWPOS, '; IMN_GUIDELINE: s:= 'IMN_GUIDELINE, '; IMN_PRIVATE: s:= 'IMN_PRIVATE, '; end; debugln(['TCustomSynEdit.WMImeNotify ',s,' ', dbgHex(Msg.lParam), ' , ', dbgHex(Msg.wParam)]); {$ENDIF} case Msg.WParam of IMN_SETOPENSTATUS: begin imc := ImmGetContext(FriendEdit.Handle); if (imc <> 0) then begin UpdateImeWinFont(imc); UpdateImeWinXY(FriendEdit.CaretXPix, FriendEdit.CaretYPix, imc, True); ImmReleaseContext(FriendEdit.Handle, imc); end; end; IMN_CLOSESTATUSWINDOW: StopIme(True); end; end; procedure LazSynImeSimple.WMImeComposition(var Msg: TMessage); var imc: HIMC; ImeCount: LongWord; p: PChar; begin if ((Msg.LParam and GCS_RESULTSTR) <> 0) then begin imc := ImmGetContext(FriendEdit.Handle); try if imc <> 0 then begin ImeCount := ImmGetCompositionStringW(imc, GCS_RESULTSTR, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['--- GCS_RESULTSTR ', dbgHex(ImeCount)]); {$ENDIF} if ImeCount > 0 then begin GetMem(p, ImeCount + 2); try FriendEdit.BeginUpdate; if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in FriendEdit.Options2) then SelectionObj.SelText := ''; ImmGetCompositionStringW(imc, GCS_RESULTSTR, p, ImeCount + 2); p[ImeCount] := #0; p[ImeCount+1] := #0; FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos; FImeBlockSelection.SelText := UTF16ToUTF8(PWCHAR(p)); FImeBlockSelection.StartLineBytePos := FImeBlockSelection.EndLineBytePos; CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos; Msg.Result := 1; finally FreeMem(p, ImeCount + 2); FriendEdit.EndUpdate; end; end; end; finally ImmReleaseContext(FriendEdit.Handle, imc); end; end; end; procedure LazSynImeSimple.WMImeStartComposition(var Msg: TMessage); var imc: HIMC; begin //debugln(['TCustomSynEdit.WMImeStartComposition ']); imc := ImmGetContext(FriendEdit.Handle); if (imc <> 0) then begin UpdateImeWinFont(imc); UpdateImeWinXY(FriendEdit.CaretXPix, FriendEdit.CaretYPix, imc, True); ImmReleaseContext(FriendEdit.Handle, imc); end; FInCompose := True; FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos; DoIMEStarted; end; procedure LazSynImeSimple.WMImeEndComposition(var Msg: TMessage); begin FInCompose := False; DoIMEEnded; end; procedure LazSynImeSimple.FocusKilled; begin StopIme(True); end; { LazSynImeFull } procedure LazSynImeFull.DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); begin StopIme(True); end; procedure LazSynImeFull.DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin StopIme(True); end; procedure LazSynImeFull.DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges); begin if FInImeMsg then exit; if FHadQueryCharPos then begin FHadQueryCharPos := False; if FriendEdit.Focused then begin windows.SetFocus(0); windows.SetFocus(FriendEdit.Handle); end; end; StopIme(True); end; procedure LazSynImeFull.EnsureLeftChar; var r: Integer; begin if (FLeftPosForTarget < 1) or not FAdjustLeftCharForTargets then exit; r := FRightPosForTarget - FriendEdit.CharsInWindow; if (FriendEdit.LeftChar < r) then FriendEdit.LeftChar := r; if (FriendEdit.LeftChar > FLeftPosForTarget) then FriendEdit.LeftChar := FLeftPosForTarget; end; procedure LazSynImeFull.StopIme(Success: Boolean); begin inherited StopIme(Success); {$IFDEF WinIMEFullDeferOverwrite} if FHasPersistLock then SelectionObj.DecPersistentLock; FHasPersistLock := False; {$ENDIF} end; procedure LazSynImeFull.SetImeTempText(const s: string); var p1, p2: TPoint; f: Boolean; begin p1 := FImeBlockSelection.FirstLineBytePos; f := FInImeMsg; FInImeMsg := True; ViewedTextBuffer.UndoList.Lock; ViewedTextBuffer.RedoList.Lock; FImeBlockSelection.SelText := s; ViewedTextBuffer.UndoList.Unlock; ViewedTextBuffer.RedoList.Unlock; FInImeMsg := f; p2 := FImeBlockSelection.FirstLineBytePos; FImeBlockSelection.StartLineBytePos := p1; FImeBlockSelection.EndLineBytePos := p2; end; constructor LazSynImeFull.Create(AOwner: TSynEditBase); begin inherited Create(AOwner); FAdjustLeftCharForTargets := True; FImeBlockSelection := TSynEditSelection.Create(ViewedTextBuffer, False); FImeBlockSelection.InvalidateLinesMethod := @InvalidateLines; FImeBlockSelection2 := TSynEditSelection.Create(ViewedTextBuffer, False); FImeBlockSelection2.InvalidateLinesMethod := @InvalidateLines; FImeBlockSelection3 := TSynEditSelection.Create(ViewedTextBuffer, False); FImeBlockSelection3.InvalidateLinesMethod := @InvalidateLines; FImeMarkupSelection := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection); FImeMarkupSelection2 := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection2); FImeMarkupSelection3 := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection3); TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection); TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection2); TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection3); FImeMarkupSelection.MarkupInfo.Clear; FImeMarkupSelection.MarkupInfo.FramePriority := MaxInt-1; FImeMarkupSelection.MarkupInfo.FrameColor := clDefault; FImeMarkupSelection.MarkupInfo.FrameStyle := slsDotted; FImeMarkupSelection.MarkupInfo.FrameEdges := sfeBottom; // TODO: prevent any other frame in the active IME (as it distracts from IME underlines // this includes left/right frame edges (can not currently be prevented) // prevent any underline FImeMarkupSelection.MarkupInfo.StylePriority[fsUnderline] := MaxInt; FImeMarkupSelection.MarkupInfo.Style:= []; FImeMarkupSelection.MarkupInfo.StyleMask:= [fsUnderline]; FImeMarkupSelection2.MarkupInfo.Clear; FImeMarkupSelection2.MarkupInfo.FramePriority := MaxInt; FImeMarkupSelection2.MarkupInfo.FrameColor := clDefault; FImeMarkupSelection2.MarkupInfo.FrameStyle := slsSolid; FImeMarkupSelection2.MarkupInfo.FrameEdges := sfeBottom; FImeMarkupSelection3.MarkupInfo.Assign(FriendEdit.SelectedColor); FriendEdit.RegisterStatusChangedHandler(@DoStatusChanged, [scCaretX, scCaretY, scModified]); FriendEdit.RegisterCommandHandler(@DoOnCommand, nil, [hcfInit]); FriendEdit.RegisterBeforeMouseDownHandler(@DoOnMouse); end; destructor LazSynImeFull.Destroy; begin FriendEdit.UnregisterBeforeMouseDownHandler(@DoOnMouse); FriendEdit.UnregisterCommandHandler(@DoOnCommand); FriendEdit.UnRegisterStatusChangedHandler(@DoStatusChanged); TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection); TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection2); TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection3); FreeAndNil(FImeMarkupSelection); FreeAndNil(FImeMarkupSelection2); FreeAndNil(FImeMarkupSelection3); FreeAndNil(FImeBlockSelection); FreeAndNil(FImeBlockSelection2); FreeAndNil(FImeBlockSelection3); inherited Destroy; end; procedure LazSynImeFull.WMImeRequest(var Msg: TMessage); var {$IFDEF WinIMEDebug} s: String; {$ENDIF} cp: PIMECHARPOSITION; p1: TPoint; CWidth: TPhysicalCharWidths; i, x: integer; begin {$IFDEF WinIMEDebug} case msg.wParam of IMR_COMPOSITIONWINDOW: s:= 'IMR_COMPOSITIONWINDOW, '; IMR_CANDIDATEWINDOW: s:= 'IMR_CANDIDATEWINDOW, '; IMR_COMPOSITIONFONT: s:= 'IMR_COMPOSITIONFONT, '; IMR_RECONVERTSTRING: s:= 'IMR_RECONVERTSTRING, '; IMR_CONFIRMRECONVERTSTRING: s:= 'IMR_CONFIRMRECONVERTSTRING, '; IMR_DOCUMENTFEED: s:= 'IMR_DOCUMENTFEED, '; end; if s <> '' then debugln(['TCustomSynEdit.WMImeRequest ', s,' ' , dbgHex(Msg.lParam)]); {$ENDIF} case msg.wParam of IMR_QUERYCHARPOSITION: begin FHadQueryCharPos := True; cp := PIMECHARPOSITION(Msg.lParam); p1 := FImeBlockSelection.StartLineBytePos; if not FInCompose then p1 := CaretObj.LineBytePos; CWidth := ViewedTextBuffer.GetPhysicalCharWidths(FImeBlockSelection.StartLinePos - 1); x := p1.x - 1; i := cp^.dwCharPos; while (i > 0) and (x < length(CWidth)) do begin inc(x); while (x < length(CWidth)) and ((CWidth[x] and PCWMask) = 0) do inc(x); dec(i); end; p1.x := x + i + 1; p1 := FriendEdit.ClientToScreen(TCustomSynEdit(FriendEdit).ScreenXYToPixels( FriendEdit.TextXYToScreenXY(p1))); cp^.pt.y := p1.y; cp^.pt.x := p1.x; cp^.cLineHeight := FriendEdit.LineHeight; cp^.rcDocument.TopLeft := FriendEdit.ClientToScreen(FriendEdit.ClientRect.TopLeft); cp^.rcDocument.BottomRight := FriendEdit.ClientToScreen(FriendEdit.ClientRect.BottomRight); {$IFDEF WinIMEDebug} debugln(['--- TCustomSynEdit.WMImeRequest ** IMR_QUERYCHARPOSITION ', dbgs(cp^.dwCharPos), ' ', dbgs(x),' ', dbgs(p1.x)]); {$ENDIF} Msg.Result := 1; end; end; end; procedure LazSynImeFull.WMImeNotify(var Msg: TMessage); {$IFDEF WinIMEDebug} var s: String; {$ENDIF} begin {$IFDEF WinIMEDebug} case msg.wParam of IMN_CLOSESTATUSWINDOW: s:= 'IMN_CLOSESTATUSWINDOW, '; IMN_OPENSTATUSWINDOW: s:= 'IMN_OPENSTATUSWINDOW, '; IMN_CHANGECANDIDATE: s:= 'IMN_CHANGECANDIDATE, '; IMN_CLOSECANDIDATE: s:= 'IMN_CLOSECANDIDATE, '; IMN_OPENCANDIDATE: s:= 'IMN_OPENCANDIDATE, '; IMN_SETCONVERSIONMODE: s:= 'IMN_SETCONVERSIONMODE, '; IMN_SETSENTENCEMODE: s:= 'IMN_SETSENTENCEMODE, '; IMN_SETOPENSTATUS: s:= 'IMN_SETOPENSTATUS, '; IMN_SETCANDIDATEPOS: s:= 'IMN_SETCANDIDATEPOS, '; IMN_SETCOMPOSITIONFONT: s:= 'IMN_SETCOMPOSITIONFONT, '; IMN_SETCOMPOSITIONWINDOW: s:= 'IMN_SETCOMPOSITIONWINDOW, '; IMN_SETSTATUSWINDOWPOS: s:= 'IMN_SETSTATUSWINDOWPOS, '; IMN_GUIDELINE: s:= 'IMN_GUIDELINE, '; IMN_PRIVATE: s:= 'IMN_PRIVATE, '; end; debugln(['TCustomSynEdit.WMImeNotify ',s,' ', dbgHex(Msg.lParam), ' , ', dbgHex(Msg.wParam)]); {$ENDIF} end; procedure LazSynImeFull.WMImeComposition(var Msg: TMessage); var CWidth: TPhysicalCharWidths; function CharToByte(AStart, AChars: integer): integer; begin if length(CWidth) = 0 then CWidth := ViewedTextBuffer.GetPhysicalCharWidths(FImeBlockSelection.StartLinePos - 1); dec(AStart); Result := AStart; while (AChars > 0) and (Result < length(CWidth)) do begin inc(Result); while (Result < length(CWidth)) and ((CWidth[Result] and PCWMask) = 0) do inc(Result); dec(AChars); end; Result := Result - AStart + AChars; end; var {$IFDEF WinIMEDebug} s: String; {$ENDIF} imc: HIMC; p: PChar; ImeCount: LongWord; x, i: Integer; xy: TPoint; grp: Boolean; {$IFDEF WinIMEFullDeferOverwrite} sel, sel2: Boolean; {$ENDIF} begin {$IFDEF WinIMEDebug} s := ''; if (Msg.lparam and GCS_COMPREADSTR)<>0 then s := s + 'GCS_COMPREADSTR, '; if (Msg.lparam and GCS_COMPREADATTR)<>0 then s := s + 'GCS_COMPREADATTR, '; if (Msg.lparam and GCS_COMPREADCLAUSE)<>0 then s := s + 'GCS_COMPREADCLAUSE, '; //if (Msg.lparam and GCS_COMPSTR)<>0 then s := s + 'GCS_COMPSTR, '; //if (Msg.lparam and GCS_COMPATTR)<>0 then s := s + 'GCS_COMPATTR, '; //if (Msg.lparam and GCS_COMPCLAUSE)<>0 then s := s + 'GCS_COMPCLAUSE, '; //if (Msg.lparam and GCS_CURSORPOS)<>0 then s := s + 'GCS_CURSORPOS, '; if (Msg.lparam and GCS_DELTASTART)<>0 then s := s + 'GCS_DELTASTART, '; if (Msg.lparam and GCS_RESULTREADSTR)<>0 then s := s + 'GCS_RESULTREADSTR, '; if (Msg.lparam and GCS_RESULTREADCLAUSE)<>0 then s := s + 'GCS_RESULTREADCLAUSE, '; //if (Msg.lparam and GCS_RESULTSTR)<>0 then s := s + 'GCS_RESULTSTR, '; if (Msg.lparam and GCS_RESULTCLAUSE)<>0 then s := s + 'GCS_RESULTCLAUSE, '; if (Msg.lparam and CS_INSERTCHAR)<>0 then s := s + ' ** CS_INSERTCHAR, '; if (Msg.lparam and CS_NOMOVECARET)<>0 then s := s + ' ** CS_NOMOVECARET, '; if s <> '' then debugln(['TCustomSynEdit.WMImeComposition ', s]); {$ENDIF} if (Msg.LParam and (GCS_RESULTSTR or GCS_COMPSTR or GCS_CURSORPOS or GCS_COMPATTR {or GCS_COMPCLAUSE})) = 0 then exit; imc := 0; FInImeMsg := True; SetLength(CWidth, 0); try if ((Msg.LParam and GCS_RESULTSTR) <> 0) then begin if imc = 0 then imc := ImmGetContext(FriendEdit.Handle); ImeCount := ImmGetCompositionStringW(imc, GCS_RESULTSTR, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['--- GCS_RESULTSTR ', dbgHex(ImeCount)]); {$ENDIF} if ImeCount > 0 then begin GetMem(p, ImeCount + 2); try SetImeTempText(''); CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos; grp := ViewedTextBuffer.UndoList.GroupUndo; ViewedTextBuffer.UndoList.GroupUndo := True; FriendEdit.BeginUpdate; ViewedTextBuffer.UndoList.CurrentReason := ecImeStr; {$IFDEF WinIMEFullDeferOverwrite} if FHasPersistLock then SelectionObj.DecPersistentLock; FHasPersistLock := False; if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in FriendEdit.Options2) then begin SelectionObj.SelText := ''; FImeBlockSelection.StartLineBytePos := SelectionObj.StartLineBytePos; end; {$ENDIF} CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos; ImmGetCompositionStringW(imc, GCS_RESULTSTR, p, ImeCount + 2); p[ImeCount] := #0; p[ImeCount+1] := #0; FImeBlockSelection.SelText := UTF16ToUTF8(PWCHAR(p)); FImeBlockSelection.StartLineBytePos := FImeBlockSelection.EndLineBytePos; CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos; {$IFnDEF WinIMEFullOverwriteSkipUndo} FNeedUndoOnCancel := False; {$ENDIF} Msg.Result := 1; finally FriendEdit.EndUpdate; ViewedTextBuffer.UndoList.GroupUndo := grp; FreeMem(p, ImeCount + 2); end; end; end; if ((Msg.LParam and GCS_COMPSTR) <> 0) then begin if imc = 0 then imc := ImmGetContext(FriendEdit.Handle); ImeCount := ImmGetCompositionStringW(imc, GCS_COMPSTR, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['--- GCS_COMPSTR ', dbgHex(ImeCount)]); {$ENDIF} if ImeCount > 0 then begin GetMem(p, ImeCount + 2); try ImmGetCompositionStringW(imc, GCS_COMPSTR, p, ImeCount + 2); p[ImeCount] := #0; p[ImeCount+1] := #0; {$IFDEF WinIMEFullDeferOverwrite} sel := (not SelectionObj.IsBackwardSel) and (CompareCarets(SelectionObj.EndLineBytePos, FImeBlockSelection.StartLineBytePos) = 0); sel2 := SelectionObj.IsBackwardSel and (CompareCarets(SelectionObj.EndLineBytePos, FImeBlockSelection.EndLineBytePos) = 0); {$ENDIF} SetImeTempText(UTF16ToUTF8(PWCHAR(p))); {$IFDEF WinIMEFullDeferOverwrite} if sel then SelectionObj.EndLineBytePos := FImeBlockSelection.StartLineBytePos; if sel2 then SelectionObj.EndLineBytePos := FImeBlockSelection.EndLineBytePos; {$ENDIF} Msg.Result := 1; finally FreeMem(p, ImeCount + 2); end; end; end; if ((Msg.LParam and GCS_COMPATTR) <> 0) then begin //ATTR_INPUT = $00; // dotted undurline //ATTR_TARGET_CONVERTED = $01; // full underline (bold underline / double width line) //ATTR_CONVERTED = $02; // light underline (single width line) //ATTR_TARGET_NOTCONVERTED = $03; // Show as selected ? //ATTR_INPUT_ERROR = $04; // ? none //ATTR_FIXEDCONVERTED = $05; // ? none // low confidence => green underline. if imc = 0 then imc := ImmGetContext(FriendEdit.Handle); ImeCount := ImmGetCompositionStringW(imc, GCS_COMPATTR, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['***** GCS_COMPATTR ', dbgHex(ImeCount)]); {$ENDIF} if ImeCount > 0 then begin FLeftPosForTarget := -1; FRightPosForTarget := -1; xy := FImeBlockSelection.StartLineBytePos; FImeBlockSelection2.StartLineBytePos := xy; FImeBlockSelection2.EndLineBytePos := xy; FImeBlockSelection3.StartLineBytePos := xy; FImeBlockSelection3.EndLineBytePos := xy; GetMem(p, ImeCount + 2); try ImmGetCompositionStringW(imc, GCS_COMPATTR, p, ImeCount + 2); {$IFDEF WinIMEDebug} DebugLn(dbgMemRange(PByte( p), ImeCount)); {$ENDIF} i := 0; while longword(i) < ImeCount do begin if ord(p[i]) = ATTR_TARGET_CONVERTED then begin x := FImeBlockSelection.StartBytePos; xy.x := x + CharToByte(x, i); FImeBlockSelection2.StartLineBytePos := xy; if (FLeftPosForTarget < 0) or (FLeftPosForTarget > xy.x) then FLeftPosForTarget := xy.x; inc(i); while (longword(i) < ImeCount) and (ord(p[i]) = ATTR_TARGET_CONVERTED) do inc(i); xy.x := x + CharToByte(x, i); FImeBlockSelection2.EndLineBytePos := xy; if (FRightPosForTarget < 0) or (FRightPosForTarget < xy.x) then FRightPosForTarget := xy.x; //break; end; if ord(p[i]) = ATTR_TARGET_NOTCONVERTED then begin x := FImeBlockSelection.StartBytePos; xy.x := x + CharToByte(x, i); if (FLeftPosForTarget < 0) or (FLeftPosForTarget > xy.x) then FLeftPosForTarget := xy.x; FImeBlockSelection3.StartLineBytePos := xy; inc(i); while (longword(i) < ImeCount) and (ord(p[i]) = ATTR_TARGET_NOTCONVERTED) do inc(i); xy.x := x + CharToByte(x, i); FImeBlockSelection3.EndLineBytePos := xy; if (FRightPosForTarget < 0) or (FRightPosForTarget < xy.x) then FRightPosForTarget := xy.x; //break; end; inc(i); end; Msg.Result := 1; finally FreeMem(p, ImeCount + 2); end; if (FLeftPosForTarget > 0) and FAdjustLeftCharForTargets then begin FLeftPosForTarget := ViewedTextBuffer.LogicalToPhysicalPos (Point(FLeftPosForTarget, FImeBlockSelection.FirstLineBytePos.Y)).x; if FRightPosForTarget > 0 then FRightPosForTarget := ViewedTextBuffer.LogicalToPhysicalPos (Point(FRightPosForTarget, FImeBlockSelection.FirstLineBytePos.Y)).x; EnsureLeftChar; end; end; end; (* if ((Msg.LParam and GCS_COMPCLAUSE) <> 0) then begin // attributes for all chars in any one clause should be the equal. if imc = 0 then imc := ImmGetContext(FriendEdit.Handle); ImeCount := ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['***** GCS_COMPCLAUSE ', dbgHex(ImeCount)]); {$ENDIF} if ImeCount > 0 then begin GetMem(p, ImeCount + 2); try ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, p, ImeCount + 2); DebugLn(dbgMemRange(PByte( p), ImeCount)); finally FreeMem(p, ImeCount + 2); end; end; end; *) if ((Msg.LParam and GCS_CURSORPOS) <> 0) then begin if imc = 0 then imc := ImmGetContext(FriendEdit.Handle); ImeCount := ImmGetCompositionStringW(imc, GCS_CURSORPOS, nil, 0); {$IFDEF WinIMEDebug} DebugLn(['--- GCS_CURSORPOS ', dbgs(ImeCount), ' FLeftPosForTarget=',FLeftPosForTarget]); {$ENDIF} if ImeCount >= 0 then begin // ToDo: Comparison is always True. ImeCount := ImeCount and $ffff; x := FImeBlockSelection.StartBytePos; x := x + CharToByte(x, ImeCount); CaretObj.CharPos := ViewedTextBuffer.LogicalToPhysicalPos(Point(x, FImeBlockSelection.StartLinePos)).x; // TODO: this causes full repaints EnsureLeftChar; end; end; finally if imc <> 0 then ImmReleaseContext(FriendEdit.Handle, imc); FInImeMsg := False; end; inherited; end; procedure LazSynImeFull.WMImeStartComposition(var Msg: TMessage); begin //debugln(['TCustomSynEdit.WMImeStartComposition ']); {$IFnDEF WinIMEFullDeferOverwrite} if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in FriendEdit.Options2) then begin {$IFnDEF WinIMEFullOverwriteSkipUndo} ViewedTextBuffer.UndoList.ForceGroupEnd; FUndoStamp1 := ViewedTextBuffer.UndoList.PeekItem; {$ENDIF} FriendEdit.BeginUpdate; ViewedTextBuffer.UndoList.CurrentReason := ecImeStr; SelectionObj.SelText := ''; FriendEdit.EndUpdate; {$IFnDEF WinIMEFullOverwriteSkipUndo} FUndoStamp2 := ViewedTextBuffer.UndoList.PeekItem; FNeedUndoOnCancel := FUndoStamp1 <> FUndoStamp2; end else begin FNeedUndoOnCancel := False {$ENDIF} end; {$ENDIF} {$IFDEF WinIMEFullDeferOverwrite} if not FHasPersistLock then SelectionObj.IncPersistentLock; FHasPersistLock := True; {$ENDIF} FImeMarkupSelection3.MarkupInfo.Assign(FriendEdit.SelectedColor); FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos; FInCompose := True; Msg.Result := 1; DoIMEStarted; end; procedure LazSynImeFull.WMImeEndComposition(var Msg: TMessage); begin //debugln(['TCustomSynEdit.WMImeEndComposition ']); SetImeTempText(''); CaretObj.LineBytePos := FImeBlockSelection.LastLineBytePos; {$IFnDEF WinIMEFullDeferOverwrite} {$IFnDEF WinIMEFullOverwriteSkipUndo} if FNeedUndoOnCancel and (ViewedTextBuffer.UndoList.PeekItem = FUndoStamp2) then FriendEdit.Undo; {$ENDIF} {$ENDIF} {$IFDEF WinIMEFullDeferOverwrite} if FHasPersistLock then SelectionObj.DecPersistentLock; FHasPersistLock := False; {$ENDIF} FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos; FImeBlockSelection2.StartLineBytePos := CaretObj.LineBytePos; FInCompose := False; Msg.Result := 1; DoIMEEnded; end; procedure LazSynImeFull.FocusKilled; begin StopIme(True); end; end.