/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include "editeng/fieldupdater.hxx" #include "editeng/macros.hxx" #include "editeng/section.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "svl/sharedstringpool.hxx" #include #include #include #if DEBUG_EDIT_ENGINE #include using std::cout; using std::endl; #endif using namespace com::sun::star; XEditAttribute* MakeXEditAttribute( SfxItemPool& rPool, const SfxPoolItem& rItem, sal_uInt16 nStart, sal_uInt16 nEnd ) { // Create thw new attribute in the pool const SfxPoolItem& rNew = rPool.Put( rItem ); XEditAttribute* pNew = new XEditAttribute( rNew, nStart, nEnd ); return pNew; } XEditAttribute::XEditAttribute( const SfxPoolItem& rAttr, sal_uInt16 nS, sal_uInt16 nE ) { pItem = &rAttr; nStart = nS; nEnd = nE; } XEditAttribute::~XEditAttribute() { pItem = 0; // belongs to the Pool. } bool XEditAttribute::IsFeature() const { sal_uInt16 nWhich = pItem->Which(); return ((nWhich >= EE_FEATURE_START) && (nWhich <= EE_FEATURE_END)); } void XEditAttribute::SetItem(const SfxPoolItem& rNew) { pItem = &rNew; } XParaPortionList::XParaPortionList( OutputDevice* pRefDev, sal_uLong nPW, sal_uInt16 _nStretchX, sal_uInt16 _nStretchY) : aRefMapMode(pRefDev->GetMapMode()), nStretchX(_nStretchX), nStretchY(_nStretchY) { nRefDevPtr = reinterpret_cast(pRefDev); nPaperWidth = nPW; eRefDevType = pRefDev->GetOutDevType(); } void XParaPortionList::push_back(XParaPortion* p) { maList.push_back(p); } const XParaPortion& XParaPortionList::operator [](size_t i) const { return maList[i]; } ContentInfo::ContentInfo( SfxItemPool& rPool ) : eFamily(SFX_STYLE_FAMILY_PARA), aParaAttribs(rPool, EE_PARA_START, EE_CHAR_END) { } // the real Copy constructor is nonsens, since I have to work with another Pool! ContentInfo::ContentInfo( const ContentInfo& rCopyFrom, SfxItemPool& rPoolToUse ) : maText(rCopyFrom.maText), aStyle(rCopyFrom.aStyle), eFamily(rCopyFrom.eFamily), aParaAttribs(rPoolToUse, EE_PARA_START, EE_CHAR_END) { // this should ensure that the Items end up in the correct Pool! aParaAttribs.Set( rCopyFrom.GetParaAttribs() ); for (size_t i = 0; i < rCopyFrom.aAttribs.size(); ++i) { const XEditAttribute& rAttr = rCopyFrom.aAttribs[i]; XEditAttribute* pMyAttr = MakeXEditAttribute( rPoolToUse, *rAttr.GetItem(), rAttr.GetStart(), rAttr.GetEnd()); aAttribs.push_back(pMyAttr); } if ( rCopyFrom.GetWrongList() ) mpWrongs.reset(rCopyFrom.GetWrongList()->Clone()); } ContentInfo::~ContentInfo() { XEditAttributesType::iterator it = aAttribs.begin(), itEnd = aAttribs.end(); for (; it != itEnd; ++it) aParaAttribs.GetPool()->Remove(*it->GetItem()); aAttribs.clear(); } void ContentInfo::NormalizeString( svl::SharedStringPool& rPool ) { maText = rPool.intern(OUString(maText.getData())); } OUString ContentInfo::GetText() const { rtl_uString* p = const_cast(maText.getData()); return OUString(p); } void ContentInfo::SetText( const OUString& rStr ) { maText = svl::SharedString(rStr.pData, NULL); } const WrongList* ContentInfo::GetWrongList() const { return mpWrongs.get(); } void ContentInfo::SetWrongList( WrongList* p ) { mpWrongs.reset(p); } // #i102062# bool ContentInfo::isWrongListEqual(const ContentInfo& rCompare) const { if(GetWrongList() == rCompare.GetWrongList()) return true; if(!GetWrongList() || !rCompare.GetWrongList()) return false; return (*GetWrongList() == *rCompare.GetWrongList()); } #if DEBUG_EDIT_ENGINE void ContentInfo::Dump() const { cout << "--" << endl; cout << "text: '" << OUString(const_cast(maText.getData())) << "'" << endl; cout << "style: '" << aStyle << "'" << endl; XEditAttributesType::const_iterator it = aAttribs.begin(), itEnd = aAttribs.end(); for (; it != itEnd; ++it) { const XEditAttribute& rAttr = *it; cout << "attribute: " << endl; cout << " span: [begin=" << rAttr.GetStart() << ", end=" << rAttr.GetEnd() << "]" << endl; cout << " feature: " << (rAttr.IsFeature() ? "yes":"no") << endl; } } #endif bool ContentInfo::operator==( const ContentInfo& rCompare ) const { if( (maText == rCompare.maText) && (aStyle == rCompare.aStyle ) && (aAttribs.size() == rCompare.aAttribs.size()) && (eFamily == rCompare.eFamily ) && (aParaAttribs == rCompare.aParaAttribs ) ) { for (size_t i = 0, n = aAttribs.size(); i < n; ++i) { if (aAttribs[i] != rCompare.aAttribs[i]) return false; } return true; } return false; } bool ContentInfo::operator!=(const ContentInfo& rCompare) const { return !operator==(rCompare); } EditTextObject::EditTextObject( SfxItemPool* pPool ) : mpImpl(new EditTextObjectImpl(this, pPool)) { } EditTextObject::EditTextObject( const EditTextObject& r ) : SfxItemPoolUser(), mpImpl(new EditTextObjectImpl(this, *r.mpImpl)) { } EditTextObject::~EditTextObject() { delete mpImpl; } sal_Int32 EditTextObject::GetParagraphCount() const { return mpImpl->GetParagraphCount(); } OUString EditTextObject::GetText(sal_Int32 nPara) const { return mpImpl->GetText(nPara); } void EditTextObject::ClearPortionInfo() { mpImpl->ClearPortionInfo(); } bool EditTextObject::HasOnlineSpellErrors() const { return mpImpl->HasOnlineSpellErrors(); } void EditTextObject::GetCharAttribs( sal_Int32 nPara, std::vector& rLst ) const { mpImpl->GetCharAttribs(nPara, rLst); } bool EditTextObject::IsFieldObject() const { return mpImpl->IsFieldObject(); } const SvxFieldItem* EditTextObject::GetField() const { return mpImpl->GetField(); } const SvxFieldData* EditTextObject::GetFieldData(sal_Int32 nPara, size_t nPos, sal_Int32 nType) const { return mpImpl->GetFieldData(nPara, nPos, nType); } bool EditTextObject::HasField( sal_Int32 nType ) const { return mpImpl->HasField(nType); } const SfxItemSet& EditTextObject::GetParaAttribs(sal_Int32 nPara) const { return mpImpl->GetParaAttribs(nPara); } bool EditTextObject::RemoveCharAttribs( sal_uInt16 nWhich ) { return mpImpl->RemoveCharAttribs(nWhich); } void EditTextObject::GetAllSections( std::vector& rAttrs ) const { mpImpl->GetAllSections(rAttrs); } void EditTextObject::GetStyleSheet(sal_Int32 nPara, OUString& rName, SfxStyleFamily& eFamily) const { mpImpl->GetStyleSheet(nPara, rName, eFamily); } void EditTextObject::SetStyleSheet(sal_Int32 nPara, const OUString& rName, const SfxStyleFamily& eFamily) { mpImpl->SetStyleSheet(nPara, rName, eFamily); } bool EditTextObject::ChangeStyleSheets( const OUString& rOldName, SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily) { return mpImpl->ChangeStyleSheets(rOldName, eOldFamily, rNewName, eNewFamily); } void EditTextObject::ChangeStyleSheetName( SfxStyleFamily eFamily, const OUString& rOldName, const OUString& rNewName) { mpImpl->ChangeStyleSheetName(eFamily, rOldName, rNewName); } editeng::FieldUpdater EditTextObject::GetFieldUpdater() { return mpImpl->GetFieldUpdater(); } void EditTextObject::NormalizeString( svl::SharedStringPool& rPool ) { mpImpl->NormalizeString(rPool); } std::vector EditTextObject::GetSharedStrings() const { return mpImpl->GetSharedStrings(); } const SfxItemPool* EditTextObject::GetPool() const { return mpImpl->GetPool(); } sal_uInt16 EditTextObject::GetUserType() const { return mpImpl->GetUserType(); } void EditTextObject::SetUserType( sal_uInt16 n ) { mpImpl->SetUserType(n); } bool EditTextObject::IsVertical() const { return mpImpl->IsVertical(); } void EditTextObject::SetVertical( bool bVertical ) { return mpImpl->SetVertical(bVertical); } SvtScriptType EditTextObject::GetScriptType() const { return mpImpl->GetScriptType(); } bool EditTextObject::Store( SvStream& rOStream ) const { if ( rOStream.GetError() ) return false; sal_Size nStartPos = rOStream.Tell(); sal_uInt16 nWhich = static_cast(EE_FORMAT_BIN); rOStream.WriteUInt16( nWhich ); sal_uInt32 nStructSz = 0; rOStream.WriteUInt32( nStructSz ); StoreData( rOStream ); sal_Size nEndPos = rOStream.Tell(); nStructSz = nEndPos - nStartPos - sizeof( nWhich ) - sizeof( nStructSz ); rOStream.Seek( nStartPos + sizeof( nWhich ) ); rOStream.WriteUInt32( nStructSz ); rOStream.Seek( nEndPos ); return rOStream.GetError() == 0; } EditTextObject* EditTextObject::Create( SvStream& rIStream, SfxItemPool* pGlobalTextObjectPool ) { sal_Size nStartPos = rIStream.Tell(); // First check what type of Object... sal_uInt16 nWhich; rIStream.ReadUInt16( nWhich ); sal_uInt32 nStructSz; rIStream.ReadUInt32( nStructSz ); if (nWhich != EE_FORMAT_BIN) { // Unknown object we no longer support. rIStream.SetError(EE_READWRITE_WRONGFORMAT); return NULL; } if ( rIStream.GetError() ) return NULL; EditTextObject* pTxtObj = new EditTextObject(pGlobalTextObjectPool);; pTxtObj->CreateData(rIStream); // Make sure that the stream is left at the correct place. sal_Size nFullSz = sizeof( nWhich ) + sizeof( nStructSz ) + nStructSz; rIStream.Seek( nStartPos + nFullSz ); return pTxtObj; } void EditTextObject::StoreData( SvStream& rStrm ) const { mpImpl->StoreData(rStrm); } void EditTextObject::CreateData( SvStream& rStrm ) { mpImpl->CreateData(rStrm); } EditTextObject* EditTextObject::Clone() const { return new EditTextObject(*this); } bool EditTextObject::operator==( const EditTextObject& rCompare ) const { return mpImpl->operator==(*rCompare.mpImpl); } // #i102062# bool EditTextObject::isWrongListEqual(const EditTextObject& rCompare) const { return mpImpl->isWrongListEqual(*rCompare.mpImpl); } void EditTextObject::ObjectInDestruction(const SfxItemPool& rSfxItemPool) { mpImpl->ObjectInDestruction(rSfxItemPool); } #if DEBUG_EDIT_ENGINE void EditTextObject::Dump() const { mpImpl->Dump(); } #endif void EditTextObject::dumpAsXml(xmlTextWriterPtr pWriter) const { xmlTextWriterStartElement(pWriter, BAD_CAST("editTextObject")); sal_Int32 nCount = GetParagraphCount(); for (sal_Int32 i = 0; i < nCount; ++i) { xmlTextWriterStartElement(pWriter, BAD_CAST("paragraph")); xmlTextWriterWriteString(pWriter, BAD_CAST(GetText(i).toUtf8().getStr())); xmlTextWriterEndElement(pWriter); } xmlTextWriterEndElement(pWriter); } // from SfxItemPoolUser void EditTextObjectImpl::ObjectInDestruction(const SfxItemPool& rSfxItemPool) { if(!bOwnerOfPool && pPool && pPool == &rSfxItemPool) { // The pool we are based on gets destructed; get owner of pool by creating own one. // No need to call RemoveSfxItemPoolUser(), this is done from the pool's destructor // Base new pool on EditEnginePool; it would also be possible to clone the used // pool if needed, but only text attributes should be used. SfxItemPool* pNewPool = EditEngine::CreatePool(); if(pPool) { pNewPool->SetDefaultMetric(pPool->GetMetric(DEF_METRIC)); } ContentInfosType aReplaced; aReplaced.reserve(aContents.size()); ContentInfosType::const_iterator it = aContents.begin(), itEnd = aContents.end(); for (; it != itEnd; ++it) aReplaced.push_back(new ContentInfo(*it, *pNewPool)); aReplaced.swap(aContents); // set local variables pPool = pNewPool; bOwnerOfPool = true; } } #if DEBUG_EDIT_ENGINE void EditTextObjectImpl::Dump() const { ContentInfosType::const_iterator it = aContents.begin(), itEnd = aContents.end(); for (; it != itEnd; ++it) it->Dump(); } #endif EditEngineItemPool* getEditEngineItemPool(SfxItemPool* pPool) { EditEngineItemPool* pRetval = dynamic_cast< EditEngineItemPool* >(pPool); while(!pRetval && pPool && pPool->GetSecondaryPool()) { pPool = pPool->GetSecondaryPool(); if(pPool) { pRetval = dynamic_cast< EditEngineItemPool* >(pPool); } } return pRetval; } EditTextObjectImpl::EditTextObjectImpl( EditTextObject* pFront, SfxItemPool* pP ) : mpFront(pFront) { nVersion = 0; nMetric = 0xFFFF; nUserType = 0; nObjSettings = 0; pPortionInfo = 0; // #i101239# ensure target is a EditEngineItemPool, else // fallback to pool ownership. This is needed to ensure that at // pool destruction time of an alien pool, the pool is still alive. // When registering would happen at an alien pool which just uses an // EditEngineItemPool as some sub-pool, that pool could already // be decoupled and deleted which would lead to crashes. pPool = getEditEngineItemPool(pP); if ( pPool ) { bOwnerOfPool = false; } else { pPool = EditEngine::CreatePool(); bOwnerOfPool = true; } if(!bOwnerOfPool && pPool) { // it is sure now that the pool is an EditEngineItemPool pPool->AddSfxItemPoolUser(*mpFront); } bVertical = false; bStoreUnicodeStrings = false; nScriptType = SvtScriptType::NONE; } EditTextObjectImpl::EditTextObjectImpl( EditTextObject* pFront, const EditTextObjectImpl& r ) : mpFront(pFront) { nVersion = r.nVersion; nMetric = r.nMetric; nUserType = r.nUserType; nObjSettings = r.nObjSettings; bVertical = r.bVertical; nScriptType = r.nScriptType; pPortionInfo = NULL; // Do not copy PortionInfo bStoreUnicodeStrings = false; if ( !r.bOwnerOfPool ) { // reuse alien pool; this must be a EditEngineItemPool // since there is no other way to construct a BinTextObject // than it's regular constructor where that is ensured pPool = r.pPool; bOwnerOfPool = false; } else { pPool = EditEngine::CreatePool(); bOwnerOfPool = true; } if (!bOwnerOfPool) { // it is sure now that the pool is an EditEngineItemPool pPool->AddSfxItemPoolUser(*mpFront); } if (bOwnerOfPool && r.pPool) pPool->SetDefaultMetric( r.pPool->GetMetric( DEF_METRIC ) ); aContents.reserve(r.aContents.size()); ContentInfosType::const_iterator it = r.aContents.begin(), itEnd = r.aContents.end(); for (; it != itEnd; ++it) aContents.push_back(new ContentInfo(*it, *pPool)); } EditTextObjectImpl::~EditTextObjectImpl() { if(!bOwnerOfPool && pPool) { pPool->RemoveSfxItemPoolUser(*mpFront); } ClearPortionInfo(); // Remove contents before deleting the pool instance since each content // has to access the pool instance in its destructor. aContents.clear(); if ( bOwnerOfPool ) { SfxItemPool::Free(pPool); } } void EditTextObjectImpl::SetUserType( sal_uInt16 n ) { nUserType = n; } void EditTextObjectImpl::NormalizeString( svl::SharedStringPool& rPool ) { ContentInfosType::iterator it = aContents.begin(), itEnd = aContents.end(); for (; it != itEnd; ++it) { ContentInfo& rInfo = *it; rInfo.NormalizeString(rPool); } } std::vector EditTextObjectImpl::GetSharedStrings() const { std::vector aSSs; aSSs.reserve(aContents.size()); ContentInfosType::const_iterator it = aContents.begin(), itEnd = aContents.end(); for (; it != itEnd; ++it) { const ContentInfo& rInfo = *it; aSSs.push_back(rInfo.GetSharedString()); } return aSSs; } void EditTextObjectImpl::SetVertical( bool b ) { if ( b != bVertical ) { bVertical = b; ClearPortionInfo(); } } void EditTextObjectImpl::SetScriptType( SvtScriptType nType ) { nScriptType = nType; } XEditAttribute* EditTextObjectImpl::CreateAttrib( const SfxPoolItem& rItem, sal_uInt16 nStart, sal_uInt16 nEnd ) { return MakeXEditAttribute( *pPool, rItem, nStart, nEnd ); } void EditTextObjectImpl::DestroyAttrib( XEditAttribute* pAttr ) { pPool->Remove( *pAttr->GetItem() ); delete pAttr; } ContentInfo* EditTextObjectImpl::CreateAndInsertContent() { aContents.push_back(new ContentInfo(*pPool)); return &aContents.back(); } sal_Int32 EditTextObjectImpl::GetParagraphCount() const { size_t nSize = aContents.size(); if (nSize > EE_PARA_MAX_COUNT) { SAL_WARN( "editeng", "EditTextObjectImpl::GetParagraphCount - overflow " << nSize); return EE_PARA_MAX_COUNT; } return static_cast(nSize); } OUString EditTextObjectImpl::GetText(sal_Int32 nPara) const { if (nPara < 0 || static_cast(nPara) >= aContents.size()) return OUString(); return aContents[nPara].GetText(); } void EditTextObjectImpl::ClearPortionInfo() { if ( pPortionInfo ) { delete pPortionInfo; pPortionInfo = NULL; } } bool EditTextObjectImpl::HasOnlineSpellErrors() const { ContentInfosType::const_iterator it = aContents.begin(), itEnd = aContents.end(); for (; it != itEnd; ++it) { if ( it->GetWrongList() && !it->GetWrongList()->empty() ) return true; } return false; } void EditTextObjectImpl::GetCharAttribs( sal_Int32 nPara, std::vector& rLst ) const { if (nPara < 0 || static_cast(nPara) >= aContents.size()) return; rLst.clear(); const ContentInfo& rC = aContents[nPara]; for (size_t nAttr = 0; nAttr < rC.aAttribs.size(); ++nAttr) { const XEditAttribute& rAttr = rC.aAttribs[nAttr]; EECharAttrib aEEAttr; aEEAttr.pAttr = rAttr.GetItem(); aEEAttr.nPara = nPara; aEEAttr.nStart = rAttr.GetStart(); aEEAttr.nEnd = rAttr.GetEnd(); rLst.push_back(aEEAttr); } } bool EditTextObjectImpl::IsFieldObject() const { return GetField() != nullptr; } const SvxFieldItem* EditTextObjectImpl::GetField() const { if (aContents.size() == 1) { const ContentInfo& rC = aContents[0]; if (rC.GetText().getLength() == 1) { size_t nAttribs = rC.aAttribs.size(); for (size_t nAttr = nAttribs; nAttr; ) { const XEditAttribute& rX = rC.aAttribs[--nAttr]; if (rX.GetItem()->Which() == EE_FEATURE_FIELD) return static_cast(rX.GetItem()); } } } return 0; } const SvxFieldData* EditTextObjectImpl::GetFieldData(sal_Int32 nPara, size_t nPos, sal_Int32 nType) const { if (nPara < 0 || static_cast(nPara) >= aContents.size()) return NULL; const ContentInfo& rC = aContents[nPara]; if (nPos >= rC.aAttribs.size()) // URL position is out-of-bound. return NULL; ContentInfo::XEditAttributesType::const_iterator it = rC.aAttribs.begin(), itEnd = rC.aAttribs.end(); size_t nCurPos = 0; for (; it != itEnd; ++it) { const XEditAttribute& rAttr = *it; if (rAttr.GetItem()->Which() != EE_FEATURE_FIELD) // Skip attributes that are not fields. continue; const SvxFieldItem* pField = static_cast(rAttr.GetItem()); const SvxFieldData* pFldData = pField->GetField(); if (nType != text::textfield::Type::UNSPECIFIED && nType != pFldData->GetClassId()) // Field type doesn't match. Skip it. UNSPECIFIED matches all field types. continue; if (nCurPos == nPos) // Found it! return pFldData; ++nCurPos; } return NULL; // field not found. } bool EditTextObjectImpl::HasField( sal_Int32 nType ) const { size_t nParagraphs = aContents.size(); for (size_t nPara = 0; nPara < nParagraphs; ++nPara) { const ContentInfo& rC = aContents[nPara]; size_t nAttrs = rC.aAttribs.size(); for (size_t nAttr = 0; nAttr < nAttrs; ++nAttr) { const XEditAttribute& rAttr = rC.aAttribs[nAttr]; if (rAttr.GetItem()->Which() != EE_FEATURE_FIELD) continue; if (nType == text::textfield::Type::UNSPECIFIED) // Match any field type. return true; const SvxFieldData* pFldData = static_cast(rAttr.GetItem())->GetField(); if (pFldData && pFldData->GetClassId() == nType) return true; } } return false; } const SfxItemSet& EditTextObjectImpl::GetParaAttribs(sal_Int32 nPara) const { const ContentInfo& rC = aContents[nPara]; return rC.GetParaAttribs(); } bool EditTextObjectImpl::RemoveCharAttribs( sal_uInt16 _nWhich ) { bool bChanged = false; for ( size_t nPara = aContents.size(); nPara; ) { ContentInfo& rC = aContents[--nPara]; for (size_t nAttr = rC.aAttribs.size(); nAttr; ) { XEditAttribute& rAttr = rC.aAttribs[--nAttr]; if ( !_nWhich || (rAttr.GetItem()->Which() == _nWhich) ) { pPool->Remove(*rAttr.GetItem()); rC.aAttribs.erase(rC.aAttribs.begin()+nAttr); bChanged = true; } } } if ( bChanged ) ClearPortionInfo(); return bChanged; } namespace { class FindByParagraph : std::unary_function { sal_Int32 mnPara; public: FindByParagraph(sal_Int32 nPara) : mnPara(nPara) {} bool operator() (const editeng::Section& rAttr) const { return rAttr.mnParagraph == mnPara; } }; class FindBySectionStart : std::unary_function { sal_Int32 mnPara; sal_Int32 mnStart; public: FindBySectionStart(sal_Int32 nPara, sal_Int32 nStart) : mnPara(nPara), mnStart(nStart) {} bool operator() (const editeng::Section& rAttr) const { return rAttr.mnParagraph == mnPara && rAttr.mnStart == mnStart; } }; } void EditTextObjectImpl::GetAllSections( std::vector& rAttrs ) const { typedef std::vector SectionBordersType; typedef std::vector ParagraphsType; ParagraphsType aParaBorders(aContents.size()); // First pass: determine section borders for each paragraph. for (size_t nPara = 0; nPara < aContents.size(); ++nPara) { const ContentInfo& rC = aContents[nPara]; SectionBordersType& rBorders = aParaBorders[nPara]; rBorders.push_back(0); rBorders.push_back(rC.GetText().getLength()); for (size_t nAttr = 0; nAttr < rC.aAttribs.size(); ++nAttr) { const XEditAttribute& rAttr = rC.aAttribs[nAttr]; const SfxPoolItem* pItem = rAttr.GetItem(); if (!pItem) continue; rBorders.push_back(rAttr.GetStart()); rBorders.push_back(rAttr.GetEnd()); } } // Sort and remove duplicates for each paragraph. ParagraphsType::iterator it = aParaBorders.begin(), itEnd = aParaBorders.end(); for (; it != itEnd; ++it) { SectionBordersType& rBorders = *it; std::sort(rBorders.begin(), rBorders.end()); SectionBordersType::iterator itUniqueEnd = std::unique(rBorders.begin(), rBorders.end()); rBorders.erase(itUniqueEnd, rBorders.end()); } std::vector aAttrs; // Create storage for each section. Note that this creates storage even // for unformatted sections. The entries are sorted first by paragraph, // then by section positions. They don't overlap with each other. it = aParaBorders.begin(); for (; it != itEnd; ++it) { size_t nPara = distance(aParaBorders.begin(), it); const SectionBordersType& rBorders = *it; if (rBorders.size() == 1 && rBorders[0] == 0) { // Empty paragraph. Push an empty section. aAttrs.push_back(editeng::Section(nPara, 0, 0)); continue; } SectionBordersType::const_iterator itBorder = rBorders.begin(), itBorderEnd = rBorders.end(); size_t nPrev = *itBorder; size_t nCur; for (++itBorder; itBorder != itBorderEnd; ++itBorder, nPrev = nCur) { nCur = *itBorder; aAttrs.push_back(editeng::Section(nPara, nPrev, nCur)); } } if (aAttrs.empty()) return; // Go through all formatted paragraphs, and store format items. std::vector::iterator itAttr = aAttrs.begin(); for (sal_Int32 nPara = 0; nPara < (sal_Int32)aContents.size(); ++nPara) { const ContentInfo& rC = aContents[nPara]; itAttr = std::find_if(itAttr, aAttrs.end(), FindByParagraph(nPara)); if (itAttr == aAttrs.end()) { // This should never happen. There is a logic error somewhere... assert(false); return; } for (size_t i = 0; i < rC.aAttribs.size(); ++i) { const XEditAttribute& rXAttr = rC.aAttribs[i]; const SfxPoolItem* pItem = rXAttr.GetItem(); if (!pItem) continue; sal_Int32 nStart = rXAttr.GetStart(), nEnd = rXAttr.GetEnd(); std::vector::iterator itCurAttr = itAttr; // Find the container whose start position matches. itCurAttr = std::find_if(itCurAttr, aAttrs.end(), FindBySectionStart(nPara, nStart)); if (itCurAttr == aAttrs.end()) { // This should never happen. There is a logic error somewhere... assert(false); return; } for (; itCurAttr != aAttrs.end() && itCurAttr->mnParagraph == nPara && itCurAttr->mnEnd <= nEnd; ++itCurAttr) { editeng::Section& rSecAttr = *itCurAttr; // serious bug: will cause duplicate attributes to be exported auto iter(std::find_if( rSecAttr.maAttributes.begin(), rSecAttr.maAttributes.end(), [&pItem](SfxPoolItem const*const pIt) { return pIt->Which() == pItem->Which(); })); if (rSecAttr.maAttributes.end() == iter) { rSecAttr.maAttributes.push_back(pItem); } else { SAL_WARN("editeng", "GetAllSections(): duplicate attribute suppressed"); } } } } rAttrs.swap(aAttrs); } void EditTextObjectImpl::GetStyleSheet(sal_Int32 nPara, OUString& rName, SfxStyleFamily& rFamily) const { if (nPara < 0 || static_cast(nPara) >= aContents.size()) return; const ContentInfo& rC = aContents[nPara]; rName = rC.GetStyle(); rFamily = rC.GetFamily(); } void EditTextObjectImpl::SetStyleSheet(sal_Int32 nPara, const OUString& rName, const SfxStyleFamily& rFamily) { if (nPara < 0 || static_cast(nPara) >= aContents.size()) return; ContentInfo& rC = aContents[nPara]; rC.GetStyle() = rName; rC.GetFamily() = rFamily; } bool EditTextObjectImpl::ImpChangeStyleSheets( const OUString& rOldName, SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily ) { const size_t nParagraphs = aContents.size(); bool bChanges = false; for (size_t nPara = 0; nPara < nParagraphs; ++nPara) { ContentInfo& rC = aContents[nPara]; if ( rC.GetFamily() == eOldFamily ) { if ( rC.GetStyle() == rOldName ) { rC.GetStyle() = rNewName; rC.GetFamily() = eNewFamily; bChanges = true; } } } return bChanges; } bool EditTextObjectImpl::ChangeStyleSheets( const OUString& rOldName, SfxStyleFamily eOldFamily, const OUString& rNewName, SfxStyleFamily eNewFamily) { bool bChanges = ImpChangeStyleSheets( rOldName, eOldFamily, rNewName, eNewFamily ); if ( bChanges ) ClearPortionInfo(); return bChanges; } void EditTextObjectImpl::ChangeStyleSheetName( SfxStyleFamily eFamily, const OUString& rOldName, const OUString& rNewName ) { ImpChangeStyleSheets( rOldName, eFamily, rNewName, eFamily ); } namespace { class FindAttribByChar : public std::unary_function { sal_uInt16 mnWhich; sal_uInt16 mnChar; public: FindAttribByChar(sal_uInt16 nWhich, sal_uInt16 nChar) : mnWhich(nWhich), mnChar(nChar) {} bool operator() (const XEditAttribute& rAttr) const { return (rAttr.GetItem()->Which() == mnWhich) && (rAttr.GetStart() <= mnChar) && (rAttr.GetEnd() > mnChar); } }; } void EditTextObjectImpl::StoreData( SvStream& rOStream ) const { sal_uInt16 nVer = 602; rOStream.WriteUInt16( nVer ); rOStream.WriteBool( bOwnerOfPool ); // First store the pool, later only the Surregate if ( bOwnerOfPool ) { GetPool()->SetFileFormatVersion( SOFFICE_FILEFORMAT_50 ); GetPool()->Store( rOStream ); } // Store Current text encoding ... rtl_TextEncoding eEncoding = GetSOStoreTextEncoding( osl_getThreadTextEncoding() ); rOStream.WriteUInt16( eEncoding ); // The number of paragraphs ... size_t nParagraphs = aContents.size(); // FIXME: this truncates, check usage of stream and if it can be changed, // i.e. is not persistent, adapt this and reader. sal_uInt16 nParagraphs_Stream = static_cast(nParagraphs); rOStream.WriteUInt16( nParagraphs_Stream ); sal_Unicode nUniChar = CH_FEATURE; char cFeatureConverted = OString(&nUniChar, 1, eEncoding).toChar(); // The individual paragraphs ... for (size_t nPara = 0; nPara < nParagraphs_Stream; ++nPara) { const ContentInfo& rC = aContents[nPara]; // Text... OStringBuffer aBuffer(OUStringToOString(rC.GetText(), eEncoding)); // Symbols? bool bSymbolPara = false; if (rC.GetParaAttribs().GetItemState( EE_CHAR_FONTINFO ) == SfxItemState::SET) { const SvxFontItem& rFontItem = static_cast(rC.GetParaAttribs().Get(EE_CHAR_FONTINFO)); if ( rFontItem.GetCharSet() == RTL_TEXTENCODING_SYMBOL ) { aBuffer = OStringBuffer(OUStringToOString(rC.GetText(), RTL_TEXTENCODING_SYMBOL)); bSymbolPara = true; } } for (size_t nA = 0; nA < rC.aAttribs.size(); ++nA) { const XEditAttribute& rAttr = rC.aAttribs[nA]; if (rAttr.GetItem()->Which() == EE_CHAR_FONTINFO) { const SvxFontItem& rFontItem = static_cast(*rAttr.GetItem()); if ( ( !bSymbolPara && ( rFontItem.GetCharSet() == RTL_TEXTENCODING_SYMBOL ) ) || ( bSymbolPara && ( rFontItem.GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) ) { // Not correctly converted OUString aPart = rC.GetText().copy( rAttr.GetStart(), rAttr.GetEnd() - rAttr.GetStart() ); OString aNew(OUStringToOString(aPart, rFontItem.GetCharSet())); aBuffer.remove(rAttr.GetStart(), rAttr.GetEnd() - rAttr.GetStart()); aBuffer.insert(rAttr.GetStart(), aNew); } // Convert StarSymbol back to StarBats FontToSubsFontConverter hConv = CreateFontToSubsFontConverter( rFontItem.GetFamilyName(), FontToSubsFontFlags::EXPORT | FontToSubsFontFlags::ONLYOLDSOSYMBOLFONTS ); if ( hConv ) { // Don't create a new Attrib with StarBats font, MBR changed the // SvxFontItem::Store() to store StarBats instead of StarSymbol! for (sal_uInt16 nChar = rAttr.GetStart(); nChar < rAttr.GetEnd(); ++nChar) { sal_Unicode cOld = rC.GetText()[ nChar ]; char cConv = OUStringToOString(OUString(ConvertFontToSubsFontChar(hConv, cOld)), RTL_TEXTENCODING_SYMBOL).toChar(); if ( cConv ) aBuffer[nChar] = cConv; } DestroyFontToSubsFontConverter( hConv ); } } } // Convert StarSymbol back to StarBats // StarSymbol as paragraph attribute or in StyleSheet? FontToSubsFontConverter hConv = NULL; if (rC.GetParaAttribs().GetItemState( EE_CHAR_FONTINFO ) == SfxItemState::SET) { hConv = CreateFontToSubsFontConverter( static_cast(rC.GetParaAttribs().Get( EE_CHAR_FONTINFO )).GetFamilyName(), FontToSubsFontFlags::EXPORT | FontToSubsFontFlags::ONLYOLDSOSYMBOLFONTS ); } if ( hConv ) { for ( sal_uInt16 nChar = 0; nChar < rC.GetText().getLength(); nChar++ ) { const ContentInfo::XEditAttributesType& rAttribs = rC.aAttribs; ContentInfo::XEditAttributesType::const_iterator it = std::find_if(rAttribs.begin(), rAttribs.end(), FindAttribByChar(EE_CHAR_FONTINFO, nChar)); if (it == rAttribs.end()) { sal_Unicode cOld = rC.GetText()[ nChar ]; char cConv = OUStringToOString(OUString(ConvertFontToSubsFontChar(hConv, cOld)), RTL_TEXTENCODING_SYMBOL).toChar(); if ( cConv ) aBuffer[nChar] = cConv; } } DestroyFontToSubsFontConverter( hConv ); } // Convert CH_FEATURE to CH_FEATURE_OLD OString aText = aBuffer.makeStringAndClear().replace(cFeatureConverted, CH_FEATURE_OLD); write_uInt16_lenPrefixed_uInt8s_FromOString(rOStream, aText); // StyleName and Family... write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStream, rC.GetStyle(), eEncoding); rOStream.WriteUInt16( rC.GetFamily() ); // Paragraph attributes ... rC.GetParaAttribs().Store( rOStream ); // The number of attributes ... size_t nAttribs = rC.aAttribs.size(); rOStream.WriteUInt16( nAttribs ); // And the individual attributes // Items as Surregate => always 8 bytes per Attribute // Which = 2; Surregat = 2; Start = 2; End = 2; for (size_t nAttr = 0; nAttr < nAttribs; ++nAttr) { const XEditAttribute& rX = rC.aAttribs[nAttr]; rOStream.WriteUInt16( rX.GetItem()->Which() ); GetPool()->StoreSurrogate(rOStream, rX.GetItem()); rOStream.WriteUInt16( rX.GetStart() ); rOStream.WriteUInt16( rX.GetEnd() ); } } rOStream.WriteUInt16( nMetric ); rOStream.WriteUInt16( nUserType ); rOStream.WriteUInt32( nObjSettings ); rOStream.WriteBool( bVertical ); rOStream.WriteUInt16( static_cast(nScriptType) ); rOStream.WriteBool( bStoreUnicodeStrings ); if ( bStoreUnicodeStrings ) { for ( size_t nPara = 0; nPara < nParagraphs_Stream; nPara++ ) { const ContentInfo& rC = aContents[nPara]; sal_uInt16 nL = rC.GetText().getLength(); rOStream.WriteUInt16( nL ); rOStream.Write(rC.GetText().getStr(), nL*sizeof(sal_Unicode)); // StyleSheetName must be Unicode too! // Copy/Paste from EA3 to BETA or from BETA to EA3 not possible, not needed... // If needed, change nL back to sal_uLong and increase version... nL = rC.GetStyle().getLength(); rOStream.WriteUInt16( nL ); rOStream.Write(rC.GetStyle().getStr(), nL*sizeof(sal_Unicode)); } } } void EditTextObjectImpl::CreateData( SvStream& rIStream ) { rIStream.ReadUInt16( nVersion ); // The text object was first created with the current setting of // pTextObjectPool. bool bOwnerOfCurrent = bOwnerOfPool; bool b; rIStream.ReadCharAsBool( b ); bOwnerOfPool = b; if ( bOwnerOfCurrent && !bOwnerOfPool ) { // A global Pool was used, but not handed over to me, but I need it! OSL_FAIL( "Give me the global TextObjectPool!" ); return; } else if ( !bOwnerOfCurrent && bOwnerOfPool ) { // A global Pool should be used, but this Textobject has its own. pPool = EditEngine::CreatePool(); } if ( bOwnerOfPool ) GetPool()->Load( rIStream ); // CharSet, in which it was saved: sal_uInt16 nCharSet; rIStream.ReadUInt16( nCharSet ); rtl_TextEncoding eSrcEncoding = GetSOLoadTextEncoding( (rtl_TextEncoding)nCharSet ); // The number of paragraphs ... sal_uInt16 nParagraphs(0); rIStream.ReadUInt16( nParagraphs ); const size_t nMinParaRecordSize = 6 + eSrcEncoding == RTL_TEXTENCODING_UNICODE ? 4 : 2; const size_t nMaxParaRecords = rIStream.remainingSize() / nMinParaRecordSize; if (nParagraphs > nMaxParaRecords) { SAL_WARN("editeng", "Parsing error: " << nMaxParaRecords << " max possible entries, but " << nParagraphs<< " claimed, truncating"); nParagraphs = nMaxParaRecords; } // The individual paragraphs ... for ( sal_uLong nPara = 0; nPara < nParagraphs; nPara++ ) { ContentInfo* pC = CreateAndInsertContent(); // The Text... OString aByteString = read_uInt16_lenPrefixed_uInt8s_ToOString(rIStream); pC->SetText(OStringToOUString(aByteString, eSrcEncoding)); // StyleName and Family... pC->GetStyle() = rIStream.ReadUniOrByteString(eSrcEncoding); sal_uInt16 nStyleFamily(0); rIStream.ReadUInt16( nStyleFamily ); pC->GetFamily() = (SfxStyleFamily)nStyleFamily; // Paragraph attributes ... pC->GetParaAttribs().Load( rIStream ); // The number of attributes ... sal_uInt16 nTmp16(0); rIStream.ReadUInt16( nTmp16 ); size_t nAttribs = nTmp16; const size_t nMinRecordSize(10); const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize; if (nAttribs > nMaxRecords) { SAL_WARN("editeng", "Parsing error: " << nMaxRecords << " max possible entries, but " << nAttribs << " claimed, truncating"); nAttribs = nMaxRecords; } // And the individual attributes // Items as Surregate => always 8 bytes per Attributes // Which = 2; Surregat = 2; Start = 2; End = 2; for (size_t nAttr = 0; nAttr < nAttribs; ++nAttr) { sal_uInt16 _nWhich(0), nStart(0), nEnd(0); const SfxPoolItem* pItem; rIStream.ReadUInt16( _nWhich ); _nWhich = pPool->GetNewWhich( _nWhich ); pItem = pPool->LoadSurrogate( rIStream, _nWhich, 0 ); rIStream.ReadUInt16( nStart ); rIStream.ReadUInt16( nEnd ); if ( pItem ) { if ( pItem->Which() == EE_FEATURE_NOTCONV ) { sal_Char cEncodedChar = aByteString[nStart]; sal_Unicode cChar = OUString(&cEncodedChar, 1, static_cast(pItem)->GetCharSet()).toChar(); pC->SetText(pC->GetText().replaceAt(nStart, 1, OUString(cChar))); } else { XEditAttribute* pAttr = new XEditAttribute( *pItem, nStart, nEnd ); pC->aAttribs.push_back(pAttr); if ( ( _nWhich >= EE_FEATURE_START ) && ( _nWhich <= EE_FEATURE_END ) ) { // Convert CH_FEATURE to CH_FEATURE_OLD DBG_ASSERT( (sal_uInt8) aByteString[nStart] == CH_FEATURE_OLD, "CreateData: CH_FEATURE expected!" ); if ( (sal_uInt8) aByteString[nStart] == CH_FEATURE_OLD ) pC->SetText(pC->GetText().replaceAt(nStart, 1, OUString(CH_FEATURE))); } } } } // But check for paragraph and character symbol attribs here, // FinishLoad will not be called in OpenOffice Calc, no StyleSheets... bool bSymbolPara = false; if ( pC->GetParaAttribs().GetItemState( EE_CHAR_FONTINFO ) == SfxItemState::SET ) { const SvxFontItem& rFontItem = static_cast(pC->GetParaAttribs().Get( EE_CHAR_FONTINFO )); if ( rFontItem.GetCharSet() == RTL_TEXTENCODING_SYMBOL ) { pC->SetText(OStringToOUString(aByteString, RTL_TEXTENCODING_SYMBOL)); bSymbolPara = true; } } for (size_t nAttr = pC->aAttribs.size(); nAttr; ) { const XEditAttribute& rAttr = pC->aAttribs[--nAttr]; if ( rAttr.GetItem()->Which() == EE_CHAR_FONTINFO ) { const SvxFontItem& rFontItem = static_cast(*rAttr.GetItem()); if ( ( !bSymbolPara && ( rFontItem.GetCharSet() == RTL_TEXTENCODING_SYMBOL ) ) || ( bSymbolPara && ( rFontItem.GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) ) { // Not correctly converted OString aPart(aByteString.copy(rAttr.GetStart(), rAttr.GetEnd()-rAttr.GetStart())); OUString aNew(OStringToOUString(aPart, rFontItem.GetCharSet())); pC->SetText(pC->GetText().replaceAt(rAttr.GetStart(), rAttr.GetEnd()-rAttr.GetStart(), aNew)); } // Convert StarMath and StarBats to StarSymbol FontToSubsFontConverter hConv = CreateFontToSubsFontConverter( rFontItem.GetFamilyName(), FontToSubsFontFlags::IMPORT | FontToSubsFontFlags::ONLYOLDSOSYMBOLFONTS ); if ( hConv ) { SvxFontItem aNewFontItem( rFontItem ); aNewFontItem.SetFamilyName( GetFontToSubsFontName( hConv ) ); // Replace the existing attribute with a new one. XEditAttribute* pNewAttr = CreateAttrib(aNewFontItem, rAttr.GetStart(), rAttr.GetEnd()); pPool->Remove(*rAttr.GetItem()); pC->aAttribs.erase(pC->aAttribs.begin()+nAttr); pC->aAttribs.insert(pC->aAttribs.begin()+nAttr, pNewAttr); for ( sal_uInt16 nChar = pNewAttr->GetStart(); nChar < pNewAttr->GetEnd(); nChar++ ) { sal_Unicode cOld = pC->GetText()[ nChar ]; DBG_ASSERT( cOld >= 0xF000, "cOld not converted?!" ); sal_Unicode cConv = ConvertFontToSubsFontChar( hConv, cOld ); if ( cConv ) pC->SetText(pC->GetText().replaceAt(nChar, 1, OUString(cConv))); } DestroyFontToSubsFontConverter( hConv ); } } } // Convert StarMath and StarBats to StarSymbol // Maybe old symbol font as paragraph attribute? if ( pC->GetParaAttribs().GetItemState( EE_CHAR_FONTINFO ) == SfxItemState::SET ) { const SvxFontItem& rFontItem = static_cast(pC->GetParaAttribs().Get( EE_CHAR_FONTINFO )); FontToSubsFontConverter hConv = CreateFontToSubsFontConverter( rFontItem.GetFamilyName(), FontToSubsFontFlags::IMPORT | FontToSubsFontFlags::ONLYOLDSOSYMBOLFONTS ); if ( hConv ) { SvxFontItem aNewFontItem( rFontItem ); aNewFontItem.SetFamilyName( GetFontToSubsFontName( hConv ) ); pC->GetParaAttribs().Put( aNewFontItem ); for ( sal_uInt16 nChar = 0; nChar < pC->GetText().getLength(); nChar++ ) { const ContentInfo::XEditAttributesType& rAttribs = pC->aAttribs; ContentInfo::XEditAttributesType::const_iterator it = std::find_if(rAttribs.begin(), rAttribs.end(), FindAttribByChar(EE_CHAR_FONTINFO, nChar)); if (it == rAttribs.end()) { sal_Unicode cOld = pC->GetText()[ nChar ]; DBG_ASSERT( cOld >= 0xF000, "cOld not converted?!" ); sal_Unicode cConv = ConvertFontToSubsFontChar( hConv, cOld ); if ( cConv ) pC->SetText(pC->GetText().replaceAt(nChar, 1, OUString(cConv))); } } DestroyFontToSubsFontConverter( hConv ); } } } // From 400 also the DefMetric: if ( nVersion >= 400 ) { sal_uInt16 nTmpMetric; rIStream.ReadUInt16( nTmpMetric ); if ( nVersion >= 401 ) { // In the 400 there was a bug in text objects with the own Pool, // therefore evaluate only from 401 nMetric = nTmpMetric; if ( bOwnerOfPool && pPool && ( nMetric != 0xFFFF ) ) pPool->SetDefaultMetric( (SfxMapUnit)nMetric ); } } if ( nVersion >= 600 ) { rIStream.ReadUInt16( nUserType ); rIStream.ReadUInt32( nObjSettings ); } if ( nVersion >= 601 ) { bool bTmp(false); rIStream.ReadCharAsBool( bTmp ); bVertical = bTmp; } if ( nVersion >= 602 ) { sal_uInt16 aTmp16; rIStream.ReadUInt16( aTmp16 ); nScriptType = static_cast(aTmp16); bool bUnicodeStrings(false); rIStream.ReadCharAsBool( bUnicodeStrings ); if ( bUnicodeStrings ) { for (sal_uInt16 nPara = 0; nPara < nParagraphs; ++nPara) { ContentInfo& rC = aContents[nPara]; sal_uInt16 nL(0); // Text rIStream.ReadUInt16(nL); if (nL) { size_t nMaxElementsPossible = rIStream.remainingSize() / sizeof(sal_Unicode); if (nL > nMaxElementsPossible) { SAL_WARN("editeng", "Parsing error: " << nMaxElementsPossible << " max possible entries, but " << nL << " claimed, truncating"); nL = nMaxElementsPossible; } rtl_uString *pStr = rtl_uString_alloc(nL); rIStream.Read(pStr->buffer, nL*sizeof(sal_Unicode)); rC.SetText((OUString(pStr, SAL_NO_ACQUIRE))); nL = 0; } // StyleSheetName rIStream.ReadUInt16( nL ); if ( nL ) { size_t nMaxElementsPossible = rIStream.remainingSize() / sizeof(sal_Unicode); if (nL > nMaxElementsPossible) { SAL_WARN("editeng", "Parsing error: " << nMaxElementsPossible << " max possible entries, but " << nL << " claimed, truncating"); nL = nMaxElementsPossible; } rtl_uString *pStr = rtl_uString_alloc(nL); rIStream.Read(pStr->buffer, nL*sizeof(sal_Unicode) ); rC.GetStyle() = OUString(pStr, SAL_NO_ACQUIRE); } } } } // from 500 the tabs are interpreted differently: TabPos + LI, previously only TabPos. // Works only if tab positions are set, not when DefTab. if ( nVersion < 500 ) { for (size_t i = 0, n = aContents.size(); i < n; ++i) { ContentInfo& rC = aContents[i]; const SvxLRSpaceItem& rLRSpace = static_cast(rC.GetParaAttribs().Get(EE_PARA_LRSPACE)); if ( rLRSpace.GetTxtLeft() && ( rC.GetParaAttribs().GetItemState( EE_PARA_TABS ) == SfxItemState::SET ) ) { const SvxTabStopItem& rTabs = static_cast(rC.GetParaAttribs().Get(EE_PARA_TABS)); SvxTabStopItem aNewTabs( 0, 0, SVX_TAB_ADJUST_LEFT, EE_PARA_TABS ); for ( sal_uInt16 t = 0; t < rTabs.Count(); t++ ) { const SvxTabStop& rT = rTabs[ t ]; aNewTabs.Insert( SvxTabStop( rT.GetTabPos() - rLRSpace.GetTxtLeft(), rT.GetAdjustment(), rT.GetDecimal(), rT.GetFill() ) ); } rC.GetParaAttribs().Put( aNewTabs ); } } } } bool EditTextObjectImpl::operator==( const EditTextObjectImpl& rCompare ) const { if( this == &rCompare ) return true; if( ( aContents.size() != rCompare.aContents.size() ) || ( pPool != rCompare.pPool ) || ( nMetric != rCompare.nMetric ) || ( nUserType!= rCompare.nUserType ) || ( nScriptType != rCompare.nScriptType ) || ( bVertical != rCompare.bVertical ) ) return false; for (size_t i = 0, n = aContents.size(); i < n; ++i) { if (aContents[i] != rCompare.aContents[i]) return false; } return true; } // #i102062# bool EditTextObjectImpl::isWrongListEqual(const EditTextObjectImpl& rCompare) const { if (aContents.size() != rCompare.aContents.size()) { return false; } for (size_t i = 0, n = aContents.size(); i < n; ++i) { const ContentInfo& rCandA = aContents[i]; const ContentInfo& rCandB = rCompare.aContents[i]; if(!rCandA.isWrongListEqual(rCandB)) { return false; } } return true; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */