mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
8.4 KiB
221 lines
8.4 KiB
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
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; |
|
using Draw = System.Drawing; |
|
|
|
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.Unicode)] |
|
struct LOGFONT |
|
{ |
|
public int lfHeight; |
|
public int lfWidth; |
|
public int lfEscapement; |
|
public int lfOrientation; |
|
public int lfWeight; |
|
public byte lfItalic; |
|
public byte lfUnderline; |
|
public byte lfStrikeOut; |
|
public byte lfCharSet; |
|
public byte lfOutPrecision; |
|
public byte lfClipPrecision; |
|
public byte lfQuality; |
|
public byte lfPitchAndFamily; |
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName; |
|
} |
|
|
|
const int CPS_CANCEL = 0x4; |
|
const int NI_COMPOSITIONSTR = 0x15; |
|
const int GCS_COMPSTR = 0x0008; |
|
|
|
public const int WM_IME_COMPOSITION = 0x10F; |
|
public const int WM_IME_SETCONTEXT = 0x281; |
|
public const int WM_INPUTLANGCHANGE = 0x51; |
|
|
|
[DllImport("imm32.dll")] |
|
public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); |
|
[DllImport("imm32.dll")] |
|
internal static extern IntPtr ImmGetContext(IntPtr hWnd); |
|
[DllImport("imm32.dll")] |
|
internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd); |
|
[DllImport("imm32.dll")] |
|
[return: MarshalAs(UnmanagedType.Bool)] |
|
internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC); |
|
[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 ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form); |
|
[DllImport("imm32.dll", CharSet = CharSet.Unicode)] |
|
[return: MarshalAs(UnmanagedType.Bool)] |
|
static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font); |
|
[DllImport("imm32.dll")] |
|
[return: MarshalAs(UnmanagedType.Bool)] |
|
static extern bool ImmGetCompositionFont(IntPtr hIMC, out LOGFONT font); |
|
|
|
[DllImport("msctf.dll")] |
|
static extern int TF_CreateThreadMgr(out ITfThreadMgr threadMgr); |
|
|
|
[ThreadStatic] static bool textFrameworkThreadMgrInitialized; |
|
[ThreadStatic] static ITfThreadMgr textFrameworkThreadMgr; |
|
|
|
public static ITfThreadMgr GetTextFrameworkThreadManager() |
|
{ |
|
if (!textFrameworkThreadMgrInitialized) { |
|
textFrameworkThreadMgrInitialized = true; |
|
TF_CreateThreadMgr(out textFrameworkThreadMgr); |
|
} |
|
return textFrameworkThreadMgr; |
|
} |
|
|
|
public static bool NotifyIme(IntPtr hIMC) |
|
{ |
|
return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL); |
|
} |
|
|
|
public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea) |
|
{ |
|
if (textArea == null) |
|
throw new ArgumentNullException("textArea"); |
|
Rect textViewBounds = textArea.TextView.GetBounds(source); |
|
Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); |
|
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); |
|
} |
|
|
|
public static bool SetCompositionFont(HwndSource source, IntPtr hIMC, TextArea textArea) |
|
{ |
|
if (textArea == null) |
|
throw new ArgumentNullException("textArea"); |
|
LOGFONT lf = new LOGFONT(); |
|
Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); |
|
lf.lfFaceName = textArea.FontFamily.Source; |
|
lf.lfHeight = (int)characterBounds.Height; |
|
return ImmSetCompositionFont(hIMC, ref lf); |
|
} |
|
|
|
static Rect GetBounds(this TextView textView, HwndSource source) |
|
{ |
|
// this may happen during layout changes in AvalonDock, so we just return an empty rectangle |
|
// in those cases. It should be refreshed immediately. |
|
if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) |
|
return EMPTY_RECT; |
|
Rect displayRect = new Rect(0, 0, textView.ActualWidth, textView.ActualHeight); |
|
return textView |
|
.TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual |
|
.TransformToDevice(source.RootVisual); // rect on HWND |
|
} |
|
|
|
static readonly Rect EMPTY_RECT = new Rect(0, 0, 0, 0); |
|
|
|
static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source) |
|
{ |
|
VisualLine vl = textView.GetVisualLine(pos.Line); |
|
if (vl == null) |
|
return EMPTY_RECT; |
|
// this may happen during layout changes in AvalonDock, so we just return an empty rectangle |
|
// in those cases. It should be refreshed immediately. |
|
if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) |
|
return EMPTY_RECT; |
|
TextLine line = vl.GetTextLine(pos.VisualColumn, pos.IsAtEndOfLine); |
|
Rect displayRect; |
|
// calculate the display rect for the current character |
|
if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) { |
|
displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle; |
|
displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop)); |
|
} else { |
|
// if we are in virtual space, we just use one wide-space as character width |
|
displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop), |
|
new Size(textView.WideSpaceWidth, textView.DefaultLineHeight)); |
|
} |
|
// adjust to current scrolling |
|
displayRect.Offset(-textView.ScrollOffset); |
|
return textView |
|
.TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual |
|
.TransformToDevice(source.RootVisual); // rect on HWND |
|
} |
|
} |
|
|
|
[ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
|
interface ITfThreadMgr |
|
{ |
|
void Activate(out int clientId); |
|
void Deactivate(); |
|
void CreateDocumentMgr(out IntPtr docMgr); |
|
void EnumDocumentMgrs(out IntPtr enumDocMgrs); |
|
void GetFocus(out IntPtr docMgr); |
|
void SetFocus(IntPtr docMgr); |
|
void AssociateFocus(IntPtr hwnd, IntPtr newDocMgr, out IntPtr prevDocMgr); |
|
void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus); |
|
void GetFunctionProvider(ref Guid classId, out IntPtr funcProvider); |
|
void EnumFunctionProviders(out IntPtr enumProviders); |
|
void GetGlobalCompartment(out IntPtr compartmentMgr); |
|
} |
|
}
|
|
|