Proprietà (elementi comuni)
Text Services Framework (TSF) fornisce proprietà che associano metadati a un intervallo di testo. Queste proprietà includono, ad esempio, attributi di visualizzazione, ad esempio testo in grassetto, identificatore della lingua del testo e dati non elaborati forniti da un servizio di testo, ad esempio i dati audio associati al testo dal servizio di testo vocale.
Nell'esempio seguente viene illustrato come visualizzare una proprietà ipotetica del colore del testo con valori possibili di rosso (R), verde (G) o blu (B).
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Le proprietà di tipi diversi possono sovrapporsi. Ad esempio, prendere l'esempio precedente e aggiungere un attributo di testo che può essere in grassetto (B) o corsivo (I).
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Il testo "this" sarebbe in grassetto, "is" sarebbe sia grassetto che rosso, "alcuni" verrebbero visualizzati normalmente, "color" sarebbe verde e corsivo e "text" sarebbe corsivo.
Le proprietà dello stesso tipo non possono sovrapporsi. Ad esempio, la situazione seguente non è consentita perché "è" e "colorato" hanno valori sovrapposti degli stessi tipi.
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
TSF definisce tre tipi diversi di proprietà.
Tipo di proprietà | Descrizione |
---|---|
Statico | Un oggetto proprietà statica archivia i dati della proprietà con testo. Archivia inoltre l'intervallo di informazioni di testo per ogni intervallo a cui si applica la proprietà . ITfReadOnlyProperty::GetType restituisce la categoria GUID_TFCAT_PROPSTYLE_STATIC. |
Static-Compact | Un oggetto proprietà static-compact è identico a un oggetto proprietà statica, ad eccezione di una proprietà static-compact non archivia i dati dell'intervallo. Quando viene richiesto l'intervallo coperto da una proprietà static-compact, viene creato un intervallo per ogni gruppo di proprietà adiacenti. Le proprietà statiche-compatta sono il modo più efficiente per archiviare le proprietà in base al carattere. ITfReadOnlyProperty::GetType restituisce la categoria GUID_TFCAT_PROPSTYLE_STATICCOMPACT. |
Personalizzazione | Un oggetto proprietà personalizzata archivia le informazioni sull'intervallo per ogni intervallo a cui si applica la proprietà . Non archivia tuttavia i dati effettivi per la proprietà . Una proprietà personalizzata archivia invece un oggetto ITfPropertyStore. Il gestore TSF usa questo oggetto per accedere e gestire i dati delle proprietà. ITfReadOnlyProperty::GetType restituisce la categoria GUID_TFCAT_PROPSTYLE_CUSTOM. |
Il valore e gli attributi della proprietà vengono ottenuti usando l'interfaccia ITfReadOnlyProperty e modificati usando l'interfaccia ITfProperty .
Se è necessario un tipo di proprietà specifico, viene usato ITfContext::GetProperty . ITfContext::GetProperty richiede un GUID che identifica la proprietà da ottenere. TSF definisce un set di identificatori di proprietà predefiniti utilizzati o un servizio di testo può definire i propri identificatori di proprietà. Se viene utilizzata una proprietà personalizzata, il provider di proprietà deve pubblicare il GUID della proprietà e il formato dei dati ottenuti.
Ad esempio, per ottenere il CLSID per il proprietario di un intervallo di testo, chiamare ITfContext::GetProperty per ottenere l'oggetto proprietà, chiamare ITfProperty::FindRange per ottenere l'intervallo che copre interamente la proprietà, quindi chiamare ITfReadOnlyProperty::GetValue per ottenere un TfGuidAtom che rappresenta il CLSID del servizio di testo proprietario del testo. Nell'esempio seguente viene illustrata una funzione che, data un contesto, un intervallo e un cookie di modifica, otterrà il CLSID del servizio di testo proprietario del testo.
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;
}
Le proprietà possono essere enumerate anche ottenendo un'interfaccia IEnumTfProperties da ITfContext::EnumProperties.
Spesso, le proprietà sono trasparenti per un'applicazione e vengono usate da uno o più servizi di testo. Per conservare i dati della proprietà, ad esempio quando si salva in un file, un'applicazione deve serializzare i dati della proprietà quando vengono archiviati e annullati l'inizializzazione dei dati della proprietà quando i dati vengono ripristinati. In questo caso, l'applicazione non deve essere interessata alle singole proprietà, ma deve enumerare tutte le proprietà nel contesto e archiviarle.
Quando si archiviano i dati delle proprietà, un'applicazione deve eseguire la procedura seguente.
- Ottenere un enumeratore di proprietà chiamando ITfContext::EnumProperties.
- Enumerare ogni proprietà chiamando IEnumTfProperties::Next.
- Per ogni proprietà, ottenere un enumeratore di intervalli chiamando ITfReadOnlyProperty::EnumRanges.
- Enumerare ogni intervallo nella proprietà chiamando IEnumTfRanges::Next.
- Per ogni intervallo nella proprietà, chiamare ITextStoreACPServices::Serialize con la proprietà, l'intervallo, una struttura TF_PERSISTENT_PROPERTY_HEADER_ACP e un oggetto flusso implementato dall'applicazione.
- Scrivere il contenuto della struttura TF_PERSISTENT_PROPERTY_HEADER_ACP in memoria persistente.
- Scrivere il contenuto dell'oggetto flusso in memoria persistente.
- Continuare i passaggi precedenti per tutti gli intervalli in tutte le proprietà.
- L'applicazione deve scrivere un tipo di terminatore nel flusso in modo che, quando i dati vengono ripristinati, sia possibile identificare un punto di arresto.
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
Quando si ripristinano i dati delle proprietà, un'applicazione deve eseguire la procedura seguente.
Impostare il puntatore del flusso all'inizio della prima struttura TF_PERSISTENT_PROPERTY_HEADER_ACP .
Leggere la struttura TF_PERSISTENT_PROPERTY_HEADER_ACP .
Chiamare ITfContext::GetProperty con il membro guidType della struttura TF_PERSISTENT_PROPERTY_HEADER_ACP .
L'applicazione può eseguire una delle due operazioni a questo punto.
- Creare un'istanza di un oggetto ITfPersistentPropertyLoaderACP che l'applicazione deve implementare. Chiamare quindi ITextStoreACPServices::Unserialize con NULL per pStream e il puntatore ITfPersistentPropertyLoaderACP .
- Passare il flusso di input a ITextStoreACPServices::Unserialize e NULL per pLoader.
Il primo metodo è preferibile perché è il più efficiente. L'implementazione del secondo metodo fa sì che tutti i dati della proprietà vengano letti dal flusso durante la chiamata ITextStoreACPServices::Unserialize . Il primo metodo fa sì che i dati della proprietà vengano letti su richiesta in un secondo momento.
Ripetere i passaggi precedenti fino a quando tutti i blocchi di proprietà non vengono annullati.
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;
}