Propriedades (elementos comuns)
A TSF (Estrutura de Serviços de Texto) fornece propriedades que associam metadados a um intervalo de texto. Essas propriedades incluem, mas não estão limitadas a, atributos de exibição, como texto em negrito, o identificador de idioma do texto e dados brutos fornecidos por um serviço de texto, como os dados de áudio associados ao texto do serviço de texto de fala.
O exemplo a seguir demonstra como uma propriedade hipotética de cor de texto com valores possíveis de vermelho (R), verde (G) ou azul (B) pode ser exibida.
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Propriedades de diferentes tipos podem se sobrepor. Por exemplo, pegue o exemplo anterior e adicione um atributo de texto que pode estar em negrito (B) ou itálico (I).
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
O texto "this" estaria em negrito, "is" seria negrito e vermelho, "alguns" seria exibido normalmente, "colorido" seria verde e em itálico e "texto" estaria em itálico.
Propriedades do mesmo tipo não podem se sobrepor. Por exemplo, a situação a seguir não é permitida porque "é" e "colorido" têm valores sobrepostos dos mesmos tipos.
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
O TSF define três tipos diferentes de propriedades.
Tipo de propriedade | Descrição |
---|---|
Estático | Um objeto de propriedade estática armazena os dados da propriedade com texto. Ele também armazena o intervalo de informações de texto para cada intervalo ao qual a propriedade se aplica. ITfReadOnlyProperty::GetType retorna a categoria GUID_TFCAT_PROPSTYLE_STATIC. |
estático-compacto | Um objeto de propriedade static-compact é idêntico a um objeto de propriedade estática, exceto que uma propriedade static-compact não armazena dados de intervalo. Quando o intervalo coberto por uma propriedade estático-compacta é solicitado, um intervalo é criado para cada grupo de propriedades adjacentes. As propriedades estáticas compactas são a maneira mais eficiente de armazenar propriedades por caractere. ITfReadOnlyProperty::GetType retorna a categoria GUID_TFCAT_PROPSTYLE_STATICCOMPACT. |
Personalizado | Um objeto de propriedade personalizada armazena as informações de intervalo para cada intervalo ao qual a propriedade se aplica. No entanto, ele não armazena os dados reais da propriedade. Em vez disso, uma propriedade personalizada armazena um objeto ITfPropertyStore. O gerenciador TSF usa esse objeto para acessar e manter os dados da propriedade. ITfReadOnlyProperty::GetType retorna a categoria GUID_TFCAT_PROPSTYLE_CUSTOM. |
O valor e os atributos da propriedade são obtidos usando a interface ITfReadOnlyProperty e modificados usando a interface ITfProperty .
Se um tipo de propriedade específico for necessário, ITfContext::GetProperty será usado. ITfContext::GetProperty requer um GUID que identifica a propriedade a ser obtida. O TSF define um conjunto de identificadores de propriedade predefinidos usados ou um serviço de texto pode definir seus próprios identificadores de propriedade. Se uma propriedade personalizada for usada, o provedor de propriedades deverá publicar o GUID da propriedade e o formato dos dados obtidos.
Por exemplo, para obter o CLSID para o proprietário de um intervalo de texto, chame ITfContext::GetProperty para obter o objeto de propriedade, chame ITfProperty::FindRange para obter o intervalo que cobre totalmente a propriedade e, em seguida, chame ITfReadOnlyProperty::GetValue para obter um TfGuidAtom que represente o CLSID do serviço de texto que possui o texto. O exemplo a seguir mostra uma função que, dado um contexto, um intervalo e um cookie de edição, obterá o CLSID do serviço de texto que possui o texto.
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;
}
As propriedades também podem ser enumeradas obtendo uma interface IEnumTfProperties de ITfContext::EnumProperties.
Muitas vezes, as propriedades são transparentes para um aplicativo e são usadas por um ou mais serviços de texto. Para preservar os dados de propriedade, como ao salvar em um arquivo, um aplicativo deve serializar os dados de propriedade quando eles são armazenados e desserializar os dados de propriedade quando os dados são restaurados. Nesse caso, o aplicativo não deve estar interessado em propriedades individuais, mas deve enumerar todas as propriedades no contexto e armazená-las.
Ao armazenar dados de propriedade, um aplicativo deve executar as etapas a seguir.
- Obtenha um enumerador de propriedades chamando ITfContext::EnumProperties.
- Enumere cada propriedade chamando IEnumTfProperties::Next.
- Para cada propriedade, obtenha um enumerador de intervalo chamando ITfReadOnlyProperty::EnumRanges.
- Enumere cada intervalo na propriedade chamando IEnumTfRanges::Next.
- Para cada intervalo na propriedade, chame ITextStoreACPServices::Serialize com a propriedade, o intervalo, uma estrutura TF_PERSISTENT_PROPERTY_HEADER_ACP e um objeto de fluxo implementado pelo aplicativo.
- Escreva o conteúdo da estrutura TF_PERSISTENT_PROPERTY_HEADER_ACP na memória persistente.
- Grave o conteúdo do objeto de fluxo na memória persistente.
- Continue as etapas anteriores para todos os intervalos em todas as propriedades.
- O aplicativo deve gravar algum tipo de terminador no fluxo para que, quando os dados forem restaurados, um ponto de parada possa ser identificado.
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
Ao restaurar dados de propriedade, um aplicativo deve executar as etapas a seguir.
Defina o ponteiro de fluxo para o início da primeira estrutura TF_PERSISTENT_PROPERTY_HEADER_ACP .
Leia a estrutura TF_PERSISTENT_PROPERTY_HEADER_ACP .
Chame ITfContext::GetProperty com o membro guidType da estrutura TF_PERSISTENT_PROPERTY_HEADER_ACP .
O aplicativo pode fazer uma de duas coisas neste momento.
- Crie uma instância de um objeto ITfPersistentPropertyLoaderACP que o aplicativo deve implementar. Em seguida, chame ITextStoreACPServices::Unserialize com NULL para pStream e o ponteiro ITfPersistentPropertyLoaderACP.
- Passe o fluxo de entrada para ITextStoreACPServices::Unserialize e NULL para pLoader.
O primeiro método é preferido, pois é o mais eficiente. A implementação do segundo método faz com que todos os dados de propriedade sejam lidos do fluxo durante a chamada ITextStoreACPServices::Unserialize . O primeiro método faz com que os dados da propriedade sejam lidos sob demanda posteriormente.
Repita as etapas anteriores até que todos os blocos de propriedades não sejam serializados.
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;
}