View Issue Details

IDProjectCategoryView StatusLast Update
0026369LazarusOtherpublic2017-07-07 01:04
ReporterDo-wan Kim Assigned To 
PrioritynormalSeverityminorReproducibilityN/A
Status newResolutionopen 
Platformx86-64OSlinux mint 
Product Version1.2.5 (SVN) 
Summary0026369: uim and synedit.
DescriptionAsian character input is not work in synedit currently.
Keyboard handling signal callback function has bug.

I made patch to work uim input method with synedit.
It also possible other TControl based component.

preedit-start, preedit-changed, preedit-end, commit signal call special function that work in control side.

This is experimental patch.
Tagspatch
Fixed in Revision
LazTarget
WidgetsetGTK 2
Attached Files

Relationships

related to 0027707 assignedMartin Friebe Source Editor(TSynEdit) doesn't respect "East Asian Width". 
related to 0013374 assignedDmitry Boyarintsev Cannot input Japanese character in IDE(SynEdit: GTK2, Carbon) 
related to 0030478 resolvedJuha Manninen Cannot input chinese character 

Activities

Do-wan Kim

2014-06-20 05:21

reporter  

uim-synedit-patch.diff (8,007 bytes)   
Index: components/synedit/synedit.pp
===================================================================
--- components/synedit/synedit.pp	(revision 45577)
+++ components/synedit/synedit.pp	(working copy)
@@ -562,6 +562,8 @@
     FOnMouseLink: TSynMouseLinkEvent;
     FPendingFoldState: String;
 
+    FgtkCommit: Boolean;
+
     procedure AquirePrimarySelection;
     function GetChangeStamp: int64;
     function GetCharsInWindow: Integer;
@@ -1131,6 +1133,9 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+  public
+    function gtk_PreeditCommit(const str: PChar; flag: Integer): Boolean;
+       override;
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4010,6 +4015,29 @@
   Result := FMarkupManager.Count;
 end;
 
+// for gtk input method. if return false, key skipped.
+function TCustomSynEdit.gtk_PreeditCommit(const str: PChar; flag: Integer
+  ): Boolean;
+begin
+  Result:=inherited gtk_PreeditCommit(str, flag);
+  if (flag=0) or (flag=2) then
+    FgtkCommit:=flag=0
+    else begin
+      if not fReadOnly then begin
+        if not FgtkCommit then begin
+          // delete on last char
+          if BiDiMode=bdLeftToRight then
+            CommandProcessor(ecSelLeft,'',nil)
+            else
+              CommandProcessor(ecSelRight,'',nil);
+        end;
+        CommandProcessor(ecChar,str,nil);
+        FgtkCommit:=flag=3;
+        Result:=False;
+      end;
+    end;
+end;
+
 procedure TCustomSynEdit.PasteFromClipboard;
 var
   ClipHelper: TSynClipboardStream;
Index: lcl/controls.pp
===================================================================
--- lcl/controls.pp	(revision 45577)
+++ lcl/controls.pp	(working copy)
@@ -1634,6 +1634,10 @@
     property HelpType: THelpType read FHelpType write FHelpType default htContext;
     property HelpKeyword: String read FHelpKeyword write SetHelpKeyword stored IsHelpKeyWordStored;
     property HelpContext: THelpContext read FHelpContext write SetHelpContext stored IsHelpContextStored default 0;
+  //{$ifdef LCLGTK2}
+  public
+    function gtk_PreeditCommit(const str:PChar;flag:Integer):Boolean; virtual;
+  //{$endif}
   end;
 
 
Index: lcl/include/control.inc
===================================================================
--- lcl/include/control.inc	(revision 45577)
+++ lcl/include/control.inc	(working copy)
@@ -5596,7 +5596,18 @@
   Result := UseRightToLeftReading;
 end;
 
+//{$ifdef LCLGTK2}
 {------------------------------------------------------------------------------
+       TControl.gtk_PreeditCommit
+------------------------------------------------------------------------------}
+
+function TControl.gtk_PreeditCommit(const str: PChar; flag: Integer): Boolean;
+begin
+  Result:=True;
+end;
+//{$endif}
+
+{------------------------------------------------------------------------------
        TControl.UseRightToLeftAlignment
 ------------------------------------------------------------------------------}
 
Index: lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lcl/interfaces/gtk2/gtk2globals.pp	(revision 45577)
+++ lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -75,6 +75,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipkey: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -421,6 +423,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipkey:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2proc.inc	(revision 45577)
+++ lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -1990,7 +1990,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2423,7 +2424,12 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_skipkey  then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+              else begin
+                Result:=True;
+                im_context_skipkey:=false;
+              end;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 45577)
+++ lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,67 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TControl;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TControl(GetNearestLCLObject(im_context_widget));
+        im_context_skipkey:=not Control.gtk_PreeditCommit(PChar(im_context_string),3);
+      end;
 end;
 
+//DW
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  Control:TControl;
+begin
+  im_context_use:=True;
+  if (im_context_widget<>nil) and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TControl(GetNearestLCLObject(im_context_widget));
+        im_context_skipkey:=not Control.gtk_PreeditCommit(PChar(''),0);
+      end;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  Control:TControl;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TControl(GetNearestLCLObject(im_context_widget));
+        im_context_skipkey := not Control.gtk_PreeditCommit(PChar(''),2);
+      end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TControl;
+  compostr:string;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    compostr:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    im_context_skipkey:=not control.gtk_PreeditCommit(PChar(compostr),1);
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1041,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin
uim-synedit-patch.diff (8,007 bytes)   

Zeljan Rikalo

2014-06-20 09:25

developer   ~0075813

This patch won't be commited since it introduces gtk specific stuff in LCL (TControl).Please change it so it affects gtk2 widgetset only.

Do-wan Kim

2014-06-20 10:36

reporter   ~0075816

I think this problem cannot solved without control modifications because there is no message like WM_IME_StartComposition & WM_IME_Comoposition & WM_IME_EndCompostion in gtk.

And End composition by non-composition character, one commit skip keybord handling signal callback.

Zeljan Rikalo

2014-06-20 11:23

developer   ~0075817

That means LCL changes and implementation for win32,qt and carbon/cocoa also.

Do-wan Kim

2014-06-20 12:35

reporter  

uim-synedit-patch-nocontrol.diff (6,483 bytes)   
Index: components/synedit/synedit.pp
===================================================================
--- components/synedit/synedit.pp	(revision 45586)
+++ components/synedit/synedit.pp	(working copy)
@@ -562,6 +562,7 @@
     FOnMouseLink: TSynMouseLinkEvent;
     FPendingFoldState: String;
 
+
     procedure AquirePrimarySelection;
     function GetChangeStamp: int64;
     function GetCharsInWindow: Integer;
@@ -1131,6 +1132,8 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+  protected
+    function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4010,6 +4013,18 @@
   Result := FMarkupManager.Count;
 end;
 
+function TCustomSynEdit.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
+begin
+  // for gtk input method.
+  if (UTF8Key=#8) and (not ReadOnly) then begin
+    if BiDiMode=bdLeftToRight then
+      CommandProcessor(ecSelLeft,#0,nil)
+      else
+        CommandProcessor(ecSelRight,#0,nil);
+  end;
+  Result:=inherited DoUTF8KeyPress(UTF8Key);
+end;
+
 procedure TCustomSynEdit.PasteFromClipboard;
 var
   ClipHelper: TSynClipboardStream;
Index: lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lcl/interfaces/gtk2/gtk2globals.pp	(revision 45586)
+++ lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -75,6 +75,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipDelete: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -421,6 +423,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipDelete:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2proc.inc	(revision 45586)
+++ lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -1990,7 +1990,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2423,7 +2424,10 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_use then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+            else
+              Result:=True;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 45586)
+++ lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,82 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TWinControl;
+  temp:TUTF8Char;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TWinControl(GetNearestLCLObject(im_context_widget));
+        if not im_context_skipDelete then begin
+          temp:=#8;
+          Control.IntfUTF8KeyPress(temp,1,False);
+        end;
+        temp:=UTF8Copy(im_context_string,1,1);
+        Control.IntfUTF8KeyPress(temp,1,False);
+        im_context_skipDelete:=True;
+      end;
 end;
 
+//DW
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_use:=True;
+    im_context_skipDelete:=True;
+  end else
+    im_context_use:=False;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_skipDelete:=True;
+  end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TWinControl;
+  compostr:string;
+  temp:TUTF8Char;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    compostr:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    if not im_context_skipDelete then begin
+      temp:=#8;
+      control.IntfUTF8KeyPress(temp,1,False);
+    end else
+      im_context_skipDelete:=False;
+    temp:=UTF8Copy(compostr,1,1);
+    control.IntfUTF8KeyPress(temp,1,False);
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1056,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin

Do-wan Kim

2014-06-20 12:39

reporter   ~0075819

I make another patch.
It has no TContol modification.
But still problem in candidate window and special characters.

Martin Friebe

2014-06-20 16:01

manager   ~0075824

I will look at it in more detail, concerning the SynEdit parts.

+ if (UTF8Key=0000008) and (not ReadOnly) then begin
+ if BiDiMode=bdLeftToRight then

Does not belong into the UIM part. Bidi can happen without UIM too. And it is definitely wrong. Caret moves inside the text in memory. So this makes no sense.
Bidi does not affect the in mem representation, only visuals.


@Zeljko: If you commit any GTK stuff, it would be nice if SynEdit could hook the messages. Then it can paint composition in its own clientarea

Do-wan Kim

2014-06-20 23:55

reporter   ~0075828

Last edited: 2014-07-21 03:10

View 5 revisions

aha, TIA.

(edit) unpredicted synedit selection by special key was solved.

(edit) backspace now works in composition mode.

(edit) fixed bug adding last char at ending composition by pressing non-composition key.

Candidate window is not work with key press, but works with mouse click.

Do-wan Kim

2014-06-21 01:01

reporter  

uim-synedit-patch-nocontrol-0.diff (6,189 bytes)   
Index: components/synedit/synedit.pp
===================================================================
--- components/synedit/synedit.pp	(revision 45588)
+++ components/synedit/synedit.pp	(working copy)
@@ -1131,6 +1131,8 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+  protected
+    function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4010,6 +4012,14 @@
   Result := FMarkupManager.Count;
 end;
 
+function TCustomSynEdit.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
+begin
+  // for gtk input method.
+  if (UTF8Key=#8) and (not ReadOnly) then
+    CommandProcessor(ecSelLeft,#0,nil);
+  Result:=inherited DoUTF8KeyPress(UTF8Key);
+end;
+
 procedure TCustomSynEdit.PasteFromClipboard;
 var
   ClipHelper: TSynClipboardStream;
Index: lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lcl/interfaces/gtk2/gtk2globals.pp	(revision 45588)
+++ lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -75,6 +75,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipDelete: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -421,6 +423,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipDelete:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2proc.inc	(revision 45588)
+++ lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -1990,7 +1990,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2423,7 +2424,10 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_use then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+            else
+              Result:=True;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 45588)
+++ lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,82 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TWinControl;
+  temp:TUTF8Char;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TWinControl(GetNearestLCLObject(im_context_widget));
+        if not im_context_skipDelete then begin
+          temp:=#8;
+          Control.IntfUTF8KeyPress(temp,1,False);
+        end;
+        temp:=UTF8Copy(im_context_string,1,1);
+        Control.IntfUTF8KeyPress(temp,1,False);
+        im_context_skipDelete:=True;
+      end;
 end;
 
+//DW
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_use:=True;
+    im_context_skipDelete:=True;
+  end else
+    im_context_use:=False;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_skipDelete:=True;
+  end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TWinControl;
+  compostr:string;
+  temp:TUTF8Char;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    compostr:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    if (not im_context_skipDelete) and (Length(compostr)>0) then begin
+      temp:=#8;
+      control.IntfUTF8KeyPress(temp,1,False);
+    end else
+      im_context_skipDelete:=False;
+    temp:=UTF8Copy(compostr,1,1);
+    control.IntfUTF8KeyPress(temp,1,False);
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1056,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin

Do-wan Kim

2014-06-23 09:18

reporter  

uim-synedit-patch-nocontrol-1.diff (6,548 bytes)   
Index: components/synedit/synedit.pp
===================================================================
--- components/synedit/synedit.pp	(revision 45619)
+++ components/synedit/synedit.pp	(working copy)
@@ -1131,6 +1131,8 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+  protected
+    function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4010,6 +4012,14 @@
   Result := FMarkupManager.Count;
 end;
 
+function TCustomSynEdit.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
+begin
+  // for gtk input method.
+  if (UTF8Key=#8) and (not ReadOnly) then
+    CommandProcessor(ecSelLeft,#0,nil);
+  Result:=inherited DoUTF8KeyPress(UTF8Key);
+end;
+
 procedure TCustomSynEdit.PasteFromClipboard;
 var
   ClipHelper: TSynClipboardStream;
Index: lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lcl/interfaces/gtk2/gtk2globals.pp	(revision 45619)
+++ lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -78,6 +78,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipDelete: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -424,6 +426,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipDelete:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2proc.inc	(revision 45619)
+++ lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -1990,7 +1990,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2351,7 +2352,7 @@
 
       Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {TODO:  repeatcount};
 
-      if not KeyAlreadyHandledByGtk
+      if (not KeyAlreadyHandledByGtk) and (not im_context_use)
       then begin
         // send the (Sys)KeyDown message directly to the LCL
         NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
@@ -2423,7 +2424,10 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_use then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+            else
+              Result:=True;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 45619)
+++ lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,82 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TWinControl;
+  temp:TUTF8Char;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TWinControl(GetNearestLCLObject(im_context_widget));
+        if not im_context_skipDelete then begin
+          temp:=#8;
+          Control.IntfUTF8KeyPress(temp,1,False);
+        end;
+        temp:=UTF8Copy(im_context_string,1,1);
+        Control.IntfUTF8KeyPress(temp,1,False);
+        im_context_skipDelete:=True;
+      end;
 end;
 
+//DW
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_use:=True;
+    im_context_skipDelete:=True;
+  end else
+    im_context_use:=False;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_skipDelete:=True;
+  end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TWinControl;
+  compostr:string;
+  temp:TUTF8Char;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    compostr:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    if (not im_context_skipDelete) and (Length(compostr)>0) then begin
+      temp:=#8;
+      control.IntfUTF8KeyPress(temp,1,False);
+    end else
+      im_context_skipDelete:=False;
+    temp:=UTF8Copy(compostr,1,1);
+    control.IntfUTF8KeyPress(temp,1,False);
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1056,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin

Do-wan Kim

2014-07-06 00:23

reporter  

uim-synedit-nocontrol-2.diff (6,887 bytes)   
Index: components/synedit/synedit.pp
===================================================================
--- components/synedit/synedit.pp	(revision 45786)
+++ components/synedit/synedit.pp	(working copy)
@@ -1131,6 +1131,8 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+  protected
+    function DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean; override;
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4010,6 +4012,14 @@
   Result := FMarkupManager.Count;
 end;
 
+function TCustomSynEdit.DoUTF8KeyPress(var UTF8Key: TUTF8Char): boolean;
+begin
+  // for gtk input method.
+  if (UTF8Key=#8) and (not ReadOnly) then
+    CommandProcessor(ecSelLeft,#0,nil);
+  Result:=inherited DoUTF8KeyPress(UTF8Key);
+end;
+
 procedure TCustomSynEdit.PasteFromClipboard;
 var
   ClipHelper: TSynClipboardStream;
Index: lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lcl/interfaces/gtk2/gtk2globals.pp	(revision 45786)
+++ lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -78,6 +78,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipDelete: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -424,6 +426,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipDelete:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2proc.inc	(revision 45786)
+++ lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -1993,7 +1993,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2000,7 +2001,7 @@
   begin
     Result := false;
     if AWidget = nil then exit;
-    
+
     if GtkWidgetIsA(AWidget, gtk_entry_get_type)
     then begin
       // the gtk_entry handles the following keys
@@ -2354,7 +2355,7 @@
 
       Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {TODO:  repeatcount};
 
-      if not KeyAlreadyHandledByGtk
+      if (not KeyAlreadyHandledByGtk) and (not im_context_use)
       then begin
         // send the (Sys)KeyDown message directly to the LCL
         NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
@@ -2426,7 +2427,10 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_use then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+            else
+              Result:=True;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 45786)
+++ lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,85 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TWinControl;
+  temp:TUTF8Char;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  { Handle commit string at ending composition,
+    it cannot handled by HandleGTKKeyUpDown }
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TWinControl(GetNearestLCLObject(im_context_widget));
+        if not im_context_skipDelete then begin
+          temp:=#8;
+          Control.IntfUTF8KeyPress(temp,1,False);
+        end;
+        temp:=UTF8Copy(im_context_string,1,1);
+        Control.IntfUTF8KeyPress(temp,1,False);
+        im_context_string:='';
+        im_context_skipDelete:=True;
+      end;
 end;
 
+//DW
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_use:=True;
+    im_context_skipDelete:=True;
+  end else
+    im_context_use:=False;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_skipDelete:=True;
+  end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TWinControl;
+  compostr:string;
+  temp:TUTF8Char;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    compostr:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    if (not im_context_skipDelete) and (Length(compostr)>0) then begin
+      temp:=#8;
+      control.IntfUTF8KeyPress(temp,1,False);
+    end else
+      im_context_skipDelete:=False;
+    temp:=UTF8Copy(compostr,1,1);
+    control.IntfUTF8KeyPress(temp,1,False);
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1059,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin
uim-synedit-nocontrol-2.diff (6,887 bytes)   

Burak OZCAN

2015-02-04 17:12

reporter   ~0080787

please look http://forum.lazarus.freepascal.org/index.php/topic,6231.15.html
I solve it

Do-wan Kim

2015-02-05 01:54

reporter  

gtk_im_synedit.patch (8,312 bytes)   
Index: lazarus/components/synedit/synedit.pp
===================================================================
--- lazarus/components/synedit/synedit.pp	(revision 47587)
+++ lazarus/components/synedit/synedit.pp	(working copy)
@@ -1142,6 +1142,10 @@
     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
+{$ifdef LCLGtk2}
+  protected
+    procedure GTK_IMComposition(var Message:TMessage); message LM_IM_COMPOSITION;
+{$endif}
   end;
 
   TSynEdit = class(TCustomSynEdit)
@@ -4029,6 +4033,26 @@
   Result := FMarkupManager.Count;
 end;
 
+{$ifdef LCLGtk2}
+procedure TCustomSynEdit.GTK_IMComposition(var Message: TMessage);
+var
+  IMStr:TUTF8Char;
+begin
+  if (not ReadOnly) then
+  begin
+    // delete previous character by selection
+    if Message.WParam and GTK_IM_FLAG_REPLACE<>0 then
+      CommandProcessor(ecSelLeft,#0,nil);
+    // valid string at composition & commit
+    if Message.WParam and (GTK_IM_FLAG_START or GTK_IM_FLAG_END)=0 then
+    begin
+      IMStr:=pchar(Message.LParam);
+      UTF8KeyPress(IMStr);
+    end;
+  end;
+end;
+{$endif}
+
 procedure TCustomSynEdit.SetCaretTypeSize(AType: TSynCaretType; AWidth, AHeight, AXOffs,
   AYOffs: Integer);
 begin
Index: lazarus/lcl/interfaces/gtk2/gtk2globals.pp
===================================================================
--- lazarus/lcl/interfaces/gtk2/gtk2globals.pp	(revision 47587)
+++ lazarus/lcl/interfaces/gtk2/gtk2globals.pp	(working copy)
@@ -78,6 +78,8 @@
   im_context: PGtkIMContext = nil;
   im_context_widget: PGtkWidget = nil;
   im_context_string: string = '';
+  im_context_use: Boolean = False; //DW
+  im_context_skipDelete: Boolean = False;
 
 procedure ResetDefaultIMContext;
 
@@ -424,6 +426,8 @@
   end;
   im_context_widget:=nil;
   im_context_string:='';
+  im_context_use:=False; //DW
+  im_context_skipDelete:=False;
 end;
 
 procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
Index: lazarus/lcl/interfaces/gtk2/gtk2proc.inc
===================================================================
--- lazarus/lcl/interfaces/gtk2/gtk2proc.inc	(revision 47587)
+++ lazarus/lcl/interfaces/gtk2/gtk2proc.inc	(working copy)
@@ -2000,7 +2000,8 @@
       end;
       Exit;
     end;
-    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
+    //DW
+    Result := ((not im_context_use) and (AEvent^.Length > 0)) or (GetSpecialChar <> #0);
   end;
   
   function KeyAlreadyHandledByGtk: boolean;
@@ -2007,7 +2008,7 @@
   begin
     Result := false;
     if AWidget = nil then exit;
-    
+
     if GtkWidgetIsA(AWidget, gtk_entry_get_type)
     then begin
       // the gtk_entry handles the following keys
@@ -2361,7 +2362,7 @@
 
       Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {TODO:  repeatcount};
 
-      if not KeyAlreadyHandledByGtk
+      if (not KeyAlreadyHandledByGtk) and (not im_context_use)
       then begin
         // send the (Sys)KeyDown message directly to the LCL
         NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
@@ -2433,7 +2434,10 @@
         begin
           OldCharacter := Character;
           // send the key after navigation keys were handled
-          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
+          if not im_context_use then
+            Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey)
+            else
+              Result:=True;
           if Result or (Character = '') then
             // dont' stop key event here, just clear it since we need a keyUp event
             ClearKey
Index: lazarus/lcl/interfaces/gtk2/gtk2widgetset.inc
===================================================================
--- lazarus/lcl/interfaces/gtk2/gtk2widgetset.inc	(revision 47587)
+++ lazarus/lcl/interfaces/gtk2/gtk2widgetset.inc	(working copy)
@@ -216,11 +216,88 @@
 
 procedure gtk_commit_cb ({%H-}context: PGtkIMContext; const Str: Pgchar;
   {%H-}Data: Pointer); cdecl;
+var
+  Control:TWinControl;
+  Flag:WPARAM;
 begin
   //DebugLn(['gtk_commit_cb ',dbgstr(Str),'="',Str,'"']);
   im_context_string:=Str;
+  { Handle commit string at ending composition,
+    it cannot handled by HandleGTKKeyUpDown }
+  if im_context_use and (im_context_widget<>nil) and (im_context_string<>'') and
+     gtk_widget_is_focus(im_context_widget) then
+      begin
+        Control:=TWinControl(GetNearestLCLObject(im_context_widget));
+        Flag:=GTK_IM_FLAG_COMMIT;
+        if not im_context_skipDelete then
+          Flag:=Flag or GTK_IM_FLAG_REPLACE;
+        SendMessage(Control.Handle,LM_IM_COMPOSITION,Flag,LPARAM(pchar(im_context_string)));
+        im_context_string:='';
+        im_context_skipDelete:=True;
+      end;
 end;
 
+procedure gtk_preedit_start_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+  Flag:WPARAM;
+begin
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_use:=True;
+    im_context_skipDelete:=True;
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    Flag:=GTK_IM_FLAG_START;
+    SendMessage(Control.Handle,LM_IM_COMPOSITION,Flag,LPARAM(Data));
+  end else
+    im_context_use:=False;
+end;
+
+procedure gtk_preedit_end_cb({%H-}context: PGtkIMContext; {%H-}Data: Pointer); cdecl;
+var
+  control:TWinControl;
+  Flag:WPARAM;
+begin
+  im_context_use:=False;
+  if (im_context_widget<>nil) and
+     (gtk_widget_is_focus(im_context_widget)) and
+     (GetNearestLCLObject(im_context_widget) is TCustomControl) then
+  begin
+    im_context_skipDelete:=True;
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    Flag:=GTK_IM_FLAG_END;
+    SendMessage(control.Handle,LM_IM_COMPOSITION,Flag,LPARAM(Data));
+  end;
+end;
+
+procedure gtk_preedit_changed_cb({%H-}context:PGtkIMContext; {%H-}Data:Pointer); cdecl;
+var
+  str:Pgchar;
+  pangoattr:PPangoAttrList;
+  pos:gint;
+  control:TWinControl;
+  Flag:WPARAM;
+begin
+  if im_context_use and (im_context_widget<>nil) and
+    gtk_widget_is_focus(im_context_widget) then
+  begin
+    control:=TWinControl(GetNearestLCLObject(im_context_widget));
+    pos:=1;
+    gtk_im_context_get_preedit_string(context,@str,pangoattr,@pos);
+    im_context_string:=str;
+    g_free(str);
+    pango_attr_list_unref(pangoattr);
+    Flag:=GTK_IM_FLAG_COMPOSITION;
+    if (not im_context_skipDelete) and (Length(im_context_string)>0) then begin
+      Flag:=Flag or GTK_IM_FLAG_REPLACE;
+    end else
+      im_context_skipDelete:=False;
+    SendMessage(control.Handle,LM_IM_COMPOSITION,Flag,LPARAM(pchar(im_context_string)));
+  end;
+end;
+
 {$IfNDef GTK2_2}
 procedure gtkTreeSelectionCountSelectedRows({%H-}model : PGtkTreeModel; {%H-}path : PGtkTreePath;
                                   {%H-}iter : PGtkTreeIter; data : PGint); cdecl;
@@ -985,6 +1062,13 @@
   im_context:=gtk_im_multicontext_new;
   g_signal_connect (G_OBJECT (im_context), 'commit',
     G_CALLBACK (@gtk_commit_cb), nil);
+  //DW
+  g_signal_connect (G_OBJECT (im_context), 'preedit-start',
+    G_CALLBACK (@gtk_preedit_start_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-end',
+    G_CALLBACK (@gtk_preedit_end_cb), nil);
+  g_signal_connect (G_OBJECT (im_context), 'preedit-changed',
+    G_CALLBACK (@gtk_preedit_changed_cb), nil);
   {$IFDEF HASX}
   if IsNoTransientWM then
   begin
Index: lazarus/lcl/lmessages.pp
===================================================================
--- lazarus/lcl/lmessages.pp	(revision 47587)
+++ lazarus/lcl/lmessages.pp	(working copy)
@@ -93,7 +93,15 @@
   LM_INTERFACELAST  = LM_LCL + 199;
   
   LM_UNKNOWN        = LM_INTERFACELAST + 1;
+  LM_IM_COMPOSITION = LM_UNKNOWN + 1; // gtk IM
 
+  // GTK IM Flags
+  GTK_IM_FLAG_START       = 1;
+  GTK_IM_FLAG_COMPOSITION = 2;
+  GTK_IM_FLAG_END         = 4;
+  GTK_IM_FLAG_COMMIT      = 8;
+  GTK_IM_FLAG_REPLACE     = 16;
+
   //-------------
   //end of messages that are sent to the interface
   //-------------
gtk_im_synedit.patch (8,312 bytes)   

Do-wan Kim

2015-02-05 02:01

reporter   ~0080794

Thank you for link url :)

It is different patch working on UIM, it actually not double input patch.
My patch is enabling asian characters on UIM, but not perfect.

隆博

2017-07-07 01:04

reporter   ~0101559

I patched the latest version of trunk and tried it

I tested it with mozc(uim-mozc), anthy (utf 8) on debian9.

This patch will allow to display the IME and display conversion candidates,
but fixed characters will be broken.

Converting one character will result in one character.

When two characters are converted, one more character appears after three characters appear, resulting in a total of four characters.


input: あ + ENTER(submit)
expected result: あ
actual result: あ + LF

input: あい + ENTER(submit)
expected result: あい
actual result: ああい -- (submit) --> あああい + LF

input: あいう + ENTER(submit)
expected result: あいう
actual result: あああいあい + invalid charactor
   -- (submit) --> あああいあいあい + invalid charactor + LF

I do not use the Linux development environment and do not investigate any further.

Issue History

Date Modified Username Field Change
2014-06-20 05:21 Do-wan Kim New Issue
2014-06-20 05:21 Do-wan Kim File Added: uim-synedit-patch.diff
2014-06-20 09:25 Zeljan Rikalo Note Added: 0075813
2014-06-20 10:36 Do-wan Kim Note Added: 0075816
2014-06-20 11:23 Zeljan Rikalo Note Added: 0075817
2014-06-20 12:35 Do-wan Kim File Added: uim-synedit-patch-nocontrol.diff
2014-06-20 12:39 Do-wan Kim Note Added: 0075819
2014-06-20 16:01 Martin Friebe Note Added: 0075824
2014-06-20 23:55 Do-wan Kim Note Added: 0075828
2014-06-21 01:01 Do-wan Kim File Added: uim-synedit-patch-nocontrol-0.diff
2014-06-21 01:03 Do-wan Kim Note Edited: 0075828 View Revisions
2014-06-23 09:18 Do-wan Kim File Added: uim-synedit-patch-nocontrol-1.diff
2014-06-23 09:19 Do-wan Kim Note Edited: 0075828 View Revisions
2014-07-06 00:23 Do-wan Kim File Added: uim-synedit-nocontrol-2.diff
2014-07-06 00:25 Do-wan Kim Note Edited: 0075828 View Revisions
2014-07-21 03:10 Do-wan Kim Note Edited: 0075828 View Revisions
2014-09-09 17:28 Juha Manninen Tag Attached: patch
2015-02-04 17:12 Burak OZCAN Note Added: 0080787
2015-02-05 01:54 Do-wan Kim File Added: gtk_im_synedit.patch
2015-02-05 02:01 Do-wan Kim Note Added: 0080794
2016-03-16 20:32 Juha Manninen Relationship added related to 0027707
2016-03-16 20:51 Juha Manninen Relationship added related to 0013374
2016-09-13 15:07 Juha Manninen Relationship added related to 0030478
2017-07-07 01:04 隆博 Note Added: 0101559