5 changed files with 278 additions and 0 deletions
@ -0,0 +1,156 @@
@@ -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 |
||||
{ |
||||
/// <summary>
|
||||
/// Native API required for IME support.
|
||||
/// </summary>
|
||||
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; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,100 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue