5 changed files with 278 additions and 0 deletions
@ -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 @@ |
|||||||
|
// 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