Properties (Common Elements)
Text Services Framework (TSF) fournit des propriétés qui associent des métadonnées à une plage de texte. Ces propriétés incluent, mais pas seulement, l’affichage d’attributs tels que le texte en gras, l’identificateur de langue du texte et les données brutes fournies par un service de texte, tels que les données audio associées au texte du service de texte vocal.
L’exemple suivant montre comment une propriété de couleur de texte hypothétique avec les valeurs possibles de rouge (R), de vert (G) ou de bleu (B) peut être consultée.
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Les propriétés de différents types peuvent se chevaucher. Par exemple, prenez l’exemple précédent et ajoutez un attribut de texte qui peut être en gras (B) ou italique (I).
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Le texte « this » serait gras, « is » serait à la fois gras et rouge, « some » serait affiché normalement, « colored » serait vert et italique et « text » serait italique.
Les propriétés du même type ne peuvent pas se chevaucher. Par exemple, la situation suivante n’est pas autorisée, car « is » et « colored » ont des valeurs qui se chevauchent des mêmes types.
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
Types de propriétés
TSF définit trois types de propriétés différents.
Type de propriété | Description |
---|---|
statique | Un objet de propriété statique stocke les données de propriété avec du texte. Il stocke également la plage d’informations de texte pour chaque plage à laquelle la propriété s’applique. ITfReadOnlyProperty ::GetType retourne la catégorie GUID_TFCAT_PROPSTYLE_STATIC. |
Static-Compact | Un objet de propriété statique compact est identique à un objet de propriété statique, sauf qu’une propriété statique compacte ne stocke pas les données de plage. Lorsque la plage couverte par une propriété statique compacte est demandée, une plage est créée pour chaque groupe de propriétés adjacentes. Les propriétés statiques compactes constituent le moyen le plus efficace de stocker les propriétés par caractère. ITfReadOnlyProperty ::GetType retourne la catégorie GUID_TFCAT_PROPSTYLE_STATICCOMPACT. |
Personnalisée | Un objet de propriété personnalisé stocke les informations de plage pour chaque plage à laquelle la propriété s’applique. Toutefois, elle ne stocke pas les données réelles de la propriété. Au lieu de cela, une propriété personnalisée stocke un objet ITfPropertyStore. Le gestionnaire TSF utilise cet objet pour accéder aux données de propriété et les conserver. ITfReadOnlyProperty ::GetType retourne la catégorie GUID_TFCAT_PROPSTYLE_CUSTOM. |
Utilisation des propriétés
La valeur et les attributs de propriété sont obtenus à l’aide de l’interface ITfReadOnlyProperty et modifiées à l’aide de l’interface ITfProperty .
Si un type de propriété spécifique est requis, ITfContext ::GetProperty est utilisé. ITfContext ::GetProperty nécessite un GUID qui identifie la propriété à obtenir. TSF définit un ensemble d’identificateurs de propriété prédéfinis utilisés ou un service de texte peut définir ses propres identificateurs de propriété. Si une propriété personnalisée est utilisée, le fournisseur de propriétés doit publier le GUID de propriété et le format des données obtenues.
Par exemple, pour obtenir le CLSID pour le propriétaire d’une plage de texte, appelez ITfContext ::GetProperty pour obtenir l’objet de propriété, appelez ITfProperty ::FindRange pour obtenir la plage qui couvre entièrement la propriété, puis appelez ITfReadOnlyProperty ::GetValue pour obtenir un TfGuidAtom qui représente le CLSID du service de texte propriétaire du texte. L’exemple suivant montre une fonction qui, en fonction d’un contexte, d’une plage et d’un cookie d’édition, obtient le CLSID du service de texte propriétaire du texte.
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;
}
Les propriétés peuvent également être énumérées en obtenant une interface IEnumTfProperties à partir d’ITfContext ::EnumProperties.
Stockage persistant des propriétés
Souvent, les propriétés sont transparentes pour une application et sont utilisées par un ou plusieurs services de texte. Pour conserver les données de propriété, telles que lors de l’enregistrement dans un fichier, une application doit sérialiser les données de propriété lorsqu’elles sont stockées et annuler la restauration des données de propriété. Dans ce cas, l’application ne doit pas s’intéresser à des propriétés individuelles, mais doit énumérer toutes les propriétés dans le contexte et les stocker.
Lors du stockage des données de propriété, une application doit effectuer les étapes suivantes.
- Obtenez un énumérateur de propriété en appelant ITfContext ::EnumProperties.
- Énumérez chaque propriété en appelant IEnumTfProperties ::Next.
- Pour chaque propriété, obtenez un énumérateur de plage en appelant ITfReadOnlyProperty ::EnumRanges.
- Énumérez chaque plage de la propriété en appelant IEnumTfRanges ::Next.
- Pour chaque plage de la propriété, appelez ITextStoreACPServices ::Serialize avec la propriété, la plage, une structure TF_PERSISTENT_PROPERTY_HEADER_ACP et un objet de flux implémenté par l’application.
- Écrivez le contenu de la structure TF_PERSISTENT_PROPERTY_HEADER_ACP en mémoire persistante.
- Écrivez le contenu de l’objet de flux en mémoire persistante.
- Poursuivez les étapes précédentes pour toutes les plages de toutes les propriétés.
- L’application doit écrire un type de terminateur dans le flux de sorte que, lorsque les données sont restaurées, un point d’arrêt peut être identifié.
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
Lors de la restauration des données de propriété, une application doit effectuer les étapes suivantes.
Définissez le pointeur de flux au début de la première structure TF_PERSISTENT_PROPERTY_HEADER_ACP .
Lisez la structure TF_PERSISTENT_PROPERTY_HEADER_ACP .
Appelez ITfContext ::GetProperty avec le membre guidType de la structure TF_PERSISTENT_PROPERTY_HEADER_ACP .
L’application peut effectuer l’une des deux opérations à ce stade.
- Créez une instance d’un objet ITfPersistentPropertyLoaderACP que l’application doit implémenter. Appelez ensuite ITextStoreACPServices ::Unserialize avec NULL pour pStream et le pointeur ITfPersistentPropertyLoaderACP .
- Transmettez le flux d’entrée à ITextStoreACPServices ::Unserialize et NULL pour pLoader.
La première méthode est préférée, car elle est la plus efficace. L’implémentation de la deuxième méthode entraîne la lecture de toutes les données de propriété à partir du flux pendant l’appel ITextStoreACPServices ::Unserialize . La première méthode entraîne la lecture des données de propriété à la demande ultérieurement.
Répétez les étapes précédentes jusqu’à ce que tous les blocs de propriétés ne soient pas réérialisés.
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;
}