OSDN Git Service

初コミット
[fooeditengine/FooEditEngine.git] / Common / DotNetTextStore / TextStoreBase.cs
1 // TSF のデバッグ表示を行うかどうか?\r
2 //#define TSF_DEBUG_OUTPUT\r
3 using System;\r
4 using System.Collections.Generic;\r
5 using System.Linq;\r
6 using System.Text;\r
7 using System.Diagnostics;\r
8 using System.Reflection;\r
9 using System.Runtime.CompilerServices;\r
10 using System.Runtime.InteropServices;\r
11 using System.Globalization;\r
12 \r
13 using DotNetTextStore.UnmanagedAPI.TSF;\r
14 using DotNetTextStore.UnmanagedAPI.TSF.TextStore;\r
15 using DotNetTextStore.UnmanagedAPI.WinDef;\r
16 using DotNetTextStore.UnmanagedAPI.WinError;\r
17 \r
18 namespace DotNetTextStore\r
19 {\r
20 #if TSF_DEBUG_OUTPUT\r
21     /// <summary>コールスタックの階層にあわせてインデントしてデバッグ表示するクラス。</summary>\r
22     public class DebugOut : IDisposable\r
23     {\r
24         public DebugOut(string i_string, params object[] i_params)\r
25         {\r
26             _text = string.Format(i_string, i_params);\r
27 \r
28             s_callCount++;\r
29             Debug.WriteLine("");\r
30             Debug.WriteLine(string.Format("{0, 4} : ↓↓↓ ", s_callCount) + _text);\r
31         }\r
32 \r
33         public void Dispose()\r
34         {\r
35             s_callCount++;\r
36             Debug.WriteLine(string.Format("{0, 4} : ↑↑↑ ", s_callCount) + _text);\r
37         }\r
38 \r
39         public static string GetCaller([CallerMemberName] string caller="")\r
40         {\r
41             return caller;\r
42         }\r
43 \r
44         string      _text;\r
45         static int  s_callCount = 0;\r
46     }\r
47 #endif\r
48 \r
49     //=============================================================================================\r
50 \r
51 \r
52     public struct TextDisplayAttribute\r
53     {\r
54         public int startIndex;\r
55         public int endIndex;\r
56         public TF_DISPLAYATTRIBUTE attribute;\r
57     }\r
58 \r
59     //========================================================================================\r
60 \r
61 \r
62     /// <summary>Dispose() で TextStore のロック解除を行うクラス。</summary>\r
63     public class Unlocker : IDisposable\r
64     {\r
65         /// <summary>コンストラクタ</summary>\r
66         public Unlocker(TextStoreBase io_textStore)\r
67         {\r
68             _textStore = io_textStore;\r
69         }\r
70         /// <summary>ロックが成功したかどうか調べる。</summary>\r
71         public bool IsLocked\r
72         {\r
73             get { return _textStore != null; }\r
74         }\r
75         /// <summary>アンロックを行う。</summary>\r
76         void IDisposable.Dispose()\r
77         {\r
78             if (_textStore != null)\r
79             {\r
80                 _textStore.UnlockDocument();\r
81                 _textStore = null;\r
82             }\r
83         }\r
84 \r
85         /// <summary>アンロックを行うテキストストア</summary>\r
86         TextStoreBase _textStore;\r
87     }\r
88 \r
89     public abstract class TextStoreBase\r
90     {\r
91         public delegate bool IsReadOnlyHandler();\r
92         public event IsReadOnlyHandler IsReadOnly;\r
93 \r
94         public delegate bool IsLoadingHandler();\r
95         public event IsLoadingHandler IsLoading;\r
96 \r
97         public delegate int GetStringLengthHandler();\r
98         public event GetStringLengthHandler GetStringLength;\r
99 \r
100         public delegate void GetSelectionIndexHandler(out int o_start, out int o_end);\r
101         public event GetSelectionIndexHandler GetSelectionIndex;\r
102 \r
103         public delegate void SetSelectionIndexHandler(int i_start, int i_end);\r
104         public event SetSelectionIndexHandler SetSelectionIndex;\r
105 \r
106         public delegate string GetStringHandler(int start, int length);\r
107         public event GetStringHandler GetString;\r
108 \r
109         public delegate void InsertAtSelectionHandler(string i_value);\r
110         public event InsertAtSelectionHandler InsertAtSelection;\r
111 \r
112         public delegate void GetScreenExtentHandler(\r
113             out POINT o_pointTopLeft,\r
114             out POINT o_pointBottomRight\r
115         );\r
116         public event GetScreenExtentHandler GetScreenExtent;\r
117 \r
118         public delegate void GetStringExtentHandler(\r
119             int i_startIndex,\r
120             int i_endIndex,\r
121             out POINT o_pointTopLeft,\r
122             out POINT o_pointBottomRight\r
123         );\r
124         public event GetStringExtentHandler GetStringExtent;\r
125 \r
126         public delegate bool CompositionStartedHandler();\r
127         public event CompositionStartedHandler CompositionStarted;\r
128 \r
129         public delegate void CompostionUpdateHandler(int start, int end);\r
130         public event CompostionUpdateHandler CompositionUpdated;\r
131 \r
132         public delegate void CompositionEndedHandler();\r
133         public event CompositionEndedHandler CompositionEnded;\r
134 \r
135         #region "生成と破棄"\r
136         public TextStoreBase()\r
137         {\r
138 #if TSF_DEBUG_OUTPUT\r
139             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
140 #endif\r
141             {\r
142                 try\r
143                 {\r
144                     // スレッドマネージャ-の生成\r
145                     CreateThreadMgr();\r
146                     // カテゴリマネージャーの生成\r
147                     CreateCategoryMgr();\r
148                     // 表示属性マネージャーの生成\r
149                     CreateDisplayAttributeMgr();\r
150 \r
151                     // ドキュメントマネージャーの生成\r
152                     _threadMgr.CreateDocumentMgr(out _documentMgr);\r
153 \r
154                     // スレッドマネージャのアクティブ化\r
155                     int clientId = 0;\r
156                     _threadMgr.Activate(out clientId);\r
157 \r
158                     // コンテキストの生成\r
159                     _documentMgr.CreateContext(clientId, 0, this, out _context, out _editCookie);\r
160 \r
161                     // コンテキストの push\r
162                     _documentMgr.Push(_context);\r
163 \r
164                     // ファンクションプロバイダーを取得する。\r
165                     Guid guid = TfDeclarations.GUID_SYSTEM_FUNCTIONPROVIDER;\r
166                     _threadMgr.GetFunctionProvider(ref guid, out _functionProvider);\r
167 \r
168                     // ITfReconversion オブジェクトを取得する。\r
169                     var guidNull = new Guid();\r
170                     var guidReconversion = new Guid("4cea93c0-0a58-11d3-8df0-00105a2799b5");    //ITfFnReconversionの定義から\r
171                     object reconversion = null;\r
172                     _functionProvider.GetFunction(\r
173                         ref guidNull,\r
174                         ref guidReconversion,\r
175                         out reconversion\r
176                     );\r
177                     _reconversion = reconversion as ITfFnReconversion;\r
178 \r
179                     // MODEBIAS の初期化\r
180                     uint guidAtom = 0;\r
181                     Guid guidModebiasNone = TfDeclarations.GUID_MODEBIAS_NONE;\r
182                     _categoryMgr.RegisterGUID(ref guidModebiasNone, out guidAtom);\r
183                     _attributeInfo[0].attrID = TfDeclarations.GUID_PROP_MODEBIAS;\r
184                     _attributeInfo[0].flags = AttributeInfoFlags.None;\r
185                     _attributeInfo[0].currentValue.vt = (short)VarEnum.VT_EMPTY;\r
186                     _attributeInfo[0].defaultValue.vt = (short)VarEnum.VT_I4;\r
187                     _attributeInfo[0].defaultValue.data1 = (IntPtr)guidAtom;\r
188                 }\r
189                 catch (Exception exception)\r
190                 {\r
191                     Debug.WriteLine(exception.Message);\r
192                     Dispose(false);\r
193                 }\r
194             }\r
195         }\r
196 \r
197         /// <summary>\r
198         /// オブジェクトの破棄を行う。このメソッドは必ず呼び出す必要があります\r
199         /// </summary>\r
200         /// <param name="flag"></param>\r
201         protected void Dispose(bool flag)\r
202         {\r
203             if (flag)\r
204                 return;\r
205 #if TSF_DEBUG_OUTPUT\r
206             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
207 #endif\r
208             {\r
209                 ReleaseComObject("_reconversion", ref _reconversion);\r
210                 ReleaseComObject("_functionProvider", ref _functionProvider);\r
211                 ReleaseComObject("_context", ref _context);\r
212                 DestroyDocumentMgr();\r
213                 DestroyDisplayAttributeMgr();\r
214                 DestroyCategoryMgr();\r
215                 DestroyThreadMgr();\r
216 \r
217                 GC.SuppressFinalize(this);\r
218             }\r
219         }\r
220 \r
221         /// <summary>\r
222         /// スレッドマネージャの生成。このメソッドの実装は必須です\r
223         /// </summary>\r
224         /// \r
225         /// <exception cref="COMException">\r
226         /// スレッドマネージャーの生成に失敗した場合。\r
227         /// </exception>\r
228         protected virtual void CreateThreadMgr()\r
229         {\r
230             throw new NotImplementedException();\r
231         }\r
232 \r
233         /// <summary>\r
234         /// スレッドマネージャーの解放。\r
235         /// </summary>\r
236         void DestroyThreadMgr()\r
237         {\r
238             if (_threadMgr != null)\r
239             {\r
240                 try { _threadMgr.Deactivate(); }\r
241                 catch (Exception) { }\r
242 \r
243                 ReleaseComObject("_threadMgr", ref _threadMgr);\r
244             }\r
245         }\r
246 \r
247         /// <summary>\r
248         /// カテゴリマネージャーの生成。\r
249         /// </summary>\r
250         /// \r
251         /// <exception cref="COMException">\r
252         /// カテゴリマネージャーの生成に失敗した場合。\r
253         /// </exception>\r
254         void CreateCategoryMgr()\r
255         {\r
256             if (_categoryMgr == null)\r
257             {\r
258                 var clsid = Marshal.GetTypeFromCLSID(TfDeclarations.CLSID_TF_CategoryMgr);\r
259                 _categoryMgr = Activator.CreateInstance(clsid) as ITfCategoryMgr;\r
260 \r
261                 if (_categoryMgr == null)\r
262                 {\r
263                     const string message = "カテゴリマネージャーの生成に失敗しました。";\r
264                     Debug.WriteLine(message);\r
265                     throw new COMException(message, HRESULT.E_NOTIMPL);\r
266                 }\r
267             }\r
268         }\r
269 \r
270         /// <summary>\r
271         /// カテゴリマネージャーの解放。\r
272         /// </summary>\r
273         void DestroyCategoryMgr()\r
274         {\r
275             ReleaseComObject("_categoryMgr", ref _categoryMgr);\r
276         }\r
277 \r
278         /// <summary>\r
279         /// 表示属性マネージャーの生成。\r
280         /// </summary>\r
281         /// \r
282         /// <exception cref="COMException">\r
283         /// 表示属性マネージャーの生成に失敗した場合。\r
284         /// </exception>\r
285         void CreateDisplayAttributeMgr()\r
286         {\r
287             if (_displayAttributeMgr == null)\r
288             {\r
289                 var clsid = Marshal.GetTypeFromCLSID(TfDeclarations.CLSID_TF_DisplayAttributeMgr);\r
290                 _displayAttributeMgr = Activator.CreateInstance(clsid) as ITfDisplayAttributeMgr;\r
291 \r
292                 if (_displayAttributeMgr == null)\r
293                 {\r
294                     const string message = "表示属性マネージャーの生成に失敗しました。";\r
295                     Debug.WriteLine(message);\r
296                     throw new COMException(message, HRESULT.E_NOTIMPL);\r
297                 }\r
298             }\r
299         }\r
300 \r
301         /// <summary>\r
302         /// 表示属性マネージャーの解放。\r
303         /// </summary>\r
304         void DestroyDisplayAttributeMgr()\r
305         {\r
306             ReleaseComObject("_displayAttributeMgr", ref _displayAttributeMgr);\r
307         }\r
308 \r
309         /// <summary>\r
310         /// ドキュメントマネージャーの解放\r
311         /// </summary>\r
312         void DestroyDocumentMgr()\r
313         {\r
314             if (_documentMgr != null)\r
315             {\r
316                 try { _documentMgr.Pop(PopFlags.TF_POPF_ALL); }\r
317                 catch (Exception) { }\r
318 \r
319                 ReleaseComObject("_documentMgr", ref _documentMgr);\r
320             }\r
321         }\r
322 \r
323         /// <summary>\r
324         /// COM オブジェクトのリリースとデバッグメッセージ出力。\r
325         /// </summary>\r
326         protected static void ReleaseComObject<ComObject>(\r
327             string i_objectName,\r
328             ref ComObject io_comObject\r
329         )\r
330         {\r
331             if (io_comObject != null)\r
332             {\r
333                 var refCount = Marshal.ReleaseComObject(io_comObject);\r
334 #if TSF_DEBUG_OUTPUT\r
335                     Debug.WriteLine(\r
336                         "Marshal.ReleaseComObject({0}) returns {1}.",\r
337                         i_objectName,\r
338                         refCount\r
339                     );\r
340 #endif\r
341 \r
342                 io_comObject = default(ComObject);\r
343             }\r
344         }\r
345         #endregion\r
346 \r
347         #region "コントロール側が状況に応じて呼び出さなければいけない TSF に通知を送るメソッド"\r
348         /// <summary>\r
349         /// 選択領域が変更されたことをTSFに伝える。各種ハンドラ内からコールしてはいけない。\r
350         /// </summary>\r
351         public void NotifySelectionChanged()\r
352         {\r
353             if( _sink != null )\r
354             {\r
355                 if( (_adviseFlags & AdviseFlags.TS_AS_SEL_CHANGE) != 0 )\r
356                     _sink.OnSelectionChange();\r
357             }\r
358         }\r
359 \r
360         \r
361         //=========================================================================================\r
362 \r
363 \r
364         /// <summary>\r
365         /// テキストが変更されたことをTSFに伝える。各種ハンドラ内からコールしてはいけない。\r
366         /// </summary>\r
367         public void NotifyTextChanged(TS_TEXTCHANGE textChange)\r
368         {\r
369             if( _sink != null )\r
370             {\r
371                 if( (_adviseFlags & AdviseFlags.TS_AS_TEXT_CHANGE) != 0 )\r
372                     _sink.OnTextChange(0, ref textChange);\r
373                 _sink.OnLayoutChange(TsLayoutCode.TS_LC_CHANGE, 1);\r
374             }\r
375         }\r
376 \r
377         \r
378         //=========================================================================================\r
379 \r
380         /// <summary>\r
381         /// テキストが変更されたことをTSFに伝える。各種ハンドラ内からコールしてはいけない。\r
382         /// </summary>\r
383         /// <param name="start">開始位置</param>\r
384         /// <param name="oldend">更新前の終了位置</param>\r
385         /// <param name="newend">更新後の終了位置</param>\r
386         /// <remarks>\r
387         /// 詳しいことはITextStoreACPSink::OnTextChangeを参照してください\r
388         /// </remarks>\r
389         public void NotifyTextChanged(int start,int oldend,int newend)\r
390         {\r
391             if (_sink != null)\r
392             {\r
393                 if ((_adviseFlags & AdviseFlags.TS_AS_TEXT_CHANGE) != 0)\r
394                 {\r
395                     var textChange = new TS_TEXTCHANGE();\r
396                     textChange.start = start;\r
397                     textChange.oldEnd = oldend;\r
398                     textChange.newEnd = newend;\r
399 \r
400                     _sink.OnTextChange(0, ref textChange);\r
401                 }\r
402                 _sink.OnLayoutChange(TsLayoutCode.TS_LC_CHANGE, 1);\r
403             }\r
404         }\r
405 \r
406         /// <summary>\r
407         /// テキストが変更されたことをTSFに伝える。各種ハンドラ内からコールしてはいけない。\r
408         /// </summary>\r
409         /// <param name="i_oldLength"></param>\r
410         /// <param name="i_newLength"></param>\r
411         public void NotifyTextChanged(int i_oldLength, int i_newLength)\r
412         {\r
413             if( _sink != null )\r
414             {\r
415                 if( (_adviseFlags & AdviseFlags.TS_AS_TEXT_CHANGE) != 0 )\r
416                 {\r
417                     var textChange = new TS_TEXTCHANGE();\r
418                     textChange.start = 0;\r
419                     textChange.oldEnd = i_oldLength;\r
420                     textChange.newEnd = i_newLength;\r
421 \r
422                     _sink.OnTextChange(0, ref textChange);\r
423                 }\r
424                 _sink.OnLayoutChange(TsLayoutCode.TS_LC_CHANGE, 1);\r
425             }\r
426         }\r
427 \r
428         \r
429         \r
430         //=========================================================================================\r
431 \r
432 \r
433         /// <summary>コントロールがフォーカスを取得した時に呼び出さなければいけない。</summary>\r
434         public void SetFocus()\r
435         {\r
436             if (_threadMgr != null)\r
437                 _threadMgr.SetFocus(_documentMgr);\r
438         }\r
439         #endregion "コントロール側が状況に応じて呼び出さなければいけない TSF に通知を送るメソッド"\r
440 \r
441         #region ロック関連\r
442         /// <summary>\r
443         /// ドキュメントのロックを行う。\r
444         /// </summary>\r
445         /// <param name="i_writable">読み書き両用ロックか?false の場合、読み取り専用。</param>\r
446         /// <returns>Unlocker のインスタンスを返す。失敗した場合 null を返す。</returns>\r
447         public Unlocker LockDocument(bool i_writable)\r
448         {\r
449             #if TSF_DEBUG_OUTPUT\r
450             using(var dbgout = new DebugOut("{0}({1})", DebugOut.GetCaller(),\r
451                                                         i_writable) )\r
452             #endif\r
453             {\r
454                 lock(this)\r
455                 {\r
456                     if( this._lockFlags == 0 )\r
457                     {\r
458                         if( i_writable )\r
459                             this._lockFlags = LockFlags.TS_LF_READWRITE;\r
460                         else\r
461                             this._lockFlags = LockFlags.TS_LF_READ;\r
462 \r
463                         #if TSF_DEBUG_OUTPUT\r
464                             Debug.WriteLine("LockDocument is succeeded.");\r
465                         #endif\r
466 \r
467                         return new Unlocker(this);\r
468                     }\r
469                     else\r
470                     {\r
471                         #if TSF_DEBUG_OUTPUT\r
472                             Debug.WriteLine("LockDocument is failed. {0}", _lockFlags);\r
473                         #endif\r
474 \r
475                         return null;\r
476                     }\r
477                 }\r
478             }\r
479         }\r
480 \r
481 \r
482         //========================================================================================\r
483 \r
484         \r
485         /// <summary>\r
486         /// ドキュメントのロックを行う。\r
487         /// </summary>\r
488         /// <param name="i_writable">読み書き両用ロックか?false の場合、読み取り専用。</param>\r
489         /// <returns>Unlocker のインスタンスを返す。失敗した場合 null を返す。</returns>\r
490         public Unlocker LockDocument(LockFlags i_flags)\r
491         {\r
492             #if TSF_DEBUG_OUTPUT\r
493             using(var dbgout = new DebugOut("{0}({1})", DebugOut.GetCaller(),\r
494                                                         i_flags) )\r
495             #endif\r
496             {\r
497                 lock(this)\r
498                 {\r
499                     if( this._lockFlags == 0 )\r
500                     {\r
501                         this._lockFlags = i_flags;\r
502 \r
503                         #if TSF_DEBUG_OUTPUT\r
504                             Debug.WriteLine("LockDocument is succeeded.");\r
505                         #endif\r
506 \r
507                         return new Unlocker(this);\r
508                     }\r
509                     else\r
510                     {\r
511                         #if TSF_DEBUG_OUTPUT\r
512                             Debug.WriteLine("LockDocument is failed. {0}", _lockFlags);\r
513                         #endif\r
514 \r
515                         return null;\r
516                     }\r
517                 }\r
518             }\r
519         }\r
520 \r
521 \r
522         //========================================================================================\r
523 \r
524         \r
525         /// <summary>\r
526         /// ドキュメントのアンロックを行う。\r
527         /// </summary>\r
528         public void UnlockDocument()\r
529         {\r
530             #if TSF_DEBUG_OUTPUT\r
531             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
532             #endif\r
533             {\r
534                 lock(this)\r
535                 {\r
536                     _lockFlags = 0;\r
537                 }\r
538 \r
539                 if( _pendingLockUpgrade )\r
540                 {\r
541                     _pendingLockUpgrade = false;\r
542                     int sessionResult;\r
543                     RequestLock(LockFlags.TS_LF_READWRITE, out sessionResult);\r
544                 }\r
545             }\r
546         }\r
547 \r
548 \r
549         //========================================================================================\r
550 \r
551         \r
552         /// <summary>\r
553         /// 指定されたフラグでロックしている状態かどうか調べる。\r
554         /// </summary>\r
555         /// <param name="i_lockFlags"></param>\r
556         /// <returns>ロックされている場合は true, されていない場合は false を返す。</returns>\r
557         public bool IsLocked(LockFlags i_lockFlags)\r
558         {\r
559             #if TSF_DEBUG_OUTPUT\r
560             using(var dbgout = new DebugOut("{0}({1})",\r
561                                             DebugOut.GetCaller(), i_lockFlags) )\r
562             #endif\r
563             {\r
564                 #if TSF_DEBUG_OUTPUT\r
565                     Debug.WriteLine(\r
566                         "IsLocked() returns " + ((this._lockFlags & i_lockFlags) == i_lockFlags)\r
567                     );\r
568                 #endif\r
569                 return (this._lockFlags & i_lockFlags) == i_lockFlags;\r
570             }\r
571         }\r
572 \r
573         /// <summary>\r
574         /// ロックされているか調べる\r
575         /// </summary>\r
576         /// <returns>ロックされているなら真、そうでなければ偽を返す</returns>\r
577         public bool IsLocked()\r
578         {\r
579 #if TSF_DEBUG_OUTPUT\r
580             using(var dbgout = new DebugOut("{0}",\r
581                                             DebugOut.GetCaller()) )\r
582 #endif\r
583             {\r
584                 bool retval = this._lockFlags != 0;\r
585 #if TSF_DEBUG_OUTPUT\r
586                     Debug.WriteLine(\r
587                         "IsLocked() returns " + retval\r
588                     );\r
589 #endif\r
590                 return retval;\r
591             }\r
592         }\r
593         #endregion\r
594 \r
595         #region ITextStroeACP,ITextStoreACP2の共通部分\r
596 \r
597         /// <summary>\r
598         /// 文字列を挿入する。\r
599         /// </summary>\r
600         public void InsertTextAtSelection(string s)\r
601         {\r
602             TS_TEXTCHANGE textChange = new TS_TEXTCHANGE();\r
603 \r
604             using (var unlocker = LockDocument(true))\r
605             {\r
606                 if (unlocker != null)\r
607                 {\r
608                     int startIndex, endIndex;\r
609 \r
610                     InsertTextAtSelection(\r
611                         UnmanagedAPI.TSF.TextStore.InsertAtSelectionFlags.TF_IAS_NOQUERY,\r
612                         s.ToCharArray(),\r
613                         s.Length,\r
614                         out startIndex,\r
615                         out endIndex,\r
616                         out textChange\r
617                     );\r
618                 }\r
619             }\r
620 \r
621             // シンクの OnSelectionChange() をコール。\r
622             NotifySelectionChanged();\r
623             NotifyTextChanged(textChange);\r
624         }\r
625 \r
626         public void InsertEmbeddedAtSelection(\r
627             InsertAtSelectionFlags flags,\r
628             object obj,\r
629             out int start,\r
630             out int end,\r
631             out TS_TEXTCHANGE change\r
632         )\r
633         {\r
634 #if TSF_DEBUG_OUTPUT\r
635             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
636 #endif\r
637             {\r
638                 throw new NotImplementedException();\r
639             }\r
640         }\r
641 \r
642         public void AdviseSink(ref Guid i_riid, object i_unknown, AdviseFlags i_mask)\r
643         {\r
644 #if TSF_DEBUG_OUTPUT\r
645             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
646 #endif\r
647             {\r
648                 if (i_riid != new Guid("22d44c94-a419-4542-a272-ae26093ececf")) //ITextStoreACPSinkの定義より\r
649                 {\r
650                     throw new COMException(\r
651                         "ITextStoreACPSink 以外のIIDが渡されました。",\r
652                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
653                     );\r
654                 }\r
655 \r
656                 // 既存のシンクのマスクのみを更新\r
657                 if (_sink == i_unknown)\r
658                 {\r
659                     _adviseFlags = i_mask;\r
660                 }\r
661                 // シンクを複数登録しようとした\r
662                 else if (_sink != null)\r
663                 {\r
664                     throw new COMException(\r
665                         "既にシンクを登録済みです。",\r
666                         UnmanagedAPI.TSF.TextStore.TsResult.CONNECT_E_ADVISELIMIT\r
667                     );\r
668                 }\r
669                 else\r
670                 {\r
671                     // 各種値を保存\r
672                     _services = (ITextStoreACPServices)i_unknown;\r
673                     _sink = (ITextStoreACPSink)i_unknown;\r
674                     _adviseFlags = i_mask;\r
675                 }\r
676             }\r
677         }\r
678 \r
679         public void UnadviseSink(object i_unknown)\r
680         {\r
681 #if TSF_DEBUG_OUTPUT\r
682             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
683 #endif\r
684             {\r
685                 if (_sink == null || _sink != i_unknown)\r
686                 {\r
687                     throw new COMException(\r
688                         "シンクは登録されていません。",\r
689                         UnmanagedAPI.TSF.TextStore.TsResult.CONNECT_E_NOCONNECTION\r
690                     );\r
691                 }\r
692 \r
693                 _services = null;\r
694                 _sink = null;\r
695                 _adviseFlags = 0;\r
696             }\r
697         }\r
698 \r
699         public void RequestLock(LockFlags i_lockFlags, out int o_sessionResult)\r
700         {\r
701 #if TSF_DEBUG_OUTPUT\r
702             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
703 #endif\r
704             {\r
705                 o_sessionResult = UnmanagedAPI.WinError.HRESULT.E_FAIL;\r
706 \r
707                 if (_sink == null)\r
708                 {\r
709                     throw new COMException(\r
710                         "シンクが登録されていません。",\r
711                         UnmanagedAPI.TSF.TextStore.TsResult.CONNECT_E_NOCONNECTION\r
712                     );\r
713                 }\r
714 \r
715                 if (_lockFlags != 0)   // すでにロックされた状態の場合。\r
716                 {\r
717                     if ((i_lockFlags & LockFlags.TS_LF_SYNC) != 0)\r
718                     {\r
719                         o_sessionResult = TsResult.TS_E_SYNCHRONOUS;\r
720                         return;\r
721                     }\r
722                     else\r
723                         if ((_lockFlags & LockFlags.TS_LF_READWRITE) == LockFlags.TS_LF_READ\r
724                         && (i_lockFlags & LockFlags.TS_LF_READWRITE) == LockFlags.TS_LF_READWRITE)\r
725                         {\r
726                             _pendingLockUpgrade = true;\r
727                             o_sessionResult = TsResult.TS_S_ASYNC;\r
728                             return;\r
729                         }\r
730 \r
731                     throw new COMException();\r
732                 }\r
733 \r
734                 using (var unlocker = LockDocument(i_lockFlags))\r
735                 {\r
736                     // ロックに失敗した場合は TS_E_SYNCHRONOUS をセットして S_OK を返す。\r
737                     if (unlocker == null)\r
738                     {\r
739                         o_sessionResult = TsResult.TS_E_SYNCHRONOUS;\r
740                     }\r
741                     // ロックに成功した場合は OnLockGranted() を呼び出す。\r
742                     else\r
743                     {\r
744                         try\r
745                         {\r
746                             o_sessionResult = _sink.OnLockGranted(i_lockFlags);\r
747                         }\r
748                         catch (COMException comException)\r
749                         {\r
750                             Debug.WriteLine("OnLockGranted() 呼び出し中に例外が発生。");\r
751                             Debug.WriteLine("  " + comException.Message);\r
752                             o_sessionResult = comException.HResult;\r
753                         }\r
754                         catch (Exception exception)\r
755                         {\r
756                             Debug.WriteLine("OnLockGranted() 呼び出し中に例外が発生。");\r
757                             Debug.WriteLine("  " + exception.Message);\r
758                         }\r
759                     }\r
760                 }\r
761             }\r
762         }\r
763 \r
764         public void GetStatus(out TS_STATUS o_documentStatus)\r
765         {\r
766 #if TSF_DEBUG_OUTPUT\r
767             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
768 #endif\r
769             {\r
770                 if (IsReadOnly != null && IsReadOnly())\r
771                     o_documentStatus.dynamicFlags = DynamicStatusFlags.TS_SD_READONLY;\r
772                 if (IsLoading != null && IsLoading())\r
773                     o_documentStatus.dynamicFlags = DynamicStatusFlags.TS_SD_LOADING;\r
774                 else\r
775                     o_documentStatus.dynamicFlags = 0;\r
776                 o_documentStatus.staticFlags = StaticStatusFlags.TS_SS_REGIONS;\r
777             }\r
778         }\r
779 \r
780         public void QueryInsert(\r
781             int i_startIndex,\r
782             int i_endIndex,\r
783             int i_length,\r
784             out int o_startIndex,\r
785             out int o_endIndex\r
786         )\r
787         {\r
788 #if TSF_DEBUG_OUTPUT\r
789             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
790 #endif\r
791             {\r
792                 if (GetStringLength == null)\r
793                     throw new NotImplementedException();\r
794 \r
795                 if (i_startIndex < 0\r
796                 || i_startIndex > i_endIndex\r
797                 || i_endIndex > GetStringLength())\r
798                 {\r
799                     throw new COMException(\r
800                         "インデックスが無効です。",\r
801                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
802                     );\r
803                 }\r
804 \r
805                 o_startIndex = i_startIndex;\r
806                 o_endIndex = i_endIndex;\r
807             }\r
808         }\r
809 \r
810         public void GetSelection(\r
811             int i_index,\r
812             int i_selectionBufferLength,\r
813             TS_SELECTION_ACP[] o_selections,\r
814             out int o_fetchedLength\r
815         )\r
816         {\r
817 #if TSF_DEBUG_OUTPUT\r
818             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
819 #endif\r
820             {\r
821                 if (GetSelectionIndex == null)\r
822                     throw new NotImplementedException();\r
823 \r
824                 o_fetchedLength = 0;\r
825 \r
826                 if (IsLocked(LockFlags.TS_LF_READ) == false)\r
827                 {\r
828                     throw new COMException(\r
829                         "読取用ロックがされていません。",\r
830                         TsResult.TS_E_NOLOCK\r
831                     );\r
832                 }\r
833 \r
834                 // -1 は TF_DEFAULT_SELECTION。選択は1つだけしかサポートしないので、\r
835                 // TF_DEFAULT_SELECTION でもなく、0 を超える数値が指定された場合はエラー。\r
836                 if (i_index != -1 && i_index > 0)\r
837                 {\r
838                     throw new COMException(\r
839                         "選択は1つだけしかサポートしていません。",\r
840                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
841                     );\r
842                 }\r
843 \r
844                 if (i_selectionBufferLength > 0)\r
845                 {\r
846                     int start = 0, end = 0;\r
847                     GetSelectionIndex(out start, out end);\r
848                     if (start <= end)\r
849                     {\r
850                         o_selections[0].start = start;\r
851                         o_selections[0].end = end;\r
852                         o_selections[0].style.ase = TsActiveSelEnd.TS_AE_END;\r
853                         o_selections[0].style.interimChar = false;\r
854                     }\r
855                     else\r
856                     {\r
857                         o_selections[0].start = end;\r
858                         o_selections[0].end = start;\r
859                         o_selections[0].style.ase = TsActiveSelEnd.TS_AE_START;\r
860                         o_selections[0].style.interimChar = false;\r
861                     }\r
862 \r
863                     o_fetchedLength = 1;\r
864                 }\r
865             }\r
866         }\r
867 \r
868         public void SetSelection(int i_count, TS_SELECTION_ACP[] i_selections)\r
869         {\r
870 #if TSF_DEBUG_OUTPUT\r
871             using(var dbgout = new DebugOut("{0}({1}, {2})",\r
872                                             DebugOut.GetCaller(),\r
873                                             i_selections[0].start,\r
874                                             i_selections[0].end))\r
875 #endif\r
876             {\r
877                 if (SetSelectionIndex == null)\r
878                     throw new NotImplementedException();\r
879 \r
880                 if (i_count != 1)\r
881                 {\r
882                     throw new COMException(\r
883                         "選択は1つだけしかサポートしていません。",\r
884                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
885                     );\r
886                 }\r
887 \r
888                 if (IsLocked(LockFlags.TS_LF_READWRITE) == false)\r
889                 {\r
890                     throw new COMException(\r
891                         "ロックされていません。",\r
892                         TsResult.TS_E_NOLOCK\r
893                     );\r
894                 }\r
895 \r
896                 SetSelectionIndex(i_selections[0].start, i_selections[0].end);\r
897             }\r
898         }\r
899 \r
900         public void GetText(\r
901             int i_startIndex,\r
902             int i_endIndex,\r
903             char[] o_plainText,\r
904             int i_plainTextLength,\r
905             out int o_plainTextLength,\r
906             TS_RUNINFO[] o_runInfos,\r
907             int i_runInfoLength,\r
908             out int o_runInfoLength,\r
909             out int o_nextUnreadCharPos\r
910         )\r
911         {\r
912 #if TSF_DEBUG_OUTPUT\r
913             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
914 #endif\r
915             {\r
916                 if (GetStringLength == null || GetString == null)\r
917                     throw new NotImplementedException();\r
918 \r
919                 if (IsLocked(LockFlags.TS_LF_READ) == false)\r
920                 {\r
921                     throw new COMException(\r
922                         "読取用ロックがされていません。",\r
923                         TsResult.TS_E_NOLOCK\r
924                     );\r
925                 }\r
926 \r
927                 if ((i_endIndex != -1 && i_startIndex > i_endIndex)\r
928                 || i_startIndex < 0 || i_startIndex > GetStringLength()\r
929                 || i_endIndex > GetStringLength())\r
930                 {\r
931                     throw new COMException(\r
932                         "インデックスが無効です。",\r
933                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
934                     );\r
935                 }\r
936 \r
937                 var textLength = 0;\r
938                 var copyLength = 0;\r
939 \r
940                 if (i_endIndex == -1)\r
941                     textLength = GetStringLength() - i_startIndex;\r
942                 else\r
943                     textLength = i_endIndex - i_startIndex;\r
944                 copyLength = Math.Min(i_plainTextLength, textLength);\r
945 \r
946                 // 文字列を格納。\r
947                 var text = GetString(i_startIndex, copyLength);\r
948                 for (int i = 0; i < copyLength; i++)\r
949                 {\r
950                     o_plainText[i] = text[i];\r
951                 }\r
952 \r
953                 // 文字数を格納。\r
954                 o_plainTextLength = copyLength;\r
955                 // RUNINFO を格納。\r
956                 if (i_runInfoLength > 0)\r
957                 {\r
958                     o_runInfos[0].type = TsRunType.TS_RT_PLAIN;\r
959                     o_runInfos[0].length = copyLength;\r
960                 }\r
961                 o_runInfoLength = 1;\r
962 \r
963                 // 次の文字の位置を格納。\r
964                 o_nextUnreadCharPos = i_startIndex + copyLength;\r
965             }\r
966         }\r
967 \r
968         public void SetText(\r
969             SetTextFlags i_flags,\r
970             int i_startIndex,\r
971             int i_endIndex,\r
972             char[] i_text,\r
973             int i_length,\r
974             out TS_TEXTCHANGE o_textChange\r
975         )\r
976         {\r
977 #if TSF_DEBUG_OUTPUT\r
978             using(var dbgout = new DebugOut("{0}({1}, {2})",\r
979                                             DebugOut.GetCaller(),\r
980                                             i_startIndex, i_endIndex) )\r
981 #endif\r
982             {\r
983                 var selections = new TS_SELECTION_ACP[]\r
984                 {\r
985                     new TS_SELECTION_ACP\r
986                     {\r
987                         start = i_startIndex,\r
988                         end   = i_endIndex,\r
989                         style = new TS_SELECTIONSTYLE\r
990                         {\r
991                             ase = TsActiveSelEnd.TS_AE_END,\r
992                             interimChar = false\r
993                         }\r
994                     }\r
995                 };\r
996 \r
997                 int startIndex = 0, endIndex = 0;\r
998                 SetSelection(1, selections);\r
999                 InsertTextAtSelection(\r
1000                     InsertAtSelectionFlags.TF_IAS_NOQUERY,\r
1001                     i_text,\r
1002                     i_length,\r
1003                     out startIndex,\r
1004                     out endIndex,\r
1005                     out o_textChange\r
1006                 );\r
1007             }\r
1008         }\r
1009 \r
1010         public void GetFormattedText(int i_start, int i_end, out object o_obj)\r
1011         {\r
1012 #if TSF_DEBUG_OUTPUT\r
1013             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1014 #endif\r
1015             {\r
1016                 throw new NotImplementedException();\r
1017             }\r
1018         }\r
1019 \r
1020         public void GetEmbedded(\r
1021             int i_position,\r
1022             ref Guid i_guidService,\r
1023             ref Guid i_riid,\r
1024             out object o_obj\r
1025         )\r
1026         {\r
1027 #if TSF_DEBUG_OUTPUT\r
1028             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1029 #endif\r
1030             {\r
1031                 throw new NotImplementedException();\r
1032             }\r
1033         }\r
1034 \r
1035         public void QueryInsertEmbedded(\r
1036             ref Guid i_guidService,\r
1037             int i_formatEtc,\r
1038             out bool o_insertable\r
1039         )\r
1040         {\r
1041 #if TSF_DEBUG_OUTPUT\r
1042             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1043 #endif\r
1044             {\r
1045                 throw new NotImplementedException();\r
1046             }\r
1047         }\r
1048 \r
1049         public void InsertEmbedded(\r
1050             InsertEmbeddedFlags i_flags,\r
1051             int i_start,\r
1052             int i_end,\r
1053             object i_obj,\r
1054             out TS_TEXTCHANGE o_textChange\r
1055         )\r
1056         {\r
1057 #if TSF_DEBUG_OUTPUT\r
1058             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1059 #endif\r
1060             {\r
1061                 throw new NotImplementedException();\r
1062             }\r
1063         }\r
1064 \r
1065         public void InsertTextAtSelection(\r
1066             InsertAtSelectionFlags i_flags,\r
1067             char[] i_text,\r
1068             int i_length,\r
1069             out int o_startIndex,\r
1070             out int o_endIndex,\r
1071             out TS_TEXTCHANGE o_textChange\r
1072         )\r
1073         {\r
1074 #if TSF_DEBUG_OUTPUT\r
1075             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1076 #endif\r
1077             {\r
1078                 if (GetSelectionIndex == null || InsertAtSelection == null)\r
1079                     throw new NotImplementedException();\r
1080 \r
1081                 int selectionStart = 0, selectionEnd = 0;\r
1082                 GetSelectionIndex(out selectionStart, out selectionEnd);\r
1083 \r
1084                 // 問い合わせのみで実際には操作を行わない\r
1085                 if ((i_flags & InsertAtSelectionFlags.TF_IAS_QUERYONLY) != 0)\r
1086                 {\r
1087                     o_startIndex = Math.Min(selectionStart, selectionEnd);\r
1088                     o_endIndex = Math.Max(selectionStart, selectionEnd);//o_startIndex + i_length;\r
1089 \r
1090                     o_textChange.start = o_startIndex;\r
1091                     o_textChange.oldEnd = o_endIndex;\r
1092                     o_textChange.newEnd = o_startIndex + i_length;\r
1093                 }\r
1094                 else\r
1095                 {\r
1096                     var start = Math.Min(selectionStart, selectionEnd);\r
1097                     var end = Math.Max(selectionStart, selectionEnd);\r
1098 \r
1099 #if TSF_DEBUG_OUTPUT\r
1100                         Debug.WriteLine(string.Format(\r
1101                             "start: {0}, end: {1}, text: {2}",\r
1102                             start, end, new string(i_text)\r
1103                         ));\r
1104 #endif\r
1105 \r
1106                     InsertAtSelection(new string(i_text));\r
1107 \r
1108                     o_startIndex = start;\r
1109                     o_endIndex = start + i_length;\r
1110 \r
1111                     o_textChange.start = start;\r
1112                     o_textChange.oldEnd = end;\r
1113                     o_textChange.newEnd = start + i_length;\r
1114 \r
1115                     // InsertAtSelection() 内でカーソル位置を更新しているため、ここでは不要。\r
1116                     // 改行した時に位置が狂う。\r
1117                     // SetSelectionIndex(start, start + i_length);\r
1118                 }\r
1119             }\r
1120         }\r
1121 \r
1122         public void GetEndACP(out int o_length)\r
1123         {\r
1124 #if TSF_DEBUG_OUTPUT\r
1125             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1126 #endif\r
1127             {\r
1128                 if (GetStringLength == null)\r
1129                     throw new NotImplementedException();\r
1130 \r
1131                 if (IsLocked(LockFlags.TS_LF_READ) == false)\r
1132                 {\r
1133                     throw new COMException(\r
1134                         "読取用ロックがされていません。",\r
1135                         TsResult.TS_E_NOLOCK\r
1136                     );\r
1137                 }\r
1138 \r
1139                 o_length = GetStringLength();\r
1140             }\r
1141         }\r
1142 \r
1143         public void GetActiveView(out int o_viewCookie)\r
1144         {\r
1145 #if TSF_DEBUG_OUTPUT\r
1146             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1147 #endif\r
1148             {\r
1149                 o_viewCookie = 1;\r
1150             }\r
1151         }\r
1152 \r
1153         public void GetACPFromPoint(\r
1154     int i_viewCookie,\r
1155     ref POINT i_point,\r
1156     GetPositionFromPointFlags i_flags,\r
1157     out int o_index\r
1158 )\r
1159         {\r
1160 #if TSF_DEBUG_OUTPUT\r
1161             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1162 #endif\r
1163             {\r
1164                 throw new NotImplementedException();\r
1165             }\r
1166         }\r
1167 \r
1168         public void GetTextExt(\r
1169             int i_viewCookie,\r
1170             int i_startIndex,\r
1171             int i_endIndex,\r
1172             out RECT o_rect,\r
1173             out bool o_isClipped\r
1174         )\r
1175         {\r
1176 #if TSF_DEBUG_OUTPUT\r
1177             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1178 #endif\r
1179             {\r
1180                 if (GetStringLength == null)\r
1181                     throw new NotImplementedException();\r
1182 \r
1183                 // 読取用ロックの確認。\r
1184                 if (IsLocked(LockFlags.TS_LF_READ) == false)\r
1185                 {\r
1186                     throw new COMException(\r
1187                         "読取用ロックがされていません。",\r
1188                         TsResult.TS_E_NOLOCK\r
1189                     );\r
1190                 }\r
1191 \r
1192                 if ((i_endIndex != -1 && i_startIndex > i_endIndex)\r
1193                 || i_startIndex < 0 || i_startIndex > GetStringLength()\r
1194                 || i_endIndex > GetStringLength())\r
1195                 {\r
1196                     throw new COMException(\r
1197                         "インデックスが無効です。",\r
1198                         UnmanagedAPI.WinError.HRESULT.E_INVALIDARG\r
1199                     );\r
1200                 }\r
1201 \r
1202                 if (i_endIndex == -1)\r
1203                     i_endIndex = GetStringLength();\r
1204 \r
1205                 var pointTopLeft = new POINT();\r
1206                 var pointBotttomRight = new POINT();\r
1207                 GetStringExtent(i_startIndex, i_endIndex, out pointTopLeft, out pointBotttomRight);\r
1208 \r
1209                 o_rect.left = (int)pointTopLeft.x;\r
1210                 o_rect.top = (int)pointTopLeft.y;\r
1211                 o_rect.bottom = (int)pointBotttomRight.y;//startFormattedText.Height);\r
1212                 o_rect.right = (int)pointBotttomRight.x;\r
1213                 o_isClipped = false;\r
1214             }\r
1215         }\r
1216 \r
1217         public void GetScreenExt(int i_viewCookie, out RECT o_rect)\r
1218         {\r
1219 #if TSF_DEBUG_OUTPUT\r
1220             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1221 #endif\r
1222             {\r
1223                 if (GetScreenExtent == null)\r
1224                     throw new NotImplementedException();\r
1225 \r
1226                 POINT pointTopLeft, pointBottomRight;\r
1227 \r
1228                 GetScreenExtent(out pointTopLeft, out pointBottomRight);\r
1229 \r
1230                 o_rect.left = (int)pointTopLeft.x;\r
1231                 o_rect.top = (int)pointTopLeft.y;\r
1232                 o_rect.right = (int)pointBottomRight.x;\r
1233                 o_rect.bottom = (int)pointBottomRight.y;\r
1234             }\r
1235         }\r
1236 \r
1237         public void RequestSupportedAttrs(\r
1238             AttributeFlags i_flags,\r
1239             int i_length,\r
1240             Guid[] i_filterAttributes\r
1241         )\r
1242         {\r
1243 #if TSF_DEBUG_OUTPUT\r
1244             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1245 #endif\r
1246             {\r
1247                 for (int i = 0; i < i_length; i++)\r
1248                 {\r
1249                     if (i_filterAttributes[i].Equals(_attributeInfo[0].attrID))\r
1250                     {\r
1251                         _attributeInfo[0].flags = AttributeInfoFlags.Requested;\r
1252                         if ((i_flags & AttributeFlags.TS_ATTR_FIND_WANT_VALUE) != 0)\r
1253                         {\r
1254                             _attributeInfo[0].flags |= AttributeInfoFlags.Default;\r
1255                         }\r
1256                         else\r
1257                         {\r
1258                             _attributeInfo[0].currentValue = _attributeInfo[0].defaultValue;\r
1259                         }\r
1260                     }\r
1261                 }\r
1262             }\r
1263         }\r
1264 \r
1265         public void RequestAttrsAtPosition(\r
1266             int i_position,\r
1267             int i_length,\r
1268             Guid[] i_filterAttributes,\r
1269             AttributeFlags i_flags\r
1270         )\r
1271         {\r
1272 #if TSF_DEBUG_OUTPUT\r
1273             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1274 #endif\r
1275             {\r
1276                 for (int i = 0; i < i_length; i++)\r
1277                 {\r
1278                     if (i_filterAttributes[i].Equals(_attributeInfo[0].attrID))\r
1279                     {\r
1280                         _attributeInfo[0].flags = AttributeInfoFlags.Requested;\r
1281                         if ((i_flags & AttributeFlags.TS_ATTR_FIND_WANT_VALUE) != 0)\r
1282                         {\r
1283                             _attributeInfo[0].flags |= AttributeInfoFlags.Default;\r
1284                         }\r
1285                         else\r
1286                         {\r
1287                             _attributeInfo[0].currentValue = _attributeInfo[0].defaultValue;\r
1288                         }\r
1289                     }\r
1290                 }\r
1291             }\r
1292         }\r
1293 \r
1294         public void RequestAttrsTransitioningAtPosition(\r
1295             int i_position,\r
1296             int i_length,\r
1297             Guid[] i_filterAttributes,\r
1298             AttributeFlags i_flags\r
1299         )\r
1300         {\r
1301 #if TSF_DEBUG_OUTPUT\r
1302             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()))\r
1303 #endif\r
1304             {\r
1305                 // 何もしない。\r
1306             }\r
1307         }\r
1308 \r
1309         public void FindNextAttrTransition(\r
1310             int i_start,\r
1311             int i_halt,\r
1312             int i_length,\r
1313             Guid[] i_filterAttributes,\r
1314             AttributeFlags i_flags,\r
1315             out int o_nextIndex,\r
1316             out bool o_found,\r
1317             out int o_foundOffset\r
1318         )\r
1319         {\r
1320 #if TSF_DEBUG_OUTPUT\r
1321             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1322 #endif\r
1323             {\r
1324                 throw new NotImplementedException();\r
1325             }\r
1326         }\r
1327 \r
1328         public void RetrieveRequestedAttrs(\r
1329             int i_length,\r
1330             TS_ATTRVAL[] o_attributeVals,\r
1331             out int o_fetchedLength\r
1332         )\r
1333         {\r
1334 #if TSF_DEBUG_OUTPUT\r
1335             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1336 #endif\r
1337             {\r
1338                 o_fetchedLength = 0;\r
1339                 for (int i = 0; i < _attributeInfo.Length && o_fetchedLength < i_length; i++)\r
1340                 {\r
1341                     if ((_attributeInfo[i].flags & AttributeInfoFlags.Requested) != 0)\r
1342                     {\r
1343                         o_attributeVals[o_fetchedLength].overlappedId = 0;\r
1344                         o_attributeVals[o_fetchedLength].attributeId = _attributeInfo[i].attrID;\r
1345                         o_attributeVals[o_fetchedLength].val = _attributeInfo[i].currentValue;\r
1346 \r
1347                         if ((_attributeInfo[i].flags & AttributeInfoFlags.Default) != 0)\r
1348                             _attributeInfo[i].currentValue = _attributeInfo[i].defaultValue;\r
1349 \r
1350                         o_fetchedLength++;\r
1351                         _attributeInfo[i].flags = AttributeInfoFlags.None;\r
1352                     }\r
1353                 }\r
1354             }\r
1355         }\r
1356         #endregion\r
1357 \r
1358         #region "ITfContextOwnerCompositionSink インターフェイスの実装"\r
1359         /// <summary>コンポジション入力が開始された時の処理。</summary>\r
1360         public void OnStartComposition(ITfCompositionView view, out bool ok)\r
1361         {\r
1362             #if TSF_DEBUG_OUTPUT\r
1363             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1364             #endif\r
1365             {\r
1366                 if( CompositionStarted != null )\r
1367                     ok = CompositionStarted();\r
1368                 else\r
1369                     ok = true;\r
1370             }\r
1371         }\r
1372 \r
1373 \r
1374         //========================================================================================\r
1375 \r
1376 \r
1377         /// <summary>コンポジションが変更された時の処理。</summary>\r
1378         public void OnUpdateComposition(ITfCompositionView view, ITfRange rangeNew)\r
1379         {\r
1380             #if TSF_DEBUG_OUTPUT\r
1381             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1382             #endif\r
1383             {\r
1384                 if (rangeNew == null)\r
1385                     return;\r
1386 \r
1387                 int start, count;\r
1388                 \r
1389                 ITfRangeACP rangeAcp = (ITfRangeACP)rangeNew;\r
1390                 \r
1391                 rangeAcp.GetExtent(out start, out count);\r
1392                 \r
1393                 if (this.CompositionUpdated != null)\r
1394                     this.CompositionUpdated(start, start + count);\r
1395             }\r
1396         }\r
1397 \r
1398         \r
1399         //========================================================================================\r
1400 \r
1401 \r
1402         /// <summary>コンポジション入力が終了した時の処理。</summary>\r
1403         public void OnEndComposition(ITfCompositionView view)\r
1404         {\r
1405             #if TSF_DEBUG_OUTPUT\r
1406             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()) )\r
1407             #endif\r
1408             {\r
1409                 if( CompositionEnded != null )\r
1410                     CompositionEnded();\r
1411             }\r
1412         }\r
1413         #endregion "ITfContextOwnerCompositionSink インターフェイスの実装"\r
1414 \r
1415         #region 表示属性\r
1416         /// <summary>表示属性の取得</summary>\r
1417         public IEnumerable<TextDisplayAttribute> EnumAttributes(int start, int end)\r
1418         {\r
1419             ITfRangeACP allRange;\r
1420             _services.CreateRange(start, end, out allRange);\r
1421 \r
1422             foreach (TextDisplayAttribute attr in this.EnumAttributes((ITfRange)allRange))\r
1423                 yield return attr;\r
1424 \r
1425             ReleaseComObject("allRange", ref allRange);\r
1426         }\r
1427 \r
1428         IEnumerable<TextDisplayAttribute> EnumAttributes(ITfRange range)\r
1429         {\r
1430 #if TSF_DEBUG_OUTPUT\r
1431             using(var dbgout = new DebugOut("{0}()", DebugOut.GetCaller()))\r
1432 #endif\r
1433             {\r
1434                 ITfProperty property = null;    // プロパティインターフェイス\r
1435                 IEnumTfRanges enumRanges;         // 範囲の列挙子\r
1436                 Guid guidPropAttribute = TfDeclarations.GUID_PROP_ATTRIBUTE;\r
1437 \r
1438                 if (_context == null || _services == null)\r
1439                     yield break;\r
1440 \r
1441                 // GUID_PROP_ATTRIBUTE プロパティを取得。\r
1442                 _context.GetProperty(ref guidPropAttribute, out property);\r
1443                 if (property == null)\r
1444                     yield break;\r
1445 \r
1446                 // 全範囲の中で表示属性プロパティをもつ範囲を列挙する。\r
1447                 property.EnumRanges((int)_editCookie, out enumRanges, range);\r
1448 \r
1449                 ITfRange[] ranges = new ITfRange[1];\r
1450                 int fetchedLength = 0;\r
1451                 while (HRESULT.Succeeded(enumRanges.Next(1, ranges, out fetchedLength))\r
1452                     && fetchedLength > 0)\r
1453                 {\r
1454                     // ItfRange から ItfRangeACP を取得。\r
1455                     ITfRangeACP rangeACP = ranges[0] as ITfRangeACP;\r
1456                     if (rangeACP == null)\r
1457                         continue;   // 普通はあり得ない\r
1458 \r
1459                     // 範囲の開始位置と文字数を取得。\r
1460                     int start, count;\r
1461                     rangeACP.GetExtent(out start, out count);\r
1462 \r
1463                     // VARIANT 値としてプロパティ値を取得。VT_I4 の GUID ATOM がとれる。\r
1464                     VARIANT value = new VARIANT();\r
1465                     property.GetValue((int)_editCookie, ranges[0], ref value);\r
1466                     if (value.vt == (short)VarEnum.VT_I4)\r
1467                     {\r
1468                         Guid guid, clsid;\r
1469                         ITfDisplayAttributeInfo info;\r
1470                         TF_DISPLAYATTRIBUTE attribute;\r
1471 \r
1472                         // GUID ATOM から GUID を取得する。\r
1473                         _categoryMgr.GetGUID((int)value.data1, out guid);\r
1474                         // その GUID から IDisplayAttributeInfo インターフェイスを取得。\r
1475                         _displayAttributeMgr.GetDisplayAttributeInfo(\r
1476                             ref guid,\r
1477                             out info,\r
1478                             out clsid\r
1479                         );\r
1480                         // さらに IDisplayAttributeInfo インターフェイスから表示属性を取得する。\r
1481                         info.GetAttributeInfo(out attribute);\r
1482                         ReleaseComObject("info", ref info);\r
1483 \r
1484                         yield return new TextDisplayAttribute\r
1485                         {\r
1486                             startIndex = start,\r
1487                             endIndex = start + count,\r
1488                             attribute = attribute\r
1489                         };\r
1490 \r
1491 #if TSF_DEBUG_OUTPUT\r
1492                             Debug.WriteLine(\r
1493                                 "*******:::: DisplayAttribute: {0} ~ {1} :::::: *********",\r
1494                                 start, start + count\r
1495                             );\r
1496                             Debug.WriteLine(attribute.bAttr);\r
1497                             Debug.WriteLine(\r
1498                                 "LineColorType: {0}, {1}",\r
1499                                 attribute.crLine.type, attribute.crLine.indexOrColorRef\r
1500                             );\r
1501                             Debug.WriteLine(\r
1502                                 "TextColorType: {0}, {1}",\r
1503                                 attribute.crText.type, attribute.crText.indexOrColorRef\r
1504                             );\r
1505                             Debug.WriteLine(\r
1506                                 "BackColorType: {0}, {1}",\r
1507                                 attribute.crBk.type, attribute.crBk.indexOrColorRef\r
1508                             );\r
1509                             Debug.WriteLine(\r
1510                                 "Bold, Style  : {0}, {1}",\r
1511                                 attribute.fBoldLine, attribute.lsStyle\r
1512                             );\r
1513 #endif\r
1514                     }\r
1515 \r
1516                     ReleaseComObject("rangeACP", ref rangeACP);\r
1517                 }\r
1518 \r
1519                 ReleaseComObject("ranges[0]", ref ranges[0]);\r
1520                 ReleaseComObject("enumRanges", ref enumRanges);\r
1521                 ReleaseComObject("property", ref property);\r
1522             }\r
1523         }\r
1524         #endregion\r
1525 \r
1526 #if METRO\r
1527         protected ITfThreadMgr2 _threadMgr;\r
1528 #else\r
1529         protected ITfThreadMgr _threadMgr;\r
1530 #endif\r
1531         protected ITfDocumentMgr _documentMgr;\r
1532         protected ITfFunctionProvider _functionProvider;\r
1533         protected ITfFnReconversion _reconversion;\r
1534         protected ITfContext _context;\r
1535         protected ITfCategoryMgr _categoryMgr;\r
1536         protected ITfDisplayAttributeMgr _displayAttributeMgr;\r
1537         protected ITextStoreACPSink _sink;\r
1538         protected uint _editCookie = 0;\r
1539         protected ITextStoreACPServices _services;\r
1540         protected AdviseFlags               _adviseFlags = 0;\r
1541         protected LockFlags                 _lockFlags = 0;\r
1542         protected bool                      _pendingLockUpgrade = false;\r
1543 \r
1544         /// <summary>\r
1545         /// AttributeInfo で使用されるフラグ。各属性の状態を示す。\r
1546         /// </summary>\r
1547         protected enum AttributeInfoFlags\r
1548         {\r
1549             /// <summary>何もない。</summary>\r
1550             None        = 0,\r
1551             /// <summary>デフォルト値の要求。</summary>\r
1552             Default     = 1 << 0,\r
1553             /// <summary>要求された。</summary>\r
1554             Requested   = 1 << 1\r
1555         }\r
1556 \r
1557         protected struct AttributeInfo\r
1558         {\r
1559             public Guid                 attrID;\r
1560             public AttributeInfoFlags   flags;\r
1561             public VARIANT              currentValue;\r
1562             public VARIANT              defaultValue;\r
1563         }\r
1564 \r
1565         protected AttributeInfo[]       _attributeInfo = new AttributeInfo[1];\r
1566 \r
1567     }\r
1568 }\r