Properti (Elemen Umum)

Text Services Framework (TSF) menyediakan properti yang mengaitkan metadata dengan rentang teks. Properti ini termasuk, tetapi tidak terbatas pada, menampilkan atribut seperti teks tebal, pengidentifikasi bahasa teks, dan data mentah yang disediakan oleh layanan teks seperti data audio yang terkait dengan teks dari layanan teks ucapan.

Contoh berikut menunjukkan bagaimana properti warna teks hipotetis dengan kemungkinan nilai merah (R), hijau (G), atau biru (B) dapat dilihat.

COLOR:      RR      GGGGGGGG
TEXT:  this is some colored text

Properti dari berbagai jenis dapat tumpang tindih. Misalnya, ambil contoh sebelumnya dan tambahkan atribut teks yang dapat ditebalkan (B) atau miring (I).

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

Teks "ini" akan ditebalkan, "adalah" akan tebal dan merah, "beberapa" akan ditampilkan secara normal, "berwarna" akan berwarna hijau dan miring dan "teks" akan miring.

Properti dengan tipe yang sama tidak dapat tumpang tindih. Misalnya, situasi berikut tidak diizinkan karena "adalah" dan "berwarna" memiliki nilai tumpang tindih dengan jenis yang sama.

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

Jenis Properti

TSF mendefinisikan tiga jenis properti yang berbeda.

Jenis properti Deskripsi
Statis Objek properti statis menyimpan data properti dengan teks. Ini juga menyimpan rentang informasi teks untuk setiap rentang yang diterapkan properti. ITfReadOnlyProperty::GetType mengembalikan kategori GUID_TFCAT_PROPSTYLE_STATIC.
Statis-Ringkas Objek properti statis-ringkas identik dengan objek properti statis kecuali properti statis-ringkas tidak menyimpan data rentang. Saat rentang yang dicakup oleh properti statis-ringkas diminta, rentang dibuat untuk setiap grup properti yang berdekatan. Properti statis-ringkas adalah cara paling efisien untuk menyimpan properti berdasarkan per karakter. ITfReadOnlyProperty::GetType mengembalikan kategori GUID_TFCAT_PROPSTYLE_STATICCOMPACT.
Adat Objek properti kustom menyimpan informasi rentang untuk setiap rentang tempat properti berlaku. Namun, tidak menyimpan data aktual untuk properti . Sebagai gantinya, properti kustom menyimpan objek ITfPropertyStore. Manajer TSF menggunakan objek ini untuk mengakses dan memelihara data properti. ITfReadOnlyProperty::GetType mengembalikan kategori GUID_TFCAT_PROPSTYLE_CUSTOM.

 

Bekerja dengan Properti

Nilai properti dan atribut diperoleh menggunakan antarmuka ITfReadOnlyProperty dan dimodifikasi menggunakan antarmuka ITfProperty .

Jika jenis properti tertentu diperlukan, maka ITfContext::GetProperty digunakan. ITfContext::GetProperty memerlukan GUID yang mengidentifikasi properti untuk diperoleh. TSF mendefinisikan sekumpulan pengidentifikasi properti yang telah ditentukan sebelumnya yang digunakan atau layanan teks dapat menentukan pengidentifikasi propertinya sendiri. Jika properti kustom digunakan, penyedia properti harus menerbitkan GUID properti dan format data yang diperoleh.

Misalnya, untuk mendapatkan CLSID bagi pemilik rentang teks, panggil ITfContext::GetProperty untuk mendapatkan objek properti, panggil ITfProperty::FindRange untuk mendapatkan rentang yang sepenuhnya mencakup properti, lalu panggil ITfReadOnlyProperty::GetValue untuk mendapatkan TfGuidAtom yang mewakili CLSID layanan teks yang memiliki teks. Contoh berikut menunjukkan fungsi yang, mengingat konteks, rentang, dan cookie edit, akan mendapatkan CLSID dari layanan teks yang memiliki teks.

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

Properti juga dapat dijumlahkan dengan mendapatkan antarmuka IEnumTfProperties dari ITfContext::EnumProperties.

Penyimpanan Properti Persisten

Seringkali, properti transparan untuk aplikasi dan digunakan oleh satu atau beberapa layanan teks. Untuk mempertahankan data properti, seperti saat menyimpan ke file, aplikasi harus membuat serial data properti saat disimpan dan membatalkan penyaringan data properti saat data dipulihkan. Dalam hal ini, aplikasi tidak boleh tertarik pada properti individual, tetapi harus menghitung semua properti dalam konteks dan menyimpannya.

Saat menyimpan data properti, aplikasi harus melakukan langkah-langkah berikut.

  1. Dapatkan enumerator properti dengan memanggil ITfContext::EnumProperties.
  2. Hitung setiap properti dengan memanggil IEnumTfProperties::Next.
  3. Untuk setiap properti, dapatkan enumerator rentang dengan memanggil ITfReadOnlyProperty::EnumRanges.
  4. Hitung setiap rentang dalam properti dengan memanggil IEnumTfRanges::Next.
  5. Untuk setiap rentang dalam properti, panggil ITextStoreACPServices::Serialize dengan properti, rentang, struktur TF_PERSISTENT_PROPERTY_HEADER_ACP , dan objek stream yang diterapkan oleh aplikasi.
  6. Tulis konten struktur TF_PERSISTENT_PROPERTY_HEADER_ACP ke dalam memori persisten.
  7. Tulis konten objek stream ke dalam memori persisten.
  8. Lanjutkan langkah-langkah sebelumnya untuk semua rentang di semua properti.
  9. Aplikasi harus menulis beberapa jenis terminator ke dalam aliran sehingga, ketika data dipulihkan, titik henti dapat diidentifikasi.
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

Saat memulihkan data properti, aplikasi harus melakukan langkah-langkah berikut.

  1. Atur penunjuk aliran ke awal struktur TF_PERSISTENT_PROPERTY_HEADER_ACP pertama.

  2. Baca struktur TF_PERSISTENT_PROPERTY_HEADER_ACP.

  3. Panggil ITfContext::GetProperty dengan anggota guidType dari struktur TF_PERSISTENT_PROPERTY_HEADER_ACP .

  4. Aplikasi ini dapat melakukan salah satu dari dua hal pada saat ini.

    1. Buat instans objek ITfPersistentPropertyLoaderACP yang harus diterapkan aplikasi. Kemudian panggil ITextStoreACPServices::Unserialize dengan NULL untuk pStream dan pointer ITfPersistentPropertyLoaderACP .
    2. Teruskan aliran input ke ITextStoreACPServices::Unserialize dan NULL untuk pLoader.

    Metode pertama lebih disukai karena ini adalah yang paling efisien. Menerapkan metode kedua menyebabkan semua data properti dibaca dari aliran selama panggilan ITextStoreACPServices::Unserialize . Metode pertama menyebabkan data properti dibaca sesuai permintaan di lain waktu.

  5. Ulangi langkah-langkah sebelumnya hingga semua blok properti tidak diserialisasi.

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