Propiedades (elementos comunes)

Text Services Framework (TSF) proporciona propiedades que asocian metadatos a un intervalo de texto. Estas propiedades incluyen, entre otras, atributos para mostrar, como texto en negrita, el identificador de idioma del texto y los datos sin procesar proporcionados por un servicio de texto, como los datos de audio asociados al texto del servicio de texto de voz.

En el ejemplo siguiente se muestra cómo se puede ver una propiedad hipotética de color de texto con posibles valores de rojo (R), verde (G) o azul (B).

COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

Las propiedades de diferentes tipos se pueden superponer. Por ejemplo, tome el ejemplo anterior y agregue un atributo de texto que pueda ser negrita (B) o cursiva (I).

ATTRIB:BBBBBBB      IIIIIIIIIIII
COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

El texto "this" sería negrita, "is" sería en negrita y rojo, "algunos" se mostrarían normalmente, "coloreado" sería verde y se cursivaría y "texto" se cursivaría.

Las propiedades del mismo tipo no se pueden superponer. Por ejemplo, no se permite la siguiente situación porque "is" y "colored" tienen valores superpuestos de los mismos tipos.

COLOR: GGG GGGG RRR BBBBGGG     
COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

Tipos de propiedades

TSF define tres tipos diferentes de propiedades.

Tipo de propiedad Descripción
estática Un objeto de propiedad estática almacena los datos de propiedad con texto. También almacena el intervalo de información de texto para cada intervalo al que se aplica la propiedad. ITfReadOnlyProperty::GetType devuelve la categoría GUID_TFCAT_PROPSTYLE_STATIC.
Static-Compact Un objeto de propiedad static-compact es idéntico a un objeto de propiedad estática, salvo que una propiedad static-compact no almacena datos de rango. Cuando se solicita el intervalo cubierto por una propiedad estática compacta, se crea un intervalo para cada grupo de propiedades adyacentes. Las propiedades compactas estáticas son la manera más eficaz de almacenar propiedades por carácter. ITfReadOnlyProperty::GetType devuelve la categoría GUID_TFCAT_PROPSTYLE_STATICCOMPACT.
Personalizado Un objeto de propiedad personalizada almacena la información de intervalo para cada intervalo al que se aplica la propiedad. Sin embargo, no almacena los datos reales de la propiedad . En su lugar, una propiedad personalizada almacena un objeto ITfPropertyStore. El administrador de TSF usa este objeto para acceder a los datos de propiedad y mantenerlos. ITfReadOnlyProperty::GetType devuelve la categoría GUID_TFCAT_PROPSTYLE_CUSTOM.

 

Trabajar con propiedades

El valor de propiedad y los atributos se obtienen mediante la interfaz ITfReadOnlyProperty y se modifican mediante la interfaz ITfProperty .

Si se requiere un tipo de propiedad específico, se usa ITfContext::GetProperty . ITfContext::GetProperty requiere un GUID que identifique la propiedad que se va a obtener. TSF define un conjunto de identificadores de propiedad predefinidos usados o un servicio de texto puede definir sus propios identificadores de propiedad. Si se usa una propiedad personalizada, el proveedor de propiedades debe publicar el GUID de propiedad y el formato de los datos obtenidos.

Por ejemplo, para obtener el CLSID para el propietario de un intervalo de texto, llame a ITfContext::GetProperty para obtener el objeto de propiedad, llame a ITfProperty::FindRange para obtener el intervalo que cubre completamente la propiedad y, a continuación, llame a ITfReadOnlyProperty::GetValue para obtener un TfGuidAtom que represente el CLSID del servicio de texto que posee el texto. En el ejemplo siguiente se muestra una función que, dada un contexto, un intervalo y una cookie de edición, obtendrá el CLSID del servicio de texto que posee el 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;
}

Las propiedades también se pueden enumerar obteniendo una interfaz IEnumTfProperties de ITfContext::EnumProperties.

Almacenamiento persistente de propiedades

A menudo, las propiedades son transparentes para una aplicación y las usan uno o varios servicios de texto. Para conservar los datos de propiedad, como al guardar en un archivo, una aplicación debe serializar los datos de propiedad cuando se almacenan y deserializan los datos de propiedad cuando se restauran. En este caso, la aplicación no debe estar interesada en propiedades individuales, pero debe enumerar todas las propiedades en el contexto y almacenarlas.

Al almacenar datos de propiedad, una aplicación debe realizar los pasos siguientes.

  1. Obtenga un enumerador de propiedades llamando a ITfContext::EnumProperties.
  2. Para enumerar cada propiedad, llame a IEnumTfProperties::Next.
  3. Para cada propiedad, obtenga un enumerador de rango llamando a ITfReadOnlyProperty::EnumRanges.
  4. Para enumerar cada intervalo de la propiedad, llame a IEnumTfRanges::Next.
  5. Para cada intervalo de la propiedad, llame a ITextStoreACPServices::Serialize con la propiedad, el intervalo, una estructura de TF_PERSISTENT_PROPERTY_HEADER_ACP y un objeto stream implementado por la aplicación.
  6. Escriba el contenido de la estructura TF_PERSISTENT_PROPERTY_HEADER_ACP en memoria persistente.
  7. Escriba el contenido del objeto de secuencia en memoria persistente.
  8. Continúe con los pasos anteriores para todos los intervalos de todas las propiedades.
  9. La aplicación debe escribir algún tipo de terminador en la secuencia para que, cuando se restauren los datos, se pueda identificar un punto de detención.
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

Al restaurar los datos de propiedad, una aplicación debe realizar los pasos siguientes.

  1. Establezca el puntero de flujo en el inicio de la primera estructura TF_PERSISTENT_PROPERTY_HEADER_ACP .

  2. Lea la estructura TF_PERSISTENT_PROPERTY_HEADER_ACP .

  3. Llame a ITfContext::GetProperty con el miembro guidType de la estructura TF_PERSISTENT_PROPERTY_HEADER_ACP .

  4. La aplicación puede hacer una de estas dos cosas en este momento.

    1. Cree una instancia de un objeto ITfPersistentPropertyLoaderACP que la aplicación debe implementar. A continuación, llame a ITextStoreACPServices::Unserialize con NULL para pStream y el puntero ITfPersistentPropertyLoaderACP .
    2. Pase el flujo de entrada a ITextStoreACPServices::Unserialize y NULL para pLoader.

    Se prefiere el primer método, ya que es el más eficaz. La implementación del segundo método hace que todos los datos de propiedad se lean desde la secuencia durante la llamada ACPServices::Unserialize de ITextStoreACPServices.:Unserialize . El primer método hace que los datos de propiedad se lean a petición más adelante.

  5. Repita los pasos anteriores hasta que todos los bloques de propiedades se deserialicen.

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;
}