Свойства (общие элементы)

Платформа текстовых служб (TSF) предоставляет свойства, которые связывают метаданные с диапазоном текста. К этим свойствам относятся, но не ограничиваются, отображаются такие атрибуты, как полужирный текст, идентификатор языка текста и необработанные данные, предоставляемые службой текста, например звуковые данные, связанные с текстом из службы речи.

В следующем примере показано, как можно просмотреть гипотетическое свойство цвета текста с возможными значениями красного цвета (R), зеленого (G) или синего (B).

COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

Свойства различных типов могут перекрываться. Например, выполните предыдущий пример и добавьте текстовый атрибут, который может быть полужирным (B) или курсивом (I).

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

Текст "этот" будет полужирным, "is" будет как полужирным, так и красным, "некоторые" будут отображаться обычно, "цвет" будет зеленым и курсивным, и "текст" будет курсивным.

Свойства одного типа не могут перекрываться. Например, следующая ситуация не допускается, так как "is" и "цветные" имеют перекрывающиеся значения одинаковых типов.

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

Типы свойств

TSF определяет три различных типа свойств.

Тип свойства Description
Статические Объект статического свойства сохраняет данные свойства с текстом. Он также сохраняет диапазон текстовых сведений для каждого диапазона, к которому применяется свойство. ITfReadOnlyProperty::GetType возвращает категорию GUID_TFCAT_PROPSTYLE_STATIC.
Static-Compact Объект свойства static-compact идентичен объекту статического свойства, кроме статического компактного свойства, не хранит данные диапазона. При запросе диапазона, охватываемого статическим компактным свойством, создается диапазон для каждой группы смежных свойств. Статические компактные свойства являются наиболее эффективным способом хранения свойств на основе каждого символа. ITfReadOnlyProperty::GetType возвращает категорию GUID_TFCAT_PROPSTYLE_STATICCOMPACT.
Пользовательское Объект пользовательского свойства хранит сведения о диапазоне для каждого диапазона, к которому применяется свойство. Однако он не сохраняет фактические данные для свойства. Вместо этого настраиваемое свойство сохраняет объект ITfPropertyStore. Диспетчер TSF использует этот объект для доступа к данным свойства и поддержания их. ITfReadOnlyProperty::GetType возвращает категорию GUID_TFCAT_PROPSTYLE_CUSTOM.

 

Работа со свойствами

Значение и атрибуты свойства получаются с помощью интерфейса ITfReadOnlyProperty и изменяются с помощью интерфейса ITfProperty.

Если требуется определенный тип свойства, используется ITfContext::GetProperty . ITfContext::GetProperty требует guid , определяющий свойство для получения. TSF определяет набор стандартных идентификаторов свойств, используемых или текстовая служба может определять собственные идентификаторы свойств. Если используется настраиваемое свойство, поставщик свойств должен опубликовать GUID свойства и формат полученных данных.

Например, чтобы получить CLSID для владельца диапазона текста, вызовите ITfContext::GetProperty для получения объекта свойства, вызовите ITfProperty::FindRange, чтобы получить диапазон, который полностью охватывает свойство, а затем вызовите ITfReadOnlyProperty::GetValue, чтобы получить clSID текстовой службы, которая владеет текстом. В следующем примере показана функция, которая с учетом контекста, диапазона и файла cookie редактирования получит CLSID текстовой службы, которая владеет текстом.

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

Свойства также можно перечислить, получив интерфейс IEnumTfProperties из ITfContext::EnumProperties.

Постоянное хранилище свойств

Часто свойства прозрачны для приложения и используются одним или несколькими текстовыми службами. Чтобы сохранить данные свойств, например при сохранении в файл, приложение должно сериализовать данные свойств при сохранении и отмене инициализации данных свойств при восстановлении данных. В этом случае приложение не должно быть заинтересовано в отдельных свойствах, но должно перечислять все свойства в контексте и хранить их.

При хранении данных свойств приложение должно выполнить следующие действия.

  1. Получите перечислитель свойств, вызвав ITfContext::EnumProperties.
  2. Перечислите каждое свойство путем вызова IEnumTfProperties::Next.
  3. Для каждого свойства получите перечислитель диапазона путем вызова ITfReadOnlyProperty::EnumRanges.
  4. Перечислите каждый диапазон в свойстве, вызвав IEnumTfRanges::Next.
  5. Для каждого диапазона в свойстве вызовите ITextStoreACPServices::Serialize со свойством, диапазоном, структурой TF_PERSISTENT_PROPERTY_HEADER_ACP и объектом потока, реализованным приложением.
  6. Напишите содержимое структуры TF_PERSISTENT_PROPERTY_HEADER_ACP в постоянную память.
  7. Запись содержимого объекта потока в постоянную память.
  8. Продолжайте предыдущие шаги для всех диапазонов во всех свойствах.
  9. Приложение должно записать определенный тип конца в поток, чтобы при восстановлении данных можно было определить точку остановки.
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

При восстановлении данных свойств приложение должно выполнить следующие действия.

  1. Задайте указатель потока на начало первой TF_PERSISTENT_PROPERTY_HEADER_ACP структуры.

  2. Чтение структуры TF_PERSISTENT_PROPERTY_HEADER_ACP.

  3. Вызовите ITfContext::GetProperty с элементом GUIDType структуры TF_PERSISTENT_PROPERTY_HEADER_ACP .

  4. Приложение может выполнить одно из двух действий на этом этапе.

    1. Создайте экземпляр объекта ITfPersistentPropertyLoaderACP , который приложение должно реализовать. Затем вызовите ITextStoreACPServices::Unserialize с значением NULL для pStream и указателя ITfPersistentPropertyLoaderACP .
    2. Передайте входной поток в ITextStoreACPServices::Unserialize и NULL для pLoader.

    Первый метод предпочтителен, так как он является наиболее эффективным. Реализация второго метода приводит к чтению всех данных свойств из потока во время вызова ITextStoreACPServices::Unserialize . Первый метод приводит к тому, что данные свойств будут считываться по запросу позже.

  5. Повторите предыдущие шаги, пока все блоки свойств не будут несериализированы.

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