1 unit LazSynIMM;
2
3 {$mode objfpc}{$H+}
4
5 {off $DEFINE WinIMEDebug}
6 {off $DEFINE WinIMEFullDeferOverwrite} // In Full IME do "overwrite selecton" when IME finishes (normally done at start)
7 {off $DEFINE WinIMEFullOverwriteSkipUndo} // In Full IME undo "overwrite selecton", if IME cancelled
8
9 interface
10
11 uses
12 windows, imm, Classes, SysUtils, Controls, LazLoggerBase, LCLType, LazUTF8, Graphics,
13 SynEditMiscClasses, SynTextDrawer, SynEditPointClasses, SynEditMarkupSelection,
14 SynEditMarkup, SynEditTypes, SynEditKeyCmds, LazSynEditText, SynEditTextBase,
15 SynEditMiscProcs;
16
17 {$IFDEF WINCE} {$IF (FPC_FULLVERSION < 20700)}
ImmSetCompositionFontAnull18 function ImmSetCompositionFontA(_himc:HIMC; lplf:LPLOGFONT):BOOL; external ImmDLL name 'ImmSetCompositionFontA';
19 const
20 WM_IME_REQUEST = $0288;
21 {$ENDIF}{$ENDIF}
22 type
23
24 { LazSynIme }
25
26 LazSynIme = class(TSynEditFriend)
27 private
28 FInvalidateLinesMethod: TInvalidateLines;
29 FOnIMEEnd: TNotifyEvent;
30 FOnIMEStart: TNotifyEvent;
31 FIMEActive: Boolean;
32 protected
33 FInCompose: Boolean;
34 procedure InvalidateLines(FirstLine, LastLine: integer);
35 procedure StopIme(Success: Boolean); virtual;
36 procedure DoIMEStarted;
37 procedure DoIMEEnded;
38 public
39 constructor Create(AOwner: TSynEditBase); reintroduce;
40 procedure WMImeRequest(var Msg: TMessage); virtual;
41 procedure WMImeNotify(var Msg: TMessage); virtual;
42 procedure WMImeComposition(var Msg: TMessage); virtual;
43 procedure WMImeStartComposition(var Msg: TMessage); virtual;
44 procedure WMImeEndComposition(var Msg: TMessage); virtual;
45 procedure FocusKilled; virtual;
46 property InvalidateLinesMethod : TInvalidateLines write FInvalidateLinesMethod;
47 property OnIMEStart: TNotifyEvent read FOnIMEStart write FOnIMEStart;
48 property OnIMEEnd: TNotifyEvent read FOnIMEEnd write FOnIMEEnd;
49 end;
50
51 { LazSynImeSimple }
52
53 LazSynImeSimple = class(LazSynIme)
54 private
55 FImeBlockSelection: TSynEditSelection;
56 FImeWinX, FImeWinY: Integer;
57 FTextDrawer: TheTextDrawer;
58 procedure SetTextDrawer(AValue: TheTextDrawer);
59 procedure UpdateImeWinXY(aX, aY: Integer; aImc: HIMC = 0; aForce: Boolean = False);
60 procedure UpdateImeWinFont(aImc: HIMC = 0);
61 procedure DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
62 procedure DoDrawerFontChanged(Sender: TObject);
63 procedure DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean;
64 var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
65 HandlerData: pointer);
66 procedure DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
67 Y: Integer);
68 public
69 constructor Create(AOwner: TSynEditBase);
70 destructor Destroy; override;
71 procedure WMImeNotify(var Msg: TMessage); override;
72 procedure WMImeComposition(var Msg: TMessage); override;
73 procedure WMImeStartComposition(var Msg: TMessage); override;
74 procedure WMImeEndComposition(var Msg: TMessage); override;
75 procedure FocusKilled; override;
76 property TextDrawer: TheTextDrawer read FTextDrawer write SetTextDrawer;
77 end;
78
79 { LazSynImeFull }
80
81 LazSynImeFull = class(LazSynIme)
82 private
83 FAdjustLeftCharForTargets: Boolean;
84 FLeftPosForTarget, FRightPosForTarget: Integer;
85 FImeBlockSelection, FImeBlockSelection2, FImeBlockSelection3: TSynEditSelection; // TODO: create a custom markup
86 FImeMarkupSelection, FImeMarkupSelection2, FImeMarkupSelection3: TSynEditMarkupSelection;
87 FInImeMsg: Boolean;
88 {$IFnDEF WinIMEFullOverwriteSkipUndo}
89 FUndoStamp1, FUndoStamp2: TSynEditUndoGroup;
90 FNeedUndoOnCancel: Boolean;
91 {$ENDIF}
92 {$IFDEF WinIMEFullDeferOverwrite}
93 FHasPersistLock: Boolean;
94 {$ENDIF}
95 procedure SetImeTempText(const s: string);
96 procedure DoOnCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean;
97 var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
98 HandlerData: pointer);
99 procedure DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
100 Y: Integer);
101 procedure DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
102 procedure EnsureLeftChar;
103 protected
104 procedure StopIme(Success: Boolean); override;
105 public
106 constructor Create(AOwner: TSynEditBase);
107 destructor Destroy; override;
108 procedure WMImeRequest(var Msg: TMessage); override;
109 procedure WMImeNotify(var Msg: TMessage); override;
110 procedure WMImeComposition(var Msg: TMessage); override;
111 procedure WMImeStartComposition(var Msg: TMessage); override;
112 procedure WMImeEndComposition(var Msg: TMessage); override;
113 procedure FocusKilled; override;
114 public
115 property AdjustLeftCharForTargets: Boolean read FAdjustLeftCharForTargets write FAdjustLeftCharForTargets;
116 end;
117
118 implementation
119 uses SynEdit;
120
121 { LazSynIme }
122
123 procedure LazSynIme.InvalidateLines(FirstLine, LastLine: integer);
124 begin
125 FInvalidateLinesMethod(FirstLine, LastLine);
126 end;
127
128 procedure LazSynIme.StopIme(Success: Boolean);
129 var
130 imc: HIMC;
131 begin
132 if (not FInCompose) or (not FriendEdit.HandleAllocated) then exit;
133
134 imc := ImmGetContext(FriendEdit.Handle);
135 if (imc <> 0) then begin
136 if Success then
137 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0)
138 else
139 ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
140 ImmReleaseContext(FriendEdit.Handle, imc);
141 end;
142 DoIMEEnded;
143 end;
144
145 procedure LazSynIme.DoIMEStarted;
146 begin
147 if FIMEActive then
148 exit;
149 FIMEActive := True;
150 if FOnIMEStart <> nil then
151 FOnIMEStart(FriendEdit);
152 end;
153
154 procedure LazSynIme.DoIMEEnded;
155 begin
156 if not FIMEActive then
157 exit;
158 FIMEActive := False;
159 if FOnIMEEnd <> nil then
160 FOnIMEEnd(FriendEdit);
161 end;
162
163 constructor LazSynIme.Create(AOwner: TSynEditBase);
164 begin
165 FriendEdit := AOwner;
166 end;
167
168 procedure LazSynIme.WMImeRequest(var Msg: TMessage);
169 begin
170 end;
171
172 procedure LazSynIme.WMImeComposition(var Msg: TMessage);
173 begin
174 end;
175
176 procedure LazSynIme.WMImeNotify(var Msg: TMessage);
177 begin
178 end;
179
180 procedure LazSynIme.WMImeStartComposition(var Msg: TMessage);
181 begin
182 end;
183
184 procedure LazSynIme.WMImeEndComposition(var Msg: TMessage);
185 begin
186 end;
187
188 procedure LazSynIme.FocusKilled;
189 begin
190 end;
191
192 { LazSynImeSimple }
193
194 procedure LazSynImeSimple.DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
195 begin
196 UpdateImeWinXY(TCustomSynEdit(FriendEdit).CaretXPix, TCustomSynEdit(FriendEdit).CaretYPix);
197 if Changes * [scCaretX, scCaretY] <> [] then
198 StopIme(False);
199 end;
200
201 procedure LazSynImeSimple.DoDrawerFontChanged(Sender: TObject);
202 var
203 imc: HIMC;
204 begin
205 if not FriendEdit.HandleAllocated then
206 exit;
207 imc := ImmGetContext(FriendEdit.Handle);
208 if (imc <> 0) then begin
209 UpdateImeWinFont(imc);
210 UpdateImeWinXY(TCustomSynEdit(FriendEdit).CaretXPix, TCustomSynEdit(FriendEdit).CaretYPix, imc);
211 ImmReleaseContext(FriendEdit.Handle, imc);
212 end;
213 end;
214
215 procedure LazSynImeSimple.DoOnCommand(Sender: TObject; AfterProcessing: boolean;
216 var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
217 HandlerData: pointer);
218 begin
219 StopIme(True);
220 end;
221
222 procedure LazSynImeSimple.DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
223 X, Y: Integer);
224 begin
225 StopIme(True);
226 end;
227
228 procedure LazSynImeSimple.UpdateImeWinXY(aX, aY: Integer; aImc: HIMC; aForce: Boolean);
229 var
230 cf: CompositionForm;
231 imc: HIMC;
232 begin
233 if not FriendEdit.HandleAllocated then exit;
234 if (not aForce) and (aX = FImeWinX) and (aY = FImeWinY) then exit;
235 FImeWinX := aX;
236 FImeWinY := aY;
237
238 cf.dwStyle := CFS_POINT;
239 cf.ptCurrentPos := Point(aX, aY);
240 if aImc = 0 then
241 imc := ImmGetContext(FriendEdit.Handle)
242 else
243 imc := aImc;
244 if (imc <> 0) then begin
245 ImmSetCompositionWindow(imc, @cf);
246 if (aImc = 0) then
247 ImmReleaseContext(FriendEdit.Handle, imc);
248 end;
249 end;
250
251 procedure LazSynImeSimple.SEtTextDrawer(AValue: TheTextDrawer);
252 begin
253 if FTextDrawer = AValue then Exit;
254 if FTextDrawer <> nil then
255 FTextDrawer.UnRegisterOnFontChangeHandler(@DoDrawerFontChanged);
256 FTextDrawer := AValue;
257 if FTextDrawer <> nil then
258 FTextDrawer.RegisterOnFontChangeHandler(@DoDrawerFontChanged);
259 end;
260
261 procedure LazSynImeSimple.UpdateImeWinFont(aImc: HIMC);
262 var
263 imc: HIMC;
264 logFont: TLogFont;
265 begin
266 if not FriendEdit.HandleAllocated then exit;
267 if aImc = 0 then
268 imc := ImmGetContext(FriendEdit.Handle)
269 else
270 imc := aImc;
271 if (imc <> 0) then begin
272 GetObject(FriendEdit.Font.Handle, SizeOf(TLogFont), @logFont);
273 ImmSetCompositionFontA(imc, @logFont);
274 if (aImc = 0) then
275 ImmReleaseContext(FriendEdit.Handle, imc);
276 end;
277 end;
278
279 constructor LazSynImeSimple.Create(AOwner: TSynEditBase);
280 begin
281 inherited Create(AOwner);
282 FImeBlockSelection := TSynEditSelection.Create(ViewedTextBuffer, False);
283 FImeBlockSelection.InvalidateLinesMethod := @InvalidateLines;
284
285 TCustomSynEdit(FriendEdit).RegisterStatusChangedHandler(@DoStatusChanged, [scCaretX, scCaretY, scLeftChar, scTopLine, scModified]);
286 TCustomSynEdit(FriendEdit).RegisterCommandHandler(@DoOnCommand, nil, [hcfInit]);
287 TCustomSynEdit(FriendEdit).RegisterBeforeMouseDownHandler(@DoOnMouse);
288 end;
289
290 destructor LazSynImeSimple.Destroy;
291 begin
292 TextDrawer := nil;
293 FreeAndNil(FImeBlockSelection);
294 TCustomSynEdit(FriendEdit).UnregisterBeforeMouseDownHandler(@DoOnMouse);
295 TCustomSynEdit(FriendEdit).UnregisterCommandHandler(@DoOnCommand);
296 TCustomSynEdit(FriendEdit).UnRegisterStatusChangedHandler(@DoStatusChanged);
297 inherited Destroy;
298 end;
299
300 procedure LazSynImeSimple.WMImeNotify(var Msg: TMessage);
301 var
302 imc: HIMC;
303 {$IFDEF WinIMEDebug}
304 s: String;
305 {$ENDIF}
306 begin
307 {$IFDEF WinIMEDebug}
308 case msg.wParam of
309 IMN_CLOSESTATUSWINDOW: s:= 'IMN_CLOSESTATUSWINDOW, ';
310 IMN_OPENSTATUSWINDOW: s:= 'IMN_OPENSTATUSWINDOW, ';
311 IMN_CHANGECANDIDATE: s:= 'IMN_CHANGECANDIDATE, ';
312 IMN_CLOSECANDIDATE: s:= 'IMN_CLOSECANDIDATE, ';
313 IMN_OPENCANDIDATE: s:= 'IMN_OPENCANDIDATE, ';
314 IMN_SETCONVERSIONMODE: s:= 'IMN_SETCONVERSIONMODE, ';
315 IMN_SETSENTENCEMODE: s:= 'IMN_SETSENTENCEMODE, ';
316 IMN_SETOPENSTATUS: s:= 'IMN_SETOPENSTATUS, ';
317 IMN_SETCANDIDATEPOS: s:= 'IMN_SETCANDIDATEPOS, ';
318 IMN_SETCOMPOSITIONFONT: s:= 'IMN_SETCOMPOSITIONFONT, ';
319 IMN_SETCOMPOSITIONWINDOW: s:= 'IMN_SETCOMPOSITIONWINDOW, ';
320 IMN_SETSTATUSWINDOWPOS: s:= 'IMN_SETSTATUSWINDOWPOS, ';
321 IMN_GUIDELINE: s:= 'IMN_GUIDELINE, ';
322 IMN_PRIVATE: s:= 'IMN_PRIVATE, ';
323 end;
324 debugln(['TCustomSynEdit.WMImeNotify ',s,' ', dbgHex(Msg.lParam), ' , ', dbgHex(Msg.wParam)]);
325 {$ENDIF}
326
327 case Msg.WParam of
328 IMN_SETOPENSTATUS: begin
329 imc := ImmGetContext(FriendEdit.Handle);
330 if (imc <> 0) then begin
331 UpdateImeWinFont(imc);
332 UpdateImeWinXY(TCustomSynEdit(FriendEdit).CaretXPix, TCustomSynEdit(FriendEdit).CaretYPix, imc, True);
333 ImmReleaseContext(FriendEdit.Handle, imc);
334 end;
335 end;
336 IMN_CLOSESTATUSWINDOW:
337 StopIme(True);
338 end;
339 end;
340
341 procedure LazSynImeSimple.WMImeComposition(var Msg: TMessage);
342 var
343 imc: HIMC;
344 ImeCount: LongWord;
345 p: PChar;
346 begin
347 if ((Msg.LParam and GCS_RESULTSTR) <> 0) then begin
348 imc := ImmGetContext(FriendEdit.Handle);
349 try
350 if imc <> 0 then begin
351 ImeCount := ImmGetCompositionStringW(imc, GCS_RESULTSTR, nil, 0);
352 {$IFDEF WinIMEDebug}
353 DebugLn(['--- GCS_RESULTSTR ', dbgHex(ImeCount)]);
354 {$ENDIF}
355 if ImeCount > 0 then begin
356 GetMem(p, ImeCount + 2);
357 try
358 TCustomSynEdit(FriendEdit).BeginUpdate;
359 if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in TCustomSynEdit(FriendEdit).Options2) then
360 SelectionObj.SelText := '';
361 ImmGetCompositionStringW(imc, GCS_RESULTSTR, p, ImeCount + 2);
362 p[ImeCount] := #0;
363 p[ImeCount+1] := #0;
364 FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos;
365 FImeBlockSelection.SelText := UTF16ToUTF8(PWCHAR(p));
366 FImeBlockSelection.StartLineBytePos := FImeBlockSelection.EndLineBytePos;
367 CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos;
368 Msg.Result := 1;
369 finally
370 FreeMem(p, ImeCount + 2);
371 TCustomSynEdit(FriendEdit).EndUpdate;
372 end;
373 end;
374 end;
375 finally
376 ImmReleaseContext(FriendEdit.Handle, imc);
377 end;
378 end;
379
380
381 end;
382
383 procedure LazSynImeSimple.WMImeStartComposition(var Msg: TMessage);
384 var
385 imc: HIMC;
386 begin
387 //debugln(['TCustomSynEdit.WMImeStartComposition ']);
388 imc := ImmGetContext(FriendEdit.Handle);
389 if (imc <> 0) then begin
390 UpdateImeWinFont(imc);
391 UpdateImeWinXY(TCustomSynEdit(FriendEdit).CaretXPix, TCustomSynEdit(FriendEdit).CaretYPix, imc, True);
392 ImmReleaseContext(FriendEdit.Handle, imc);
393 end;
394 FInCompose := True;
395 FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos;
396 DoIMEStarted;
397 end;
398
399 procedure LazSynImeSimple.WMImeEndComposition(var Msg: TMessage);
400 begin
401 FInCompose := False;
402 DoIMEEnded;
403 end;
404
405 procedure LazSynImeSimple.FocusKilled;
406 begin
407 StopIme(True);
408 end;
409
410 { LazSynImeFull }
411
412 procedure LazSynImeFull.DoOnCommand(Sender: TObject; AfterProcessing: boolean;
413 var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
414 HandlerData: pointer);
415 begin
416 StopIme(True);
417 end;
418
419 procedure LazSynImeFull.DoOnMouse(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
420 X, Y: Integer);
421 begin
422 StopIme(True);
423 end;
424
425 procedure LazSynImeFull.DoStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
426 begin
427 if FInImeMsg then exit;
428 StopIme(True);
429 end;
430
431 procedure LazSynImeFull.EnsureLeftChar;
432 var
433 r: Integer;
434 begin
435 if (FLeftPosForTarget < 1) or not FAdjustLeftCharForTargets then
436 exit;
437
438 r := FRightPosForTarget - TCustomSynEdit(FriendEdit).CharsInWindow;
439
440 if (TCustomSynEdit(FriendEdit).LeftChar < r) then
441 TCustomSynEdit(FriendEdit).LeftChar := r;
442
443 if (TCustomSynEdit(FriendEdit).LeftChar > FLeftPosForTarget) then
444 TCustomSynEdit(FriendEdit).LeftChar := FLeftPosForTarget;
445
446 end;
447
448 procedure LazSynImeFull.StopIme(Success: Boolean);
449 begin
450 inherited StopIme(Success);
451 {$IFDEF WinIMEFullDeferOverwrite}
452 if FHasPersistLock then
453 SelectionObj.DecPersistentLock;
454 FHasPersistLock := False;
455 {$ENDIF}
456 end;
457
458 procedure LazSynImeFull.SetImeTempText(const s: string);
459 var
460 p1, p2: TPoint;
461 f: Boolean;
462 begin
463 p1 := FImeBlockSelection.FirstLineBytePos;
464
465 f := FInImeMsg;
466 FInImeMsg := True;
467 ViewedTextBuffer.UndoList.Lock;
468 ViewedTextBuffer.RedoList.Lock;
469 FImeBlockSelection.SelText := s;
470 ViewedTextBuffer.UndoList.Unlock;
471 ViewedTextBuffer.RedoList.Unlock;
472 FInImeMsg := f;
473
474 p2 := FImeBlockSelection.FirstLineBytePos;
475 FImeBlockSelection.StartLineBytePos := p1;
476 FImeBlockSelection.EndLineBytePos := p2;
477 end;
478
479 constructor LazSynImeFull.Create(AOwner: TSynEditBase);
480 begin
481 inherited Create(AOwner);
482 FAdjustLeftCharForTargets := True;
483
484 FImeBlockSelection := TSynEditSelection.Create(ViewedTextBuffer, False);
485 FImeBlockSelection.InvalidateLinesMethod := @InvalidateLines;
486 FImeBlockSelection2 := TSynEditSelection.Create(ViewedTextBuffer, False);
487 FImeBlockSelection2.InvalidateLinesMethod := @InvalidateLines;
488 FImeBlockSelection3 := TSynEditSelection.Create(ViewedTextBuffer, False);
489 FImeBlockSelection3.InvalidateLinesMethod := @InvalidateLines;
490
491 FImeMarkupSelection := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection);
492 FImeMarkupSelection2 := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection2);
493 FImeMarkupSelection3 := TSynEditMarkupSelection.Create(FriendEdit, FImeBlockSelection3);
494
495 TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection);
496 TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection2);
497 TSynEditMarkupManager(MarkupMgr).AddMarkUp(FImeMarkupSelection3);
498
499 FImeMarkupSelection.MarkupInfo.Clear;
500 FImeMarkupSelection.MarkupInfo.FramePriority := MaxInt-1;
501 FImeMarkupSelection.MarkupInfo.FrameColor := clDefault;
502 FImeMarkupSelection.MarkupInfo.FrameStyle := slsDotted;
503 FImeMarkupSelection.MarkupInfo.FrameEdges := sfeBottom;
504
505 // TODO: prevent any other frame in the active IME (as it distracts from IME underlines
506 // this includes left/right frame edges (can not currently be prevented)
507
508 // prevent any underline
509 FImeMarkupSelection.MarkupInfo.StylePriority[fsUnderline] := MaxInt;
510 FImeMarkupSelection.MarkupInfo.Style:= [];
511 FImeMarkupSelection.MarkupInfo.StyleMask:= [fsUnderline];
512
513 FImeMarkupSelection2.MarkupInfo.Clear;
514 FImeMarkupSelection2.MarkupInfo.FramePriority := MaxInt;
515 FImeMarkupSelection2.MarkupInfo.FrameColor := clDefault;
516 FImeMarkupSelection2.MarkupInfo.FrameStyle := slsSolid;
517 FImeMarkupSelection2.MarkupInfo.FrameEdges := sfeBottom;
518
519 FImeMarkupSelection3.MarkupInfo.Assign(TCustomSynEdit(FriendEdit).SelectedColor);
520
521 TCustomSynEdit(FriendEdit).RegisterStatusChangedHandler(@DoStatusChanged, [scCaretX, scCaretY, scModified]);
522 TCustomSynEdit(FriendEdit).RegisterCommandHandler(@DoOnCommand, nil, [hcfInit]);
523 TCustomSynEdit(FriendEdit).RegisterBeforeMouseDownHandler(@DoOnMouse);
524
525 end;
526
527 destructor LazSynImeFull.Destroy;
528 begin
529 TCustomSynEdit(FriendEdit).UnregisterBeforeMouseDownHandler(@DoOnMouse);
530 TCustomSynEdit(FriendEdit).UnregisterCommandHandler(@DoOnCommand);
531 TCustomSynEdit(FriendEdit).UnRegisterStatusChangedHandler(@DoStatusChanged);
532 TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection);
533 TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection2);
534 TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FImeMarkupSelection3);
535
536 FreeAndNil(FImeMarkupSelection);
537 FreeAndNil(FImeMarkupSelection2);
538 FreeAndNil(FImeMarkupSelection3);
539 FreeAndNil(FImeBlockSelection);
540 FreeAndNil(FImeBlockSelection2);
541 FreeAndNil(FImeBlockSelection3);
542 inherited Destroy;
543 end;
544
545 procedure LazSynImeFull.WMImeRequest(var Msg: TMessage);
546 var
547 {$IFDEF WinIMEDebug}
548 s: String;
549 {$ENDIF}
550 cp: PIMECHARPOSITION;
551 p1: TPoint;
552 CWidth: TPhysicalCharWidths;
553 i, x: integer;
554 begin
555 {$IFDEF WinIMEDebug}
556 case msg.wParam of
557 IMR_COMPOSITIONWINDOW: s:= 'IMR_COMPOSITIONWINDOW, ';
558 IMR_CANDIDATEWINDOW: s:= 'IMR_CANDIDATEWINDOW, ';
559 IMR_COMPOSITIONFONT: s:= 'IMR_COMPOSITIONFONT, ';
560 IMR_RECONVERTSTRING: s:= 'IMR_RECONVERTSTRING, ';
561 IMR_CONFIRMRECONVERTSTRING: s:= 'IMR_CONFIRMRECONVERTSTRING, ';
562 IMR_DOCUMENTFEED: s:= 'IMR_DOCUMENTFEED, ';
563 end;
564 if s <> '' then debugln(['TCustomSynEdit.WMImeRequest ', s,' ' , dbgHex(Msg.lParam)]);
565 {$ENDIF}
566
567 case msg.wParam of
568 IMR_QUERYCHARPOSITION: begin
569 cp := PIMECHARPOSITION(Msg.lParam);
570 p1 := FImeBlockSelection.StartLineBytePos;
571 if not FInCompose then
572 p1 := CaretObj.LineBytePos;
573
574 CWidth := ViewedTextBuffer.GetPhysicalCharWidths(FImeBlockSelection.StartLinePos - 1);
575 x := p1.x - 1;
576 i := cp^.dwCharPos;
577 while (i > 0) and (x < length(CWidth)) do begin
578 inc(x);
579 while (x < length(CWidth)) and ((CWidth[x] and PCWMask) = 0) do
580 inc(x);
581 dec(i);
582 end;
583 p1.x := x + i + 1;
584 p1 := FriendEdit.ClientToScreen(TCustomSynEdit(FriendEdit).RowColumnToPixels(ViewedTextBuffer.LogicalToPhysicalPos(p1)));
585
586 cp^.pt.y := p1.y;
587 cp^.pt.x := p1.x;
588 cp^.cLineHeight := TCustomSynEdit(FriendEdit).LineHeight;
589 cp^.rcDocument.TopLeft := FriendEdit.ClientToScreen(FriendEdit.ClientRect.TopLeft);
590 cp^.rcDocument.BottomRight := FriendEdit.ClientToScreen(FriendEdit.ClientRect.BottomRight);
591 {$IFDEF WinIMEDebug}
592 debugln(['--- TCustomSynEdit.WMImeRequest ** IMR_QUERYCHARPOSITION ', dbgs(cp^.dwCharPos), ' ', dbgs(x),' ', dbgs(p1.x)]);
593 {$ENDIF}
594 Msg.Result := 1;
595 end;
596 end;
597 end;
598
599 procedure LazSynImeFull.WMImeNotify(var Msg: TMessage);
600 {$IFDEF WinIMEDebug}
601 var
602 s: String;
603 {$ENDIF}
604 begin
605 {$IFDEF WinIMEDebug}
606 case msg.wParam of
607 IMN_CLOSESTATUSWINDOW: s:= 'IMN_CLOSESTATUSWINDOW, ';
608 IMN_OPENSTATUSWINDOW: s:= 'IMN_OPENSTATUSWINDOW, ';
609 IMN_CHANGECANDIDATE: s:= 'IMN_CHANGECANDIDATE, ';
610 IMN_CLOSECANDIDATE: s:= 'IMN_CLOSECANDIDATE, ';
611 IMN_OPENCANDIDATE: s:= 'IMN_OPENCANDIDATE, ';
612 IMN_SETCONVERSIONMODE: s:= 'IMN_SETCONVERSIONMODE, ';
613 IMN_SETSENTENCEMODE: s:= 'IMN_SETSENTENCEMODE, ';
614 IMN_SETOPENSTATUS: s:= 'IMN_SETOPENSTATUS, ';
615 IMN_SETCANDIDATEPOS: s:= 'IMN_SETCANDIDATEPOS, ';
616 IMN_SETCOMPOSITIONFONT: s:= 'IMN_SETCOMPOSITIONFONT, ';
617 IMN_SETCOMPOSITIONWINDOW: s:= 'IMN_SETCOMPOSITIONWINDOW, ';
618 IMN_SETSTATUSWINDOWPOS: s:= 'IMN_SETSTATUSWINDOWPOS, ';
619 IMN_GUIDELINE: s:= 'IMN_GUIDELINE, ';
620 IMN_PRIVATE: s:= 'IMN_PRIVATE, ';
621 end;
622 debugln(['TCustomSynEdit.WMImeNotify ',s,' ', dbgHex(Msg.lParam), ' , ', dbgHex(Msg.wParam)]);
623 {$ENDIF}
624 end;
625
626 procedure LazSynImeFull.WMImeComposition(var Msg: TMessage);
627 var
628 CWidth: TPhysicalCharWidths;
629
CharToBytenull630 function CharToByte(AStart, AChars: integer): integer;
631 begin
632 if length(CWidth) = 0 then
633 CWidth := ViewedTextBuffer.GetPhysicalCharWidths(FImeBlockSelection.StartLinePos - 1);
634 dec(AStart);
635 Result := AStart;
636 while (AChars > 0) and (Result < length(CWidth)) do begin
637 inc(Result);
638 while (Result < length(CWidth)) and ((CWidth[Result] and PCWMask) = 0) do
639 inc(Result);
640 dec(AChars);
641 end;
642 Result := Result - AStart + AChars;
643 end;
644 var
645 {$IFDEF WinIMEDebug}
646 s: String;
647 {$ENDIF}
648 imc: HIMC;
649 p: PChar;
650 ImeCount: LongWord;
651 x, i: Integer;
652 xy: TPoint;
653 grp: Boolean;
654 {$IFDEF WinIMEFullDeferOverwrite}
655 sel, sel2: Boolean;
656 {$ENDIF}
657 begin
658 {$IFDEF WinIMEDebug}
659 s := '';
660 if (Msg.lparam and GCS_COMPREADSTR)<>0 then s := s + 'GCS_COMPREADSTR, ';
661 if (Msg.lparam and GCS_COMPREADATTR)<>0 then s := s + 'GCS_COMPREADATTR, ';
662 if (Msg.lparam and GCS_COMPREADCLAUSE)<>0 then s := s + 'GCS_COMPREADCLAUSE, ';
663 //if (Msg.lparam and GCS_COMPSTR)<>0 then s := s + 'GCS_COMPSTR, ';
664 //if (Msg.lparam and GCS_COMPATTR)<>0 then s := s + 'GCS_COMPATTR, ';
665 //if (Msg.lparam and GCS_COMPCLAUSE)<>0 then s := s + 'GCS_COMPCLAUSE, ';
666 //if (Msg.lparam and GCS_CURSORPOS)<>0 then s := s + 'GCS_CURSORPOS, ';
667 if (Msg.lparam and GCS_DELTASTART)<>0 then s := s + 'GCS_DELTASTART, ';
668 if (Msg.lparam and GCS_RESULTREADSTR)<>0 then s := s + 'GCS_RESULTREADSTR, ';
669 if (Msg.lparam and GCS_RESULTREADCLAUSE)<>0 then s := s + 'GCS_RESULTREADCLAUSE, ';
670 //if (Msg.lparam and GCS_RESULTSTR)<>0 then s := s + 'GCS_RESULTSTR, ';
671 if (Msg.lparam and GCS_RESULTCLAUSE)<>0 then s := s + 'GCS_RESULTCLAUSE, ';
672 if (Msg.lparam and CS_INSERTCHAR)<>0 then s := s + ' ** CS_INSERTCHAR, ';
673 if (Msg.lparam and CS_NOMOVECARET)<>0 then s := s + ' ** CS_NOMOVECARET, ';
674 if s <> '' then debugln(['TCustomSynEdit.WMImeComposition ', s]);
675 {$ENDIF}
676
677 if (Msg.LParam and (GCS_RESULTSTR or GCS_COMPSTR or GCS_CURSORPOS or GCS_COMPATTR {or GCS_COMPCLAUSE})) = 0 then
678 exit;
679
680 imc := 0;
681 FInImeMsg := True;
682 SetLength(CWidth, 0);
683 try
684
685 if ((Msg.LParam and GCS_RESULTSTR) <> 0) then begin
686 if imc = 0 then
687 imc := ImmGetContext(FriendEdit.Handle);
688 ImeCount := ImmGetCompositionStringW(imc, GCS_RESULTSTR, nil, 0);
689 {$IFDEF WinIMEDebug}
690 DebugLn(['--- GCS_RESULTSTR ', dbgHex(ImeCount)]);
691 {$ENDIF}
692 if ImeCount > 0 then begin
693 GetMem(p, ImeCount + 2);
694 try
695 SetImeTempText('');
696 CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos;
697 grp := ViewedTextBuffer.UndoList.GroupUndo;
698 ViewedTextBuffer.UndoList.GroupUndo := True;
699 TCustomSynEdit(FriendEdit).BeginUpdate;
700 ViewedTextBuffer.UndoList.CurrentReason := ecImeStr;
701 {$IFDEF WinIMEFullDeferOverwrite}
702 if FHasPersistLock then
703 SelectionObj.DecPersistentLock;
704 FHasPersistLock := False;
705 if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in TCustomSynEdit(FriendEdit).Options2)
706 then begin
707 SelectionObj.SelText := '';
708 FImeBlockSelection.StartLineBytePos := SelectionObj.StartLineBytePos;
709 end;
710 {$ENDIF}
711 CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos;
712 ImmGetCompositionStringW(imc, GCS_RESULTSTR, p, ImeCount + 2);
713 p[ImeCount] := #0;
714 p[ImeCount+1] := #0;
715 FImeBlockSelection.SelText := UTF16ToUTF8(PWCHAR(p));
716 FImeBlockSelection.StartLineBytePos := FImeBlockSelection.EndLineBytePos;
717 CaretObj.LineBytePos := FImeBlockSelection.StartLineBytePos;
718 {$IFnDEF WinIMEFullOverwriteSkipUndo}
719 FNeedUndoOnCancel := False;
720 {$ENDIF}
721 Msg.Result := 1;
722 finally
723 TCustomSynEdit(FriendEdit).EndUpdate;
724 ViewedTextBuffer.UndoList.GroupUndo := grp;
725 FreeMem(p, ImeCount + 2);
726 end;
727 end;
728 end;
729
730 if ((Msg.LParam and GCS_COMPSTR) <> 0) then begin
731 if imc = 0 then
732 imc := ImmGetContext(FriendEdit.Handle);
733 ImeCount := ImmGetCompositionStringW(imc, GCS_COMPSTR, nil, 0);
734 {$IFDEF WinIMEDebug}
735 DebugLn(['--- GCS_COMPSTR ', dbgHex(ImeCount)]);
736 {$ENDIF}
737 if ImeCount > 0 then begin
738 GetMem(p, ImeCount + 2);
739 try
740 ImmGetCompositionStringW(imc, GCS_COMPSTR, p, ImeCount + 2);
741 p[ImeCount] := #0;
742 p[ImeCount+1] := #0;
743 {$IFDEF WinIMEFullDeferOverwrite}
744 sel := (not SelectionObj.IsBackwardSel) and (CompareCarets(SelectionObj.EndLineBytePos, FImeBlockSelection.StartLineBytePos) = 0);
745 sel2 := SelectionObj.IsBackwardSel and (CompareCarets(SelectionObj.EndLineBytePos, FImeBlockSelection.EndLineBytePos) = 0);
746 {$ENDIF}
747 SetImeTempText(UTF16ToUTF8(PWCHAR(p)));
748 {$IFDEF WinIMEFullDeferOverwrite}
749 if sel then SelectionObj.EndLineBytePos := FImeBlockSelection.StartLineBytePos;
750 if sel2 then SelectionObj.EndLineBytePos := FImeBlockSelection.EndLineBytePos;
751 {$ENDIF}
752 Msg.Result := 1;
753 finally
754 FreeMem(p, ImeCount + 2);
755 end;
756 end;
757 end;
758
759 if ((Msg.LParam and GCS_COMPATTR) <> 0) then begin
760 //ATTR_INPUT = $00; // dotted undurline
761 //ATTR_TARGET_CONVERTED = $01; // full underline (bold underline / double width line)
762 //ATTR_CONVERTED = $02; // light underline (single width line)
763 //ATTR_TARGET_NOTCONVERTED = $03; // Show as selected ?
764 //ATTR_INPUT_ERROR = $04; // ? none
765 //ATTR_FIXEDCONVERTED = $05; // ? none
766 // low confidence => green underline.
767 if imc = 0 then
768 imc := ImmGetContext(FriendEdit.Handle);
769 ImeCount := ImmGetCompositionStringW(imc, GCS_COMPATTR, nil, 0);
770 {$IFDEF WinIMEDebug}
771 DebugLn(['***** GCS_COMPATTR ', dbgHex(ImeCount)]);
772 {$ENDIF}
773 if ImeCount > 0 then begin
774 FLeftPosForTarget := -1;
775 FRightPosForTarget := -1;
776 xy := FImeBlockSelection.StartLineBytePos;
777 FImeBlockSelection2.StartLineBytePos := xy;
778 FImeBlockSelection2.EndLineBytePos := xy;
779 FImeBlockSelection3.StartLineBytePos := xy;
780 FImeBlockSelection3.EndLineBytePos := xy;
781 GetMem(p, ImeCount + 2);
782 try
783 ImmGetCompositionStringW(imc, GCS_COMPATTR, p, ImeCount + 2);
784 DebugLn(dbgMemRange(PByte( p), ImeCount));
785 i := 0;
786 while longword(i) < ImeCount do begin
787 if ord(p[i]) = ATTR_TARGET_CONVERTED then begin
788 x := FImeBlockSelection.StartBytePos;
789 xy.x := x + CharToByte(x, i);
790 FImeBlockSelection2.StartLineBytePos := xy;
791 if (FLeftPosForTarget < 0) or (FLeftPosForTarget > xy.x) then
792 FLeftPosForTarget := xy.x;
793 inc(i);
794
795 while (longword(i) < ImeCount) and (ord(p[i]) = ATTR_TARGET_CONVERTED) do
796 inc(i);
797 xy.x := x + CharToByte(x, i);
798 FImeBlockSelection2.EndLineBytePos := xy;
799 if (FRightPosForTarget < 0) or (FRightPosForTarget < xy.x) then
800 FRightPosForTarget := xy.x;
801 //break;
802 end;
803
804 if ord(p[i]) = ATTR_TARGET_NOTCONVERTED then begin
805 x := FImeBlockSelection.StartBytePos;
806 xy.x := x + CharToByte(x, i);
807 if (FLeftPosForTarget < 0) or (FLeftPosForTarget > xy.x) then
808 FLeftPosForTarget := xy.x;
809 FImeBlockSelection3.StartLineBytePos := xy;
810 inc(i);
811
812 while (longword(i) < ImeCount) and (ord(p[i]) = ATTR_TARGET_NOTCONVERTED) do
813 inc(i);
814 xy.x := x + CharToByte(x, i);
815 FImeBlockSelection3.EndLineBytePos := xy;
816 if (FRightPosForTarget < 0) or (FRightPosForTarget < xy.x) then
817 FRightPosForTarget := xy.x;
818 //break;
819 end;
820
821 inc(i);
822 end;
823
824 Msg.Result := 1;
825 finally
826 FreeMem(p, ImeCount + 2);
827 end;
828
829 if (FLeftPosForTarget > 0) and FAdjustLeftCharForTargets then begin
830 FLeftPosForTarget := ViewedTextBuffer.LogicalToPhysicalPos
831 (Point(FLeftPosForTarget, FImeBlockSelection.FirstLineBytePos.Y)).x;
832 if FRightPosForTarget > 0 then
833 FRightPosForTarget := ViewedTextBuffer.LogicalToPhysicalPos
834 (Point(FRightPosForTarget, FImeBlockSelection.FirstLineBytePos.Y)).x;
835 EnsureLeftChar;
836 end;
837 end;
838 end;
839
840 (*
841 if ((Msg.LParam and GCS_COMPCLAUSE) <> 0) then begin
842 // attributes for all chars in any one clause should be the equal.
843 if imc = 0 then
844 imc := ImmGetContext(FriendEdit.Handle);
845 ImeCount := ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, nil, 0);
846 {$IFDEF WinIMEDebug}
847 DebugLn(['***** GCS_COMPCLAUSE ', dbgHex(ImeCount)]);
848 {$ENDIF}
849 if ImeCount > 0 then begin
850 GetMem(p, ImeCount + 2);
851 try
852 ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, p, ImeCount + 2);
853
854 DebugLn(dbgMemRange(PByte( p), ImeCount));
855 finally
856 FreeMem(p, ImeCount + 2);
857 end;
858 end;
859 end;
860 *)
861
862 if ((Msg.LParam and GCS_CURSORPOS) <> 0) then begin
863 if imc = 0 then
864 imc := ImmGetContext(FriendEdit.Handle);
865
866 ImeCount := ImmGetCompositionStringW(imc, GCS_CURSORPOS, nil, 0);
867 {$IFDEF WinIMEDebug}
868 DebugLn(['--- GCS_CURSORPOS ', dbgs(ImeCount), ' FLeftPosForTarget=',FLeftPosForTarget]);
869 {$ENDIF}
870 if ImeCount >= 0 then begin // ToDo: Comparison is always True.
871 ImeCount := ImeCount and $ffff;
872 x := FImeBlockSelection.StartBytePos;
873 x := x + CharToByte(x, ImeCount);
874 CaretObj.CharPos := ViewedTextBuffer.LogicalToPhysicalPos(Point(x, FImeBlockSelection.StartLinePos)).x;
875 // TODO: this causes full repaints
876 EnsureLeftChar;
877 end;
878 end;
879
880 finally
881 if imc <> 0 then
882 ImmReleaseContext(FriendEdit.Handle, imc);
883 FInImeMsg := False;
884 end;
885 inherited;
886 end;
887
888 procedure LazSynImeFull.WMImeStartComposition(var Msg: TMessage);
889 begin
890 //debugln(['TCustomSynEdit.WMImeStartComposition ']);
891 {$IFnDEF WinIMEFullDeferOverwrite}
892 if SelectionObj.SelAvail and (not SelectionObj.Persistent) and (eoOverwriteBlock in TCustomSynEdit(FriendEdit).Options2)
893 then begin
894 {$IFnDEF WinIMEFullOverwriteSkipUndo}
895 ViewedTextBuffer.UndoList.ForceGroupEnd;
896 FUndoStamp1 := ViewedTextBuffer.UndoList.PeekItem;
897 {$ENDIF}
898 TCustomSynEdit(FriendEdit).BeginUpdate;
899 ViewedTextBuffer.UndoList.CurrentReason := ecImeStr;
900 SelectionObj.SelText := '';
901 TCustomSynEdit(FriendEdit).EndUpdate;
902 {$IFnDEF WinIMEFullOverwriteSkipUndo}
903 FUndoStamp2 := ViewedTextBuffer.UndoList.PeekItem;
904 FNeedUndoOnCancel := FUndoStamp1 <> FUndoStamp2;
905 end
906 else begin
907 FNeedUndoOnCancel := False
908 {$ENDIF}
909 end;
910 {$ENDIF}
911 {$IFDEF WinIMEFullDeferOverwrite}
912 if not FHasPersistLock then
913 SelectionObj.IncPersistentLock;
914 FHasPersistLock := True;
915 {$ENDIF}
916
917 FImeMarkupSelection3.MarkupInfo.Assign(TCustomSynEdit(FriendEdit).SelectedColor);
918 FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos;
919 FInCompose := True;
920 Msg.Result := 1;
921 DoIMEStarted;
922 end;
923
924 procedure LazSynImeFull.WMImeEndComposition(var Msg: TMessage);
925 begin
926 //debugln(['TCustomSynEdit.WMImeEndComposition ']);
927 SetImeTempText('');
928 CaretObj.LineBytePos := FImeBlockSelection.LastLineBytePos;
929 {$IFnDEF WinIMEFullDeferOverwrite}
930 {$IFnDEF WinIMEFullOverwriteSkipUndo}
931 if FNeedUndoOnCancel and (ViewedTextBuffer.UndoList.PeekItem = FUndoStamp2) then
932 TCustomSynEdit(FriendEdit).Undo;
933 {$ENDIF}
934 {$ENDIF}
935 {$IFDEF WinIMEFullDeferOverwrite}
936 if FHasPersistLock then
937 SelectionObj.DecPersistentLock;
938 FHasPersistLock := False;
939 {$ENDIF}
940
941 FImeBlockSelection.StartLineBytePos := CaretObj.LineBytePos;
942 FImeBlockSelection2.StartLineBytePos := CaretObj.LineBytePos;
943 FInCompose := False;
944 Msg.Result := 1;
945 DoIMEEnded;
946 end;
947
948 procedure LazSynImeFull.FocusKilled;
949 begin
950 StopIme(True);
951 end;
952
953 end.
954
955