プロパティ (共通要素)
Text Services Framework (TSF) は、メタデータをテキストの範囲に関連付けるプロパティを提供します。 これらのプロパティには、太字テキストなどの表示属性、テキストの言語識別子、音声テキスト サービスのテキストに関連付けられたオーディオ データなどのテキスト サービスによって提供される生データが含まれますが、これらに限定されません。
次の例では、赤 (R)、緑 (G)、または青 (B) の可能な値を持つ架空のテキストの色プロパティを表示する方法を示します。
COLOR: RR GGGGGGGG
TEXT: this is some colored text
異なる型のプロパティは重複する可能性があります。 たとえば、前の例を見て、太字 (B) または斜体 (I) のテキスト属性を追加します。
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
テキスト "this" は太字、"is" は太字と赤の両方になり、"some" は通常表示され、"色付き" は緑色で斜体になり、"text" は斜体になります。
同じ型のプロパティは重複できません。 たとえば、"is" と "colored" には同じ型の値が重複しているため、次の状況は許可されません。
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
プロパティの型
TSF は、3 種類のプロパティを定義します。
プロパティの種類 | 説明 |
---|---|
静的 | 静的プロパティ オブジェクトは、プロパティ データをテキストと共に格納します。 また、プロパティが適用される各範囲のテキスト情報の範囲も格納されます。 ITfReadOnlyProperty::GetType は、GUID_TFCAT_PROPSTYLE_STATIC カテゴリを返します。 |
Static-Compact | 静的コンパクト プロパティ オブジェクトは、静的コンパクト プロパティが範囲データを格納しない点を除き、静的プロパティ オブジェクトと同じです。 静的コンパクト プロパティの対象となる範囲が要求されると、隣接するプロパティのグループごとに範囲が作成されます。 静的コンパクト プロパティは、文字単位でプロパティを格納する最も効率的な方法です。 ITfReadOnlyProperty::GetType は、GUID_TFCAT_PROPSTYLE_STATICCOMPACT カテゴリを返します。 |
Custom | カスタム プロパティ オブジェクトには、プロパティが適用される各範囲の範囲情報が格納されます。 ただし、プロパティの実際のデータは格納されません。 代わりに、カスタム プロパティには ITfPropertyStore オブジェクトが格納されます。 TSF マネージャーは、このオブジェクトを使用して、プロパティ・データにアクセスして保守します。ITfReadOnlyProperty::GetType は、GUID_TFCAT_PROPSTYLE_CUSTOM カテゴリを返します。 |
プロパティの操作
プロパティの値と属性は、 ITfReadOnlyProperty インターフェイスを使用して取得され、 ITfProperty インターフェイスを使用して変更されます。
特定のプロパティ型が必要な場合は、 ITfContext::GetProperty が使用されます。 ITfContext::GetProperty には、取得するプロパティを識別する GUID が必要です。 TSF は、 定義されたプロパティ識別子のセットを定義します 使用するか、テキスト サービスで独自のプロパティ識別子を定義できます。 カスタム プロパティを使用する場合、プロパティ プロバイダーはプロパティ GUID および取得したデータの形式を公開する必要があります。
たとえば、テキスト範囲の所有者のCLSIDを取得するには、ITfContext::GetProperty を呼び出してプロパティ オブジェクトを取得します。 ITfProperty::FindRange を呼び出してプロパティを完全にカバーする範囲を取得し、ITfReadOnlyProperty::GetValue を呼び出して、テキストを所有するテキスト サービスのCLSIDを表すTfGuidAtomを取得します。 次の例は、コンテキスト、範囲、および編集 Cookie を指定すると、テキストを所有するテキスト サービスの CLSID を取得する関数を示しています。
HRESULT GetTextOwner( ITfContext *pContext,
ITfRange *pRange,
TfEditCookie ec,
CLSID *pclsidOwner)
{
HRESULT hr;
ITfProperty *pProp;
*pclsidOwner = GUID_NULL;
hr = pContext->GetProperty(GUID_PROP_TEXTOWNER, &pProp);
if(S_OK == hr)
{
ITfRange *pPropRange;
hr = pProp->FindRange(ec, pRange, &pPropRange, TF_ANCHOR_START);
if(S_OK == hr)
{
VARIANT var;
VariantInit(&var);
hr = pProp->GetValue(ec, pPropRange, &var);
if(S_OK == hr)
{
if(VT_I4 == var.vt)
{
/*
var.lVal is a TfGuidAtom that represents the CLSID of the
text owner. Use ITfCategoryMgr to obtain the CLSID from
the TfGuidAtom.
*/
ITfCategoryMgr *pCatMgr;
hr = CoCreateInstance( CLSID_TF_CategoryMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITfCategoryMgr,
(LPVOID*)&pCatMgr);
if(SUCCEEDED(hr))
{
hr = pCatMgr->GetGUID((TfGuidAtom)var.lVal, pclsidOwner);
if(SUCCEEDED(hr))
{
/*
*pclsidOwner now contains the CLSID of the text
service that owns the text at the selection.
*/
}
pCatMgr->Release();
}
}
else
{
//Unrecognized VARIANT type
hr = E_FAIL;
}
VariantClear(&var);
}
pPropRange->Release();
}
pProp->Release();
}
return hr;
}
プロパティは、ITfContext::EnumProperties から IEnumTfProperties インターフェイスを取得して列挙することもできます。
プロパティの永続的なストレージ
多くの場合、プロパティはアプリケーションに対して透過的であり、1 つ以上のテキスト サービスによって使用されます。 ファイルに保存する場合など、プロパティ データを保持するには、アプリケーションが格納されるときにプロパティ データをシリアル化し、データの復元時にプロパティ データを非シリアル化する必要があります。 この場合、アプリケーションは個々のプロパティに関心を持つ必要はありませんが、コンテキスト内のすべてのプロパティを列挙して格納する必要があります。
プロパティ データを格納する場合、アプリケーションは次の手順を実行する必要があります。
- ITfContext::EnumProperties を呼び出して、プロパティ列挙子を取得します。
- IEnumTfProperties::Next を呼び出して、各プロパティを列挙します。
- プロパティごとに、 ITfReadOnlyProperty::EnumRanges を呼び出して範囲列挙子を取得します。
- IEnumTfRanges::Next を呼び出して、プロパティ内の各範囲を列挙します。
- プロパティ内の各範囲について、 ITextStoreACPServices::Serialize アプリケーションによって実装されるプロパティ、範囲、 TF_PERSISTENT_PROPERTY_HEADER_ACP 構造体、およびストリーム オブジェクトを呼び出します。
- TF_PERSISTENT_PROPERTY_HEADER_ACP構造体の内容を永続メモリに書き込みます。
- ストリーム オブジェクトの内容を永続メモリに書き込みます。
- すべてのプロパティのすべての範囲について、前の手順を続行します。
- データが復元されたときに停止ポイントを識別できるように、アプリケーションは何らかの種類のターミネータをストリームに書き込む必要があります。
HRESULT SaveProperties( ITfContext *pContext,
ITextStoreACPServices *pServices,
TfEditCookie ec,
IStream *pStream)
{
HRESULT hr;
IEnumTfProperties *pEnumProps;
TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;
ULONG uWritten;
//Enumerate the properties in the context.
hr = pContext->EnumProperties(&pEnumProps);
if(SUCCEEDED(hr))
{
ITfProperty *pProp;
ULONG uFetched;
while(SUCCEEDED(pEnumProps->Next(1, &pProp, &uFetched)) && uFetched)
{
//Enumerate all the ranges that contain the property.
IEnumTfRanges *pEnumRanges;
hr = pProp->EnumRanges(ec, &pEnumRanges, NULL);
if(SUCCEEDED(hr))
{
IStream *pTempStream;
//Create a temporary stream to write the property data to.
hr = CreateStreamOnHGlobal(NULL, TRUE, &pTempStream);
if(SUCCEEDED(hr))
{
ITfRange *pRange;
while(SUCCEEDED(pEnumRanges->Next(1, &pRange, &uFetched)) && uFetched)
{
LARGE_INTEGER li;
//Reset the temporary stream pointer.
li.QuadPart = 0;
pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
//Get the property header and data for the range.
hr = pServices->Serialize(pProp, pRange, &PropHeader, pTempStream);
/*
Write the property header into the primary stream.
The header also contains the size of the property
data.
*/
hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);
//Reset the temporary stream pointer.
li.QuadPart = 0;
pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
//Copy the property data from the temporary stream into the primary stream.
ULARGE_INTEGER uli;
uli.QuadPart = PropHeader.cb;
hr = pTempStream->CopyTo(pStream, uli, NULL, NULL);
pRange->Release();
}
pTempStream->Release();
}
pEnumRanges->Release();
}
pProp->Release();
}
pEnumProps->Release();
}
//Write a property header with zero size and guid into the stream as a terminator
ZeroMemory(&PropHeader, sizeof(PropHeader));
hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);
return hr;
}
ITextStoreACPServices::SerializeITfPropertyStore::Serialize
プロパティ データを復元する場合、アプリケーションは次の手順を実行する必要があります。
ストリーム ポインターを最初の TF_PERSISTENT_PROPERTY_HEADER_ACP 構造体の先頭に設定します。
TF_PERSISTENT_PROPERTY_HEADER_ACP構造体を読み取る。
TF_PERSISTENT_PROPERTY_HEADER_ACP構造体の guidType メンバーを使用して ITfContext::GetProperty を呼び出します。
この時点で、アプリケーションは 2 つのうち 1 つを実行できます。
- アプリケーションで実装する必要がある ITfPersistentPropertyLoaderACP オブジェクトのインスタンスを作成します。 次に、ITextStoreACPServices::Unserialize pStream および ITfPersistentPropertyLoaderACP ポインターに対して NULL を指定して呼び出します。
- 入力ストリームを ITextStoreACPServices::Unserialize に渡し、pLoader の場合は NULL をします。
最も効率的であるため、最初の方法が好ましい。 2 番目のメソッドを実装すると、 ITextStoreACPServices::Unserialize 呼び出し中に、すべてのプロパティ データがストリームから読み取られます。 最初のメソッドでは、プロパティ データが後でオンデマンドで読み取られます。
すべてのプロパティ ブロックが初期化されないまで、前の手順を繰り返します。
HRESULT LoadProperties( ITfContext *pContext,
ITextStoreACPServices *pServices,
IStream *pStream)
{
HRESULT hr;
ULONG uRead;
TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;
/*
Read each property header and property data from the stream. The
list of properties is terminated by a TF_PERSISTENT_PROPERTY_HEADER_ACP
structure with a cb member of zero.
*/
hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
while( SUCCEEDED(hr) &&
(sizeof(PropHeader) == uRead) &&
(0 != PropHeader.cb))
{
ITfProperty *pProp;
hr = pContext->GetProperty(PropHeader.guidType, &pProp);
if(SUCCEEDED(hr))
{
/*
Have TSF read the property data from the stream. This call
requests a read-only lock, so be sure it can be granted
or else this method will fail.
*/
CTSFPersistentPropertyLoader *pLoader = new CTSFPersistentPropertyLoader(&PropHeader, pStream);
hr = pServices->Unserialize(pProp, &PropHeader, NULL, pLoader);
pProp->Release();
}
//Read the next header.
hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
}
return hr;
}