Właściwości (wspólne elementy)
Struktura usług tekstowych (TSF) udostępnia właściwości, które kojarzą metadane z zakresem tekstu. Te właściwości obejmują, ale nie są ograniczone do atrybutów wyświetlania, takich jak tekst pogrubiony, identyfikator języka tekstu i nieprzetworzone dane dostarczane przez usługę tekstową, takie jak dane audio skojarzone z tekstem z usługi zamiany mowy.
W poniższym przykładzie pokazano, jak można wyświetlić hipotetyczną właściwość koloru tekstu z możliwymi wartościami czerwonego (R), zielonego (G) lub niebieskiego (B).
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Właściwości różnych typów mogą się nakładać. Na przykład weźmy poprzedni przykład i dodaj atrybut tekstowy, który może być pogrubiony (B) lub kursywa (I).
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Tekst "this" będzie pogrubiony, "is" będzie pogrubiony i czerwony, "some" będzie wyświetlany normalnie, "colored" będzie zielony, a kursywa i "tekst" będzie kursywą.
Właściwości tego samego typu nie mogą się nakładać. Na przykład następująca sytuacja nie jest dozwolona, ponieważ "is" i "colored" mają nakładające się wartości tego samego typu.
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Typy właściwości
TSF definiuje trzy różne typy właściwości.
Typ właściwości | Opis |
---|---|
Statyczny | Obiekt właściwości statycznej przechowuje dane właściwości za pomocą tekstu. Przechowuje również zakres informacji tekstowych dla każdego zakresu, do którego ma zastosowanie właściwość. ITfReadOnlyProperty::GetType zwraca kategorię GUID_TFCAT_PROPSTYLE_STATIC. |
Static-Compact | Obiekt właściwości static-compact jest identyczny z obiektem właściwości statycznej, z wyjątkiem właściwości static-compact nie przechowuje danych zakresu. Gdy żądany jest zakres objęty właściwością static-compact, dla każdej grupy sąsiednich właściwości jest tworzony zakres. Właściwości statyczno-kompaktowe to najbardziej wydajny sposób przechowywania właściwości na podstawie poszczególnych znaków. ITfReadOnlyProperty::GetType zwraca kategorię GUID_TFCAT_PROPSTYLE_STATICCOMPACT. |
Zwyczaj | Obiekt właściwości niestandardowej przechowuje informacje o zakresie dla każdego zakresu, do którego ma zastosowanie właściwość. Nie przechowuje jednak rzeczywistych danych dla właściwości. Zamiast tego właściwość niestandardowa przechowuje obiekt ITfPropertyStore. Menedżer TSF używa tego obiektu do uzyskiwania dostępu do danych właściwości i ich obsługi. ITfReadOnlyProperty::GetType zwraca kategorię GUID_TFCAT_PROPSTYLE_CUSTOM. |
Praca z właściwościami
Wartość właściwości i atrybuty są uzyskiwane przy użyciu interfejsu ITfReadOnlyProperty i modyfikowane przy użyciu interfejsu ITfProperty.
Jeśli wymagany jest określony typ właściwości, zostanie użyty ITfContext::GetProperty. ITfContext::GetProperty wymaga identyfikatora GUID identyfikującego właściwość do uzyskania. TSF definiuje zestaw wstępnie zdefiniowanych identyfikatorów właściwości używanych lub usługa tekstowa może definiować własne identyfikatory właściwości. Jeśli jest używana właściwość niestandardowa, dostawca właściwości musi opublikować właściwość identyfikator GUID i format uzyskanych danych.
Aby na przykład uzyskać CLSID właściciela zakresu tekstu, wywołaj ITfContext::GetProperty, aby uzyskać obiekt właściwości, wywołaj ITfProperty::FindRange, aby uzyskać zakres, który całkowicie obejmuje właściwość, a następnie wywołaj ITfReadOnlyProperty::GetValue, aby uzyskać TfGuidAtom reprezentujący CLSID usługi tekstowej będącej właścicielem tekstu. W poniższym przykładzie przedstawiono funkcję, która, biorąc pod uwagę kontekst, zakres i plik cookie edycji, uzyska CLSID usługi tekstowej, która jest właścicielem tekstu.
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;
}
Właściwości można również wyliczyć, uzyskując interfejs IEnumTfProperties z ITfContext::EnumProperties.
Trwały magazyn właściwości
Często właściwości są niewidoczne dla aplikacji i są używane przez co najmniej jedną usługę tekstową. Aby zachować dane właściwości, takie jak podczas zapisywania w pliku, aplikacja musi serializować dane właściwości, gdy są przechowywane i nieserializować dane właściwości po przywróceniu danych. W takim przypadku aplikacja nie powinna być zainteresowana poszczególnymi właściwościami, ale powinna wyliczać wszystkie właściwości w kontekście i przechowywać je.
Podczas przechowywania danych właściwości aplikacja powinna wykonać następujące kroki.
- Uzyskaj moduł wyliczający właściwości, wywołując ITfContext::EnumProperties.
- Wyliczaj każdą właściwość, wywołując IEnumTfProperties::Next.
- Dla każdej właściwości uzyskaj moduł wyliczający zakresów, wywołując ITfReadOnlyProperty::EnumRanges.
- Wyliczanie każdego zakresu we właściwości przez wywołanie IEnumTfRanges::Next.
- Dla każdego zakresu we właściwości wywołaj metodę ITextStoreACPServices::Serialize z właściwością, zakresem, strukturą TF_PERSISTENT_PROPERTY_HEADER_ACP i obiektem strumienia zaimplementowanym przez aplikację.
- Zapisz zawartość struktury TF_PERSISTENT_PROPERTY_HEADER_ACP w pamięci trwałej.
- Zapisz zawartość obiektu strumienia w pamięci trwałej.
- Kontynuuj poprzednie kroki dla wszystkich zakresów we wszystkich właściwościach.
- Aplikacja powinna zapisać jakiś typ terminatora do strumienia, aby po przywróceniu danych można zidentyfikować punkt zatrzymania.
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
Podczas przywracania danych właściwości aplikacja powinna wykonać następujące kroki.
Ustaw wskaźnik strumienia na początek pierwszej struktury TF_PERSISTENT_PROPERTY_HEADER_ACP.
Przeczytaj strukturę TF_PERSISTENT_PROPERTY_HEADER_ACP.
Wywołaj ITfContext::GetProperty za pomocą elementu guidType składowej struktury TF_PERSISTENT_PROPERTY_HEADER_ACP.
Aplikacja może w tym momencie wykonać jedną z dwóch czynności.
- Utwórz wystąpienie obiektu ITfPersistentPropertyLoaderACP, który musi zaimplementować aplikacja. Następnie wywołaj ITextStoreACPServices::Unserialize za pomocą null dla pStream i wskaźnika ITfPersistentPropertyLoaderACP.
- Przekaż strumień wejściowy do ITextStoreACPServices::Unserialize i null dla pLoader.
Pierwsza metoda jest preferowana, ponieważ jest najbardziej wydajna. Implementacja drugiej metody powoduje odczytywanie wszystkich danych właściwości ze strumienia podczas wywołania ITextStoreACPServices::Unserialize. Pierwsza metoda powoduje, że dane właściwości będą odczytywane na żądanie w późniejszym czasie.
Powtórz poprzednie kroki, dopóki wszystkie bloki właściwości nie zostaną niezaserializowane.
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;
}