diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
index 5c6999c1fd..c82f906040 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
@@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{
readonly TextArea textArea;
readonly TextView textView;
+ readonly ImeSupport ime;
readonly CaretLayer caretAdorner;
bool visible;
@@ -30,6 +31,7 @@ namespace ICSharpCode.AvalonEdit.Editing
this.textArea = textArea;
this.textView = textArea.TextView;
position = new TextViewPosition(1, 1, 0);
+ ime = new ImeSupport(textArea);
caretAdorner = new CaretLayer(textView);
textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
@@ -434,6 +436,7 @@ namespace ICSharpCode.AvalonEdit.Editing
} else {
caretAdorner.Hide();
}
+ ime.UpdateCompositionWindow();
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeNativeWrapper.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeNativeWrapper.cs
new file mode 100644
index 0000000000..9b4d40684a
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeNativeWrapper.cs
@@ -0,0 +1,156 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using ICSharpCode.AvalonEdit;
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.AvalonEdit.Utils;
+
+namespace ICSharpCode.AvalonEdit.Editing
+{
+ ///
+ /// Native API required for IME support.
+ ///
+ static class ImeNativeWrapper
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ struct CompositionForm
+ {
+ public int dwStyle;
+ public POINT ptCurrentPos;
+ public RECT rcArea;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct POINT
+ {
+ public int x;
+ public int y;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct RECT
+ {
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
+ class LOGFONT
+ {
+ public int lfHeight = 0;
+ public int lfWidth = 0;
+ public int lfEscapement = 0;
+ public int lfOrientation = 0;
+ public int lfWeight = 0;
+ public byte lfItalic = 0;
+ public byte lfUnderline = 0;
+ public byte lfStrikeOut = 0;
+ public byte lfCharSet = 0;
+ public byte lfOutPrecision = 0;
+ public byte lfClipPrecision = 0;
+ public byte lfQuality = 0;
+ public byte lfPitchAndFamily = 0;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName = null;
+ }
+
+ const int CPS_CANCEL = 0x4;
+ const int NI_COMPOSITIONSTR = 0x15;
+ const int GCS_COMPSTR = 0x0008;
+ public const int WM_IME_COMPOSITION = 0x10F;
+
+ [DllImport("imm32.dll")]
+ static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
+ [DllImport("imm32.dll")]
+ static extern IntPtr ImmGetContext(IntPtr hWnd);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form);
+
+ public static IntPtr AssociateContext(HwndSource source, IntPtr hIMC)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+ return ImmAssociateContext(source.Handle, hIMC);
+ }
+
+ public static bool NotifyIme(IntPtr hIMC)
+ {
+ return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL);
+ }
+
+ public static IntPtr GetContext(HwndSource source)
+ {
+ if (source == null)
+ return IntPtr.Zero;
+ return ImmGetContext(source.Handle);
+ }
+
+ public static bool ReleaseContext(HwndSource source, IntPtr hIMC)
+ {
+ return source != null && hIMC != IntPtr.Zero && ImmReleaseContext(source.Handle, hIMC);
+ }
+
+ public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ Rect textViewBounds = textArea.TextView.GetBounds();
+ Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source);
+ if (source != null) {
+ Matrix transformToDevice = source.CompositionTarget.TransformToDevice;
+ textViewBounds.Transform(transformToDevice);
+ characterBounds.Transform(transformToDevice);
+ }
+ CompositionForm form = new CompositionForm();
+ form.dwStyle = 0x0020;
+ form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left);
+ form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top);
+ form.rcArea.left = (int)textViewBounds.Left;
+ form.rcArea.top = (int)textViewBounds.Top;
+ form.rcArea.right = (int)textViewBounds.Right;
+ form.rcArea.bottom = (int)textViewBounds.Bottom;
+ return ImmSetCompositionWindow(hIMC, ref form);
+ }
+
+ static Rect GetBounds(this TextView textView)
+ {
+ Point location = textView.TranslatePoint(new Point(0,0), textView);
+ return new Rect(location, new Size(textView.ActualWidth, textView.ActualHeight));
+ }
+
+ static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source)
+ {
+ VisualLine vl = textView.GetVisualLine(pos.Line);
+ if (vl == null)
+ throw new Exception();
+ TextLine line = vl.GetTextLine(pos.VisualColumn);
+ double offset = vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop) - textView.ScrollOffset.Y;
+ Rect r = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle;
+ r.Offset(-textView.ScrollOffset.X, offset);
+ Point pointOnRootVisual = textView.TransformToAncestor(source.RootVisual).Transform(r.Location);
+ Point pointOnHwnd = pointOnRootVisual.TransformToDevice(source.RootVisual);
+ r.Location = pointOnHwnd;
+ return r;
+ }
+ }
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeSupport.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeSupport.cs
new file mode 100644
index 0000000000..5e3082b0dd
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeSupport.cs
@@ -0,0 +1,100 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using ICSharpCode.AvalonEdit;
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Rendering;
+
+namespace ICSharpCode.AvalonEdit.Editing
+{
+ class ImeSupport : IDisposable
+ {
+ TextArea textArea;
+ IntPtr currentContext;
+ IntPtr previousContext;
+ HwndSource hwndSource;
+
+ public ImeSupport(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ InputMethod.SetIsInputMethodSuspended(this.textArea, true);
+ textArea.GotKeyboardFocus += TextAreaGotKeyboardFocus;
+ textArea.LostKeyboardFocus += TextAreaLostKeyboardFocus;
+ currentContext = IntPtr.Zero;
+ previousContext = IntPtr.Zero;
+ }
+
+ public void Dispose()
+ {
+ if (textArea != null) {
+ textArea.GotKeyboardFocus -= TextAreaGotKeyboardFocus;
+ textArea.LostKeyboardFocus -= TextAreaLostKeyboardFocus;
+ textArea = null;
+ }
+ ClearContext();
+ }
+
+ void ClearContext()
+ {
+ if (hwndSource != null) {
+ hwndSource.RemoveHook(WndProc);
+ ImeNativeWrapper.AssociateContext(hwndSource, previousContext);
+ previousContext = IntPtr.Zero;
+ ImeNativeWrapper.ReleaseContext(hwndSource, currentContext);
+ hwndSource = null;
+ currentContext = IntPtr.Zero;
+ }
+ }
+
+ void TextAreaGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+ {
+ if (this.textArea == null)
+ return;
+ if (e.OriginalSource != this.textArea)
+ return;
+ hwndSource = (HwndSource)PresentationSource.FromVisual(this.textArea);
+ if (hwndSource != null) {
+ currentContext = ImeNativeWrapper.GetContext(hwndSource);
+ previousContext = ImeNativeWrapper.AssociateContext(hwndSource, currentContext);
+ hwndSource.AddHook(WndProc);
+ }
+ }
+
+ void TextAreaLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+ {
+ if (e.OriginalSource != this.textArea)
+ return;
+ if (currentContext != IntPtr.Zero)
+ ImeNativeWrapper.NotifyIme(currentContext);
+ ClearContext();
+ }
+
+ IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ if (msg == ImeNativeWrapper.WM_IME_COMPOSITION)
+ UpdateCompositionWindow();
+ return IntPtr.Zero;
+ }
+
+ public void UpdateCompositionWindow()
+ {
+ if (currentContext != IntPtr.Zero && textArea != null) {
+ ImeNativeWrapper.SetCompositionWindow(hwndSource, currentContext, textArea);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
index 3d644e2710..b45a6fb85a 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
@@ -147,7 +147,9 @@
Selection.cs
+
+
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs
index df95a8d488..95ee2498c7 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs
@@ -381,5 +381,22 @@ namespace ICSharpCode.AvalonEdit
}
}
}
+
+ bool enableImeSupport;
+
+ ///
+ /// Gets/Sets whether the support for Input Method Editors (IME)
+ /// for non-alphanumeric scripts (Chinese, Japanese, Korean, ...) is enabled.
+ ///
+ [DefaultValue(true)]
+ public virtual bool EnableImeSupport {
+ get { return enableImeSupport; }
+ set {
+ if (enableImeSupport != value) {
+ enableImeSupport = value;
+ OnPropertyChanged("EnableImeSupport");
+ }
+ }
+ }
}
}