using TSF;
using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace ManagedTSF
{
public struct AdviseSink
{
public object Unknown;
public ITextStoreACPSink TextStoreACPSink;
public TS_AS Mask;
}
public static class IIDConstants
{
public static Guid IID_TextStoreACPSink = new Guid(0x22d44c94, 0xa419, 0x4542, 0xa2, 0x72, 0xae, 0x26, 0x09, 0x3e, 0xce, 0xcf);
public static Guid IID_ITfProperty = new Guid(0x34b45670, 0x7526, 0x11d2, 0xa1, 0x47, 0x00, 0x10, 0x5a, 0x27, 0x99, 0xb5);
public static Guid GUID_PROP_INPUTSCOPE = new Guid(0x1713dd5a, 0x68e7, 0x4a5b, 0x9a, 0xf6, 0x59, 0x2a, 0x59, 0x5c, 0x77, 0x8d);
public static Guid TSATTRID_Font_Style_Height = new Guid(0x7e937477, 0x12e6, 0x458b, 0x92, 0x6a, 0x1f, 0xa4, 0x4e, 0xe8, 0xf3, 0x91);
public static Guid TSATTRID_Font_FaceName = new Guid(0xb536aeb6, 0x053b, 0x4eb8, 0xb6, 0x5a, 0x50, 0xda, 0x1e, 0x81, 0xe7, 0x2e);
public static Guid TSATTRID_Font_SizePts = new Guid(0xc8493302, 0xa5e9, 0x456d, 0xaf, 0x04, 0x80, 0x05, 0xe4, 0x13, 0x0f, 0x03);
public static Guid TSATTRID_Text_ReadOnly = new Guid(0x85836617, 0xde32, 0x4afd, 0xa5, 0x0f, 0xa2, 0xdb, 0x11, 0x0e, 0x6e, 0x4d);
public static Guid TSATTRID_Text_Orientation = new Guid(0x6bab707f, 0x8785, 0x4c39, 0x8b, 0x52, 0x96, 0xf8, 0x78, 0x30, 0x3f, 0xfb);
public static Guid TSATTRID_Text_VerticalWriting = new Guid(0x6bba8195, 0x046f, 0x4ea9, 0xb3, 0x11, 0x97, 0xfd, 0x66, 0xc4, 0x27, 0x4b);
}
public class KeyFilter : IDisposable
{
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
ITfKeystrokeMgr _keystrokeMgr;
public KeyFilter(ITfKeystrokeMgr mgr)
{
_keystrokeMgr = mgr;
}
public static KeyFilter Create()
{
ITfThreadMgr mgr;
TextFrameworkFunctions.TF_GetThreadMgr(out mgr);
if (mgr == null)
{
Type clsid = Type.GetTypeFromCLSID(TextFrameworkDeclarations.CLSID_TF_ThreadMgr);
mgr = Activator.CreateInstance(clsid) as ITfThreadMgr;
}
//ToDo: need dispose for mgr.
return new KeyFilter((ITfKeystrokeMgr)mgr);
}
/// <summary>
///
/// </summary>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns>true: eaten. false: not eaten.</returns>
public bool Filter(int message, IntPtr wParam, IntPtr lParam)
{
if (message == WM_KEYDOWN)
{
bool eaten;
_keystrokeMgr.TestKeyDown((uint)wParam, (int)lParam, out eaten);
if (eaten)
{
_keystrokeMgr.KeyDown((uint)wParam, (int)lParam, out eaten);
return eaten;
}
return false;
}
else if (message == WM_KEYUP)
{
bool eaten;
_keystrokeMgr.TestKeyUp((uint)wParam, (int)lParam, out eaten);
if (eaten)
{
_keystrokeMgr.KeyUp((uint)wParam, (int)lParam, out eaten);
return eaten;
}
return false;
}
else
{
return false;
}
}
#region IDisposable メンバ
public void Dispose()
{
if (_keystrokeMgr != null)
{
/*
* We get keystrokeMgr via cast.
Marshal.ReleaseComObject(_keystrokeMgr);
* */
_keystrokeMgr = null;
}
}
#endregion
public void ShareThreadMgr(TextStore _store)
{
_store.ThreadMgr = (ITfThreadMgr) _keystrokeMgr;
}
}
public interface IManagedTextStoreACP
{
/// <summary>
/// 保持しているテキストの長さを返す
/// </summary>
int TextLength { get; }
/// <summary>
/// フォントの高さを表す
/// </summary>
int FontHeight { get; }
/// <summary>
/// TextStoreを実装しているHWNDを表す
/// </summary>
IntPtr HWnd { get; }
/// <summary>
/// ビュー全体を表す四角形領域(スクリーン座標で返すこと)
/// </summary>
RECT ViewRectangle { get; }
/// <summary>
/// テキストを取得する
/// </summary>
/// <param name="acpStart">開始位置</param>
/// <param name="len">長さ</param>
/// <returns>文字列を返す</returns>
string GetText(int start, int len);
/// <summary>
/// 選択領域の先頭にキャレットがあれば真を返す
/// </summary>
bool IsCaretSelectionStart { get; }
/// <summary>
/// 選択領域にテキストを挿入する
/// </summary>
/// <param name="insertText"></param>
void ReplaceSelectText(string insertText);
/// <summary>
/// キャレット位置を返す
/// </summary>
int CurrentPosition { get; }
/// <summary>
/// 選択領域を返す
/// </summary>
/// <param name="start">選択領域の開始位置</param>
/// <param name="end">選択領域の終了位置</param>
void GetSelection(out int start, out int end);
/// <summary>
/// 選択領域を設定する
/// </summary>
/// <param name="start">選択領域の開始位置</param>
/// <param name="end">選択領域の終了位置</param>
void SetSelection(int start, int end);
/// <summary>
/// 要求されたテキストの四角形領域を返す
/// </summary>
/// <param name="start">開始位置</param>
/// <param name="end">終了位置</param>
/// <param name="clipped">領域内に収まりきらないなら真を返す</param>
/// <returns>四角形領域(スクリーン座標で返すこと)</returns>
RECT GetTextRectangle(int start,int end,out bool clipped);
}
public class TextStore : ITextStoreACP, IDisposable
{
ITfThreadMgr _threadMgr;
ITfDocumentMgr _docMgr;
List<char> _string;
AdviseSink _sink;
IManagedTextStoreACP _managedAcp;
int LockCount;
bool fInterimChar = false;
public TextStore(IManagedTextStoreACP managed)
{
_string = new List<char>();
_managedAcp = managed;
}
public ITfThreadMgr ThreadMgr
{
get { return _threadMgr; }
set { _threadMgr = value; }
}
#region ITextStoreACP メンバ
public void AdviseSink(ref System.Guid riid, object punk, TS_AS dwMask)
{
if (_sink.Unknown == punk)
{
_sink.Mask = dwMask;
return;
}
Debug.Assert(_sink.Unknown == null);
if (riid == IIDConstants.IID_TextStoreACPSink)
{
_sink.Mask = dwMask;
_sink.Unknown = punk;
_sink.TextStoreACPSink = (ITextStoreACPSink) punk;
}
}
private static void DP(string msg)
{
Debug.Print(msg);
}
public void FindNextAttrTransition(int acpStart, int acpHalt, uint cFilterAttrs, Guid[] paFilterAttrs, TS_ATTR_FIND dwFlags, out int pacpNext, out int pfFound, out int plFoundOffset)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void GetACPFromPoint(uint vcView, ref POINT ptScreen, uint dwFlags, out int pacp)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void GetActiveView(out uint pvcView)
{
pvcView = 0;
}
public void GetEmbedded(int acpPos, ref System.Guid rguidService, ref System.Guid riid, out object ppunk)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void GetEndACP(out int pacp)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void GetFormattedText(int acpStart, int acpEnd, out System.Runtime.InteropServices.ComTypes.IDataObject ppDataObject)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void GetScreenExt(uint vcView, out RECT prc)
{
prc = _managedAcp.ViewRectangle;
}
unsafe public void GetSelection(uint ulIndex, uint ulCount, TS_SELECTION_ACP[] pSelection, uint* pcFetched)
{
if (this.LockCount == 0)
throw new InvalidOperationException();
*pcFetched = 0;
if ((ulCount > 0) && ((ulIndex == 0) || (ulIndex == uint.MaxValue/* TS_DEFAULT_SELECTION */)))
{
int selStart,selEnd;
_managedAcp.GetSelection(out selStart, out selEnd);
pSelection[0].acpStart = selStart;
pSelection[0].acpEnd = selEnd;
pSelection[0].style.fInterimChar = fInterimChar;
if (fInterimChar)
pSelection[0].style.ase = TsActiveSelEnd.TS_AE_NONE;
else
pSelection[0].style.ase = _managedAcp.IsCaretSelectionStart ? TsActiveSelEnd.TS_AE_START : TsActiveSelEnd.TS_AE_END;
*pcFetched = 1;
}
}
public void GetStatus(out TS_STATUS pdcs)
{
pdcs.dwDynamicFlags = 0;
pdcs.dwStaticFlags = TS_SS.TS_SS_REGIONS;
}
public void GetText(int acpStart, int acpEnd, char[] pchPlain, int cchPlainReq, out int pcchPlainRet, TS_RUNINFO[] prgRunInfo, int cRunInfoReq, out int pcRunInfoRet, out int pacpNext)
{
if (this.LockCount == 0)
throw new InvalidOperationException();
pcchPlainRet = 0;
pcRunInfoRet = 0;
pacpNext = 0;
pcRunInfoRet = 0;
if ((cchPlainReq == 0) && (cRunInfoReq == 0))
{
return;
}
if (acpEnd == -1)
acpEnd = _managedAcp.TextLength;
acpEnd = Math.Min(acpEnd, acpStart + (int)cchPlainReq);
if (acpStart != acpEnd)
{
string text = _managedAcp.GetText(acpStart, acpEnd - acpStart);
for (int i = 0; i < text.Length; i++)
{
pchPlain[i] = text[i];
}
}
pcchPlainRet = acpEnd - acpStart;
if (cRunInfoReq != 0)
{
prgRunInfo[0].uCount = (uint)(acpEnd - acpStart);
prgRunInfo[0].type = TsRunType.TS_RT_PLAIN; // TS_RT_PLAIN;
pcRunInfoRet = 1;
}
pacpNext = acpEnd;
return ;
}
public void GetTextExt(uint vcView, int acpStart, int acpEnd, out RECT prc, out bool pfClipped)
{
if (this.LockCount == 0)
throw new InvalidOperationException();
if (acpStart == acpEnd)
throw new InvalidOperationException();
prc = _managedAcp.GetTextRectangle(acpStart, acpEnd, out pfClipped);
}
public void GetWnd(uint vcView, System.IntPtr phwnd)
{
phwnd = _managedAcp.HWnd;
}
public void InsertEmbedded(TS_IE dwFlags, int acpStart, int acpEnd, System.Runtime.InteropServices.ComTypes.IDataObject pDataObject, out TS_TEXTCHANGE pChange)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void InsertEmbeddedAtSelection(TF_IAS dwFlags, System.Runtime.InteropServices.ComTypes.IDataObject pDataObject, out int pacpStart, out int pacpEnd, out TS_TEXTCHANGE pChange)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void InsertTextAtSelection(TF_IAS dwFlags, char[] pchText, uint cch, out int pacpStart, out int pacpEnd, out TS_TEXTCHANGE pChange)
{
if (this.LockCount == 0)
throw new InvalidOperationException();
pacpStart = pacpEnd = 0;
int acpStart, acpEnd;
_managedAcp.GetSelection(out acpStart, out acpEnd);
if ((dwFlags & TF_IAS.TF_IAS_QUERYONLY) == TF_IAS.TF_IAS_QUERYONLY)
{
pacpStart = acpStart;
pacpEnd = acpEnd;
pChange.acpStart = 0;
pChange.acpOldEnd = 0;
pChange.acpNewEnd = 0;
return;
}
string str = new String(pchText, 0, (int)cch);
if(pchText != null)
_managedAcp.ReplaceSelectText(str);
int newEnd = acpStart + (int)cch;
_managedAcp.SetSelection(acpStart, newEnd);
int selStart, selEnd;
_managedAcp.GetSelection(out selStart, out selEnd);
if ((dwFlags & TF_IAS.TF_IAS_NOQUERY) != TF_IAS.TF_IAS_NOQUERY)
{
pacpStart = selStart;
pacpEnd = selEnd;
}
pChange.acpNewEnd = selEnd;
pChange.acpStart = pacpStart;
pChange.acpOldEnd = acpEnd;
}
public void QueryInsert(int acpTestStart, int acpTestEnd, uint cch, out int pacpResultStart, out int pacpResultEnd)
{
pacpResultStart = acpTestStart;
pacpResultEnd = acpTestEnd;
}
public void QueryInsertEmbedded(ref System.Guid pguidService, ref System.Runtime.InteropServices.ComTypes.FORMATETC pformatetc, out bool pfInsertable)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void RequestAttrsAtPosition(int acpPos, uint cFilterAttrs, Guid[] paFilterAttrs, uint dwFlags)
{
HandleRequestAttrs(cFilterAttrs, paFilterAttrs);
}
public void RequestAttrsTransitioningAtPosition(int acpPos, uint cFilterAttrs, Guid[] paFilterAttrs, TS_ATTR_FIND dwFlags)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void RequestLock(TS_LF dwLockFlags, out TextStoreReturnValues phrSession)
{
System.Threading.Interlocked.Increment(ref this.LockCount);
if (this.LockCount > 1)
{
if ((dwLockFlags & TS_LF.TS_LF_SYNC) == TS_LF.TS_LF_SYNC)
{
phrSession = TextStoreReturnValues.TS_E_SYNCHRONOUS;
return;
}
else if ((dwLockFlags & TS_LF.TS_LF_READ) == TS_LF.TS_LF_READ &&
(dwLockFlags & TS_LF.TS_LF_READWRITE) == TS_LF.TS_LF_READWRITE)
{
phrSession = TextStoreReturnValues.E_NOTIMPL;
return;
}
throw new InvalidOperationException();
}
try
{
_sink.TextStoreACPSink.OnLockGranted((uint)dwLockFlags);
phrSession = 0;
}
catch
{
phrSession = TextStoreReturnValues.E_FAIL;
}
System.Threading.Interlocked.Decrement(ref this.LockCount);
}
private int _nAttrVals = 0;
private TS_ATTRVAL[] _attrval = new TS_ATTRVAL[8];
Guid[] _supportedAttrs = { IIDConstants.GUID_PROP_INPUTSCOPE,
IIDConstants.TSATTRID_Font_Style_Height,
IIDConstants.TSATTRID_Font_FaceName,
IIDConstants.TSATTRID_Font_SizePts,
IIDConstants.TSATTRID_Text_ReadOnly,
IIDConstants.TSATTRID_Text_Orientation,
IIDConstants.TSATTRID_Text_VerticalWriting};
public void RequestSupportedAttrs(uint dwFlags, uint cFilterAttrs, Guid[] paFilterAttrs)
{
HandleRequestAttrs(cFilterAttrs, paFilterAttrs);
}
private void HandleRequestAttrs(uint cFilterAttrs, Guid[] paFilterAttrs)
{
_nAttrVals = 0;
for (int i = 0; i < _supportedAttrs.Length; i++)
{
if (cFilterAttrs != 0)
{
bool found = false;
for (uint j = 0; j < cFilterAttrs; j++)
{
if (_supportedAttrs[j] == paFilterAttrs[j])
{
found = true;
break;
}
}
if (!found)
continue;
_attrval[_nAttrVals].idAttr = _supportedAttrs[i];
_attrval[_nAttrVals].dwOverlapId = (uint)i + 1;
switch (i + 1)
{
case 1: // input scope
_attrval[_nAttrVals].varValue = null;
break;
case 2: // font height
_attrval[_nAttrVals].varValue = _managedAcp.FontHeight; // font height
break;
case 3: // font face name
_attrval[_nAttrVals].varValue = String.Empty;
break;
case 4: // FONT_SIZEPTS
_attrval[_nAttrVals].varValue = (int)((double)_managedAcp.FontHeight / 96.0 * 72.0);
break;
case 5: // TEXT_READONLY:
_attrval[_nAttrVals].varValue = false;
break;
case 6: // TEXT_ORIENTATION:
_attrval[_nAttrVals].varValue = 0;
break;
case 7: // TEXT_VERTICALWRITING:
_attrval[_nAttrVals].varValue = false;
break;
default:
Debug.Assert(false, "never reached here");
break;
}
_nAttrVals++;
}
}
}
unsafe public void RetrieveRequestedAttrs(uint ulCount, out TS_ATTRVAL paAttrVals, uint* pcFetched)
{
throw new System.Exception("The method or operation is not implemented.");
}
public void SetSelection(uint ulCount, ref TS_SELECTION_ACP pSelection)
{
if (ulCount > 1)
return;
if (this.LockCount == 0)
throw new InvalidOperationException();
this.fInterimChar = pSelection.style.fInterimChar;
TsActiveSelEnd selEnd;
if (this.fInterimChar)
selEnd = TsActiveSelEnd.TS_AE_NONE;
else
selEnd = pSelection.style.ase;
if (selEnd == TsActiveSelEnd.TS_AE_START)
_managedAcp.SetSelection(pSelection.acpEnd, pSelection.acpStart);
else
_managedAcp.SetSelection(pSelection.acpStart, pSelection.acpEnd);
}
public void SetText(TS_ST dwFlags, int acpStart, int acpEnd, char[] pchText, int cch, out TS_TEXTCHANGE pChange)
{
string insertText = new String(pchText, 0, cch);
TS_SELECTION_ACP tsa = new TS_SELECTION_ACP();
tsa.acpStart = acpStart;
tsa.acpEnd = acpEnd;
tsa.style.ase = TsActiveSelEnd.TS_AE_START;
tsa.style.fInterimChar = false;
this.SetSelection(1, ref tsa);
int dummy;
this.InsertTextAtSelection(TF_IAS.TF_IAS_NOQUERY, pchText, (uint)cch, out dummy, out dummy, out pChange);
}
public void UnadviseSink(object punk)
{
_sink.TextStoreACPSink = null;
_sink.Unknown = null;
}
public void LockDocument()
{
Interlocked.Increment(ref this.LockCount);
if (this.LockCount > 1)
throw new InvalidOperationException();
}
public void UnlockDocument()
{
if (this.LockCount == 0)
throw new InvalidOperationException();
Interlocked.Decrement(ref this.LockCount);
}
public bool IsLocked()
{
return this.LockCount > 0;
}
unsafe public void GetDisplayAttributes(int index, int length)
{
TF_SELECTION selection;
uint fetched;
this._context.GetSelection(this.clientId, (uint)index, (uint)length, out selection, &fetched);
ITfCategoryMgr categorymgr;
TextFrameworkFunctions.TF_CreateCategoryMgr(out categorymgr);
ITfDisplayAttributeMgr displaymgr;
TextFrameworkFunctions.TF_CreateDisplayAttributeMgr(out displaymgr);
ITfProperty property;
this._context.GetProperty(ref IIDConstants.IID_ITfProperty, out property);
object value;
property.GetValue(this.clientId, selection.range, out value);
Guid guid = (Guid)value;
Guid owner;
ITfDisplayAttributeInfo info;
displaymgr.GetDisplayAttributeInfo(ref guid, out info, out owner);
TF_DISPLAYATTRIBUTE attr;
info.GetAttributeInfo(out attr);
}
#endregion
ITfContext _context;
uint clientId;
public void InitContext()
{
_threadMgr.CreateDocumentMgr(out _docMgr);
_threadMgr.Activate(out this.clientId);
uint pecTextStore;
_docMgr.CreateContext(this.clientId, 0, (ITextStoreACP)this, out _context, out pecTextStore);
_docMgr.Push(_context);
}
public void SetFocus()
{
_threadMgr.SetFocus(_docMgr);
}
#region IDisposable メンバ
public void Dispose()
{
Marshal.ReleaseComObject(_context);
Marshal.ReleaseComObject(_docMgr);
Marshal.ReleaseComObject(_threadMgr);
}
#endregion
}
}