/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; static SwContentNode* GetContentNode(SwDoc& rDoc, SwNodeIndex& rIdx, bool bNext) { SwContentNode * pCNd = rIdx.GetNode().GetContentNode(); if(!pCNd && nullptr == (pCNd = bNext ? rDoc.GetNodes().GoNext(&rIdx) : SwNodes::GoPrevious(&rIdx))) { pCNd = bNext ? SwNodes::GoPrevious(&rIdx) : rDoc.GetNodes().GoNext(&rIdx); OSL_ENSURE(pCNd, "no ContentNode found"); } return pCNd; } static OUString lcl_getTypePath(OUString& rType) { OUString aRet; if (rType.startsWith("urn:bails")) { rType = "urn:bails"; aRet = "tscp/bails.rdf"; } return aRet; } // Stack entry for all text attributes SwFltStackEntry::SwFltStackEntry(const SwPosition& rStartPos, std::unique_ptr pHt) : m_aMkPos(rStartPos) , m_aPtPos(rStartPos) , pAttr( std::move(pHt) ) , m_isAnnotationOnEnd(false) , mnStartCP(-1) , mnEndCP(-1) , bIsParaEnd(false) { bOld = false; // used for marking Attributes *before* skipping field results bOpen = true; // lock the attribute --> may first bConsumedByField = false; } SwFltStackEntry::~SwFltStackEntry() { // Although attribute got passed as pointer, it gets deleted here } void SwFltStackEntry::SetEndPos(const SwPosition& rEndPos) { // Release attribute and keep track of end // Everything with sal_uInt16s, lest the inserting of new text at // the cursor position moves the attribute's range // That's not the desired behavior! bOpen = false; // release and remember END m_aPtPos.FromSwPosition(rEndPos); } bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode const eCheck, const SwFltPosition &rMkPos, const SwFltPosition &rPtPos, bool bIsParaEnd, sal_uInt16 nWhich) { // does this range actually contain something? // empty range is allowed if at start of empty paragraph // fields are special: never have range, so leave them // The only position of 0x0D will not be able to make region in the old logic // because it is beyond the length of para...need special consideration here. sal_uLong nMk = rMkPos.m_nNode.GetIndex() + 1; const SwNodes& rMkNodes = rMkPos.m_nNode.GetNodes(); if (nMk >= rMkNodes.Count()) return false; SwContentNode *const pContentNode(rMkNodes[nMk]->GetContentNode()); if (rMkPos == rPtPos && ((0 != rPtPos.m_nContent) || (pContentNode && (0 != pContentNode->Len()))) && ( RES_TXTATR_FIELD != nWhich && RES_TXTATR_ANNOTATION != nWhich && RES_TXTATR_INPUTFIELD != nWhich ) && !(bIsParaEnd && pContentNode && pContentNode->IsTextNode() && 0 != pContentNode->Len() )) { return false; } // The content indices always apply to the node! rRegion.GetPoint()->nNode = rMkPos.m_nNode.GetIndex() + 1; SwContentNode* pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, true); SAL_WARN_IF(pCNd->Len() < rMkPos.m_nContent, "sw.ww8", "invalid content index " << rMkPos.m_nContent << " but text node has only " << pCNd->Len()); rRegion.GetPoint()->nContent.Assign(pCNd, std::min(rMkPos.m_nContent, pCNd->Len())); rRegion.SetMark(); if (rMkPos.m_nNode != rPtPos.m_nNode) { sal_uLong n = rPtPos.m_nNode.GetIndex() + 1; SwNodes& rNodes = rRegion.GetPoint()->nNode.GetNodes(); if (n >= rNodes.Count()) return false; rRegion.GetPoint()->nNode = n; pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, false); } SAL_WARN_IF(pCNd->Len() < rPtPos.m_nContent, "sw.ww8", "invalid content index " << rPtPos.m_nContent << " but text node has only " << pCNd->Len()); rRegion.GetPoint()->nContent.Assign(pCNd, std::min(rPtPos.m_nContent, pCNd->Len())); OSL_ENSURE( CheckNodesRange( rRegion.Start()->nNode, rRegion.End()->nNode, true ), "attribute or similar crosses section-boundaries" ); bool bRet = true; if (eCheck & RegionMode::CheckNodes) { bRet &= CheckNodesRange(rRegion.Start()->nNode, rRegion.End()->nNode, true); } if (eCheck & RegionMode::CheckFieldmark) { bRet &= !sw::mark::IsFieldmarkOverlap(rRegion); } return bRet; } bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode eCheck) const { return MakeRegion(rDoc, rRegion, eCheck, m_aMkPos, m_aPtPos, bIsParaEnd, pAttr->Which()); } SwFltControlStack::SwFltControlStack(SwDoc& rDo, sal_uLong nFieldFl) : nFieldFlags(nFieldFl),bHasSdOD(true), bSdODChecked(false), rDoc(rDo), bIsEndStack(false) { } SwFltControlStack::~SwFltControlStack() { OSL_ENSURE(m_Entries.empty(), "There are still Attributes on the stack"); } // MoveAttrs() is meant to address the following problem: // When a field like "set variable" is set through the stack, the text // is shifted by one \xff character, which makes all subsequent // attribute positions invalid. // After setting the attribute in the doc, MoveAttrs() needs to be // called in order to push all attribute positions to the right in the // same paragraph further out by one character. void SwFltControlStack::MoveAttrs(const SwPosition& rPos, MoveAttrsMode eMode) { sal_uLong nPosNd = rPos.nNode.GetIndex(); sal_uInt16 nPosCt = rPos.nContent.GetIndex() - 1; for (size_t i = 0, nCnt = m_Entries.size(); i < nCnt; ++i) { SwFltStackEntry& rEntry = *m_Entries[i]; if ( (rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) && (rEntry.m_aMkPos.m_nContent >= nPosCt) ) { rEntry.m_aMkPos.m_nContent++; OSL_ENSURE( rEntry.m_aMkPos.m_nContent <= rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(), "Attribute ends after end of line" ); } if ( (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) && (rEntry.m_aPtPos.m_nContent >= nPosCt) ) { if ( !rEntry.m_isAnnotationOnEnd || rEntry.m_aPtPos.m_nContent > nPosCt) { assert(!(rEntry.m_isAnnotationOnEnd && rEntry.m_aPtPos.m_nContent > nPosCt)); if ( eMode == MoveAttrsMode::POSTIT_INSERTED && rEntry.m_aPtPos.m_nContent == nPosCt && rEntry.pAttr->Which() == RES_FLTR_ANNOTATIONMARK) { rEntry.m_isAnnotationOnEnd = true; eMode = MoveAttrsMode::DEFAULT; // only set 1 flag } rEntry.m_aPtPos.m_nContent++; OSL_ENSURE( rEntry.m_aPtPos.m_nContent <= rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(), "Attribute ends after end of line" ); } } } } void SwFltControlStack::MarkAllAttrsOld() { size_t nCnt = m_Entries.size(); for (size_t i=0; i < nCnt; ++i) m_Entries[i]->bOld = true; } namespace { bool couldExtendEntry(const SwFltStackEntry *pExtendCandidate, const SfxPoolItem& rAttr) { return (pExtendCandidate && !pExtendCandidate->bConsumedByField && //if we bring character attributes into the fold we need to both //a) consider RES_CHRATR_FONTSIZE and RES_CHRATR_FONT wrt Word's CJK/CTL variants //b) consider crossing table cell boundaries (tdf#102334) isPARATR_LIST(rAttr.Which()) && *(pExtendCandidate->pAttr) == rAttr); } } void SwFltControlStack::NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr) { sal_uInt16 nWhich = rAttr.Which(); // Set end position of potentially equal attributes on stack, so // as to avoid having them accumulate SwFltStackEntry *pExtendCandidate = SetAttr(rPos, nWhich); if (couldExtendEntry(pExtendCandidate, rAttr)) { //Here we optimize by seeing if there is an attribute uncommitted //to the document which //a) has the same value as this attribute //b) is already open, or ends at the same place as where we're starting //from. If so we merge it with this one and elide adding another //to the stack pExtendCandidate->SetEndPos(rPos); pExtendCandidate->bOpen=true; } else { SwFltStackEntry *pTmp = new SwFltStackEntry(rPos, std::unique_ptr(rAttr.Clone()) ); pTmp->SetStartCP(GetCurrAttrCP()); m_Entries.push_back(std::unique_ptr(pTmp)); } } void SwFltControlStack::DeleteAndDestroy(Entries::size_type nCnt) { OSL_ENSURE(nCnt < m_Entries.size(), "Out of range!"); if (nCnt < m_Entries.size()) { auto aElement = m_Entries.begin() + nCnt; m_Entries.erase(aElement); } //Clear the para end position recorded in reader intermittently for the least impact on loading performance //Because the attributes handled based on the unit of para if ( empty() ) { ClearParaEndPosition(); bHasSdOD = true; bSdODChecked = false; } } // SwFltControlStack::StealAttr() removes attributes of the given type // from the stack. Allowed as nAttrId: 0 meaning any, or a specific // type. This makes them disappear from the doc structure. Only // attributes from the same paragraph as rPos are removed. Used for // graphic apos -> images. void SwFltControlStack::StealAttr(const SwNodeIndex& rNode) { size_t nCnt = m_Entries.size(); while (nCnt) { nCnt --; SwFltStackEntry& rEntry = *m_Entries[nCnt]; if (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == rNode.GetIndex()) { DeleteAndDestroy(nCnt); // delete from the stack } } } // SwFltControlStack::KillUnlockedAttr() removes all attributes from // the stack, which are assigned to an rPos. This makes them disappear // from the doc structure. Used in WW import for ignoring attributes // assigned to the 0x0c section break symbol. void SwFltControlStack::KillUnlockedAttrs(const SwPosition& rPos) { SwFltPosition aFltPos(rPos); size_t nCnt = m_Entries.size(); while( nCnt ) { nCnt --; SwFltStackEntry& rEntry = *m_Entries[nCnt]; if( !rEntry.bOld && !rEntry.bOpen && (rEntry.m_aMkPos == aFltPos) && (rEntry.m_aPtPos == aFltPos)) { DeleteAndDestroy( nCnt ); // remove from stack } } } // Unlock all locked attributes and move to the end, all others will // be applied to the document and removed from the stack. // Returns if there were any selected attributes on the stack SwFltStackEntry* SwFltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, bool bTstEnd, tools::Long nHand, bool consumedByField) { SwFltStackEntry *pRet = nullptr; SwFltPosition aFltPos(rPos); OSL_ENSURE(!nAttrId || (POOLATTR_BEGIN <= nAttrId && POOLATTR_END > nAttrId) || (RES_FLTRATTR_BEGIN <= nAttrId && RES_FLTRATTR_END > nAttrId), "Wrong id for attribute"); auto aI = m_Entries.begin(); while (aI != m_Entries.end()) { bool bLastEntry = aI == m_Entries.end() - 1; SwFltStackEntry& rEntry = **aI; if (rEntry.bOpen) { // set end of attribute bool bF = false; if (!nAttrId ) { bF = true; } else if (nAttrId == rEntry.pAttr->Which()) { if( nAttrId != RES_FLTR_BOOKMARK && nAttrId != RES_FLTR_ANNOTATIONMARK && nAttrId != RES_FLTR_RDFMARK ) { // query handle bF = true; } else if (nAttrId == RES_FLTR_BOOKMARK && nHand == static_cast(rEntry.pAttr.get())->GetHandle()) { bF = true; } else if (nAttrId == RES_FLTR_ANNOTATIONMARK && nHand == static_cast(rEntry.pAttr.get())->GetValue()) { bF = true; } else if (nAttrId == RES_FLTR_RDFMARK && nHand == static_cast(rEntry.pAttr.get())->GetHandle()) { bF = true; } } if (bF) { rEntry.bConsumedByField = consumedByField; rEntry.SetEndPos(rPos); rEntry.SetEndCP(GetCurrAttrCP()); if (bLastEntry && nAttrId == rEntry.pAttr->Which()) { //potential candidate for merging with an identical //property beginning at rPos pRet = &rEntry; } } ++aI; continue; } // if the end position is equal to the cursor position, then // refrain from applying it; there needs to be following text, // except at the very end. (attribute expansion !!) // Never apply end stack except at document ending if (bTstEnd) { if (bIsEndStack) { ++aI; continue; } //defer inserting this attribute into the document until //we advance to the next node, or finish processing the document if (rEntry.m_aPtPos.m_nNode.GetIndex() == aFltPos.m_nNode.GetIndex()) { if (bLastEntry && nAttrId == rEntry.pAttr->Which() && rEntry.m_aPtPos.m_nContent == aFltPos.m_nContent) { //potential candidate for merging with an identical //property beginning at rPos pRet = &rEntry; } ++aI; continue; } } SetAttrInDoc(rPos, rEntry); aI = m_Entries.erase(aI); } return pRet; } static bool MakePoint(const SwFltStackEntry& rEntry, SwDoc& rDoc, SwPaM& rRegion) { // the anchor is the Pam's Point. It's modified when inserting // text, etc.; therefore it is kept on the stack. Only the // attribute's format needs to be set. rRegion.DeleteMark(); sal_uLong nMk = rEntry.m_aMkPos.m_nNode.GetIndex() + 1; const SwNodes& rMkNodes = rEntry.m_aMkPos.m_nNode.GetNodes(); if (nMk >= rMkNodes.Count()) return false; rRegion.GetPoint()->nNode = nMk; SwContentNode* pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, true); rRegion.GetPoint()->nContent.Assign(pCNd, rEntry.m_aMkPos.m_nContent); return true; } // MakeBookRegionOrPoint() behaves like MakeRegionOrPoint, except that // it adheres to certain restrictions on bookmarks in tables (cannot // span more than one cell) static bool MakeBookRegionOrPoint(const SwFltStackEntry& rEntry, SwDoc& rDoc, SwPaM& rRegion ) { if (rEntry.MakeRegion(rDoc, rRegion, SwFltStackEntry::RegionMode::CheckNodes)) { if (rRegion.GetPoint()->nNode.GetNode().FindTableBoxStartNode() != rRegion.GetMark()->nNode.GetNode().FindTableBoxStartNode()) { rRegion.Exchange(); // invalid range rRegion.DeleteMark(); // -> both to mark } return true; } return MakePoint(rEntry, rDoc, rRegion); } // IterateNumrulePiece() looks for the first range valid for Numrules // between rTmpStart and rEnd. // rNds denotes the doc nodes // rEnd denotes the range end, // rTmpStart is an in/out parameter: in: start of range to be searched, // out: start of valid range // rTmpEnd is an out parameter // Returns true for valid range static bool IterateNumrulePiece( const SwNodeIndex& rEnd, SwNodeIndex& rTmpStart, SwNodeIndex& rTmpEnd ) { while( ( rTmpStart <= rEnd ) && !( rTmpStart.GetNode().IsTextNode() ) ) // look for valid start ++rTmpStart; rTmpEnd = rTmpStart; while( ( rTmpEnd <= rEnd ) && ( rTmpEnd.GetNode().IsTextNode() ) ) // look for valid end + 1 ++rTmpEnd; --rTmpEnd; // valid end return rTmpStart <= rTmpEnd; // valid ? } //***This function will check whether there is existing individual attribute position for 0x0D***/ //The check will happen only once for a paragraph during loading bool SwFltControlStack::HasSdOD() { bool bRet = false; for (auto const& it : m_Entries) { SwFltStackEntry& rEntry = *it; if ( rEntry.mnStartCP == rEntry.mnEndCP ) { if ( CheckSdOD(rEntry.mnStartCP,rEntry.mnEndCP) ) { bRet = true; break; } } } return bRet; } void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, SwFltStackEntry& rEntry) { SwPaM aRegion( rTmpPos ); switch(rEntry.pAttr->Which()) { case RES_FLTR_ANCHOR: { SwFrameFormat* pFormat = static_cast(rEntry.pAttr.get())->GetFrameFormat(); if (pFormat != nullptr) { MakePoint(rEntry, rDoc, aRegion); SwFormatAnchor aAnchor(pFormat->GetAnchor()); aAnchor.SetAnchor(aRegion.GetPoint()); pFormat->SetFormatAttr(aAnchor); // So the frames will be created when inserting into // existing doc (after setting the anchor!): if (rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && (RndStdIds::FLY_AT_PARA == pFormat->GetAnchor().GetAnchorId())) { pFormat->MakeFrames(); } } } break; case RES_TXTATR_FIELD: case RES_TXTATR_ANNOTATION: case RES_TXTATR_INPUTFIELD: break; case RES_TXTATR_TOXMARK: break; case RES_FLTR_NUMRULE: // insert Numrule { const OUString& rNumNm = static_cast(rEntry.pAttr.get())->GetValue(); SwNumRule* pNumRule = rDoc.FindNumRulePtr( rNumNm ); if( pNumRule ) { if (rEntry.MakeRegion(rDoc, aRegion, SwFltStackEntry::RegionMode::CheckNodes)) { SwNodeIndex aTmpStart( aRegion.Start()->nNode ); SwNodeIndex aTmpEnd( aTmpStart ); SwNodeIndex& rRegEndNd = aRegion.End()->nNode; while( IterateNumrulePiece( rRegEndNd, aTmpStart, aTmpEnd ) ) { SwPaM aTmpPam( aTmpStart, aTmpEnd ); // no start of a new list rDoc.SetNumRule( aTmpPam, *pNumRule, false ); aTmpStart = aTmpEnd; // here starts the next range ++aTmpStart; } } else rDoc.DelNumRule( rNumNm ); } } break; case RES_FLTR_BOOKMARK: { SwFltBookmark* pB = static_cast(rEntry.pAttr.get()); const OUString& rName = static_cast(rEntry.pAttr.get())->GetName(); if (IsFlagSet(BOOK_TO_VAR_REF)) { SwFieldType* pFT = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rName, false); if (!pFT) { SwSetExpFieldType aS(&rDoc, rName, nsSwGetSetExpType::GSE_STRING); pFT = rDoc.getIDocumentFieldsAccess().InsertFieldType(aS); } SwSetExpField aField(static_cast(pFT), pB->GetValSys()); aField.SetSubType( nsSwExtendedSubType::SUB_INVISIBLE ); MakePoint(rEntry, rDoc, aRegion); rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, SwFormatField(aField)); MoveAttrs( *(aRegion.GetPoint()) ); } if ( ( !IsFlagSet(HYPO) || IsFlagSet(BOOK_AND_REF) ) && !rEntry.bConsumedByField ) { MakeBookRegionOrPoint(rEntry, rDoc, aRegion); // #i120879# - create a cross reference heading bookmark if appropriate. const IDocumentMarkAccess::MarkType eBookmarkType = ( pB->IsTOCBookmark() && IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aRegion ) ) ? IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK : IDocumentMarkAccess::MarkType::BOOKMARK; rDoc.getIDocumentMarkAccess()->makeMark(aRegion, rName, eBookmarkType, sw::mark::InsertMode::New); } } break; case RES_FLTR_ANNOTATIONMARK: { if (MakeBookRegionOrPoint(rEntry, rDoc, aRegion)) { SwTextNode const*const pTextNode( aRegion.End()->nNode.GetNode().GetTextNode()); SwTextField const*const pField = pTextNode ? pTextNode->GetFieldTextAttrAt( aRegion.End()->nContent.GetIndex() - 1, true) : nullptr; if (pField) { SwPostItField const*const pPostIt( dynamic_cast(pField->GetFormatField().GetField())); if (pPostIt) { assert(pPostIt->GetName().isEmpty()); if (!aRegion.HasMark()) { // Annotation range was found in the file, but start/end is the same, // pointing after the postit placeholder (see assert above). // Adjust the start of the range to actually cover the comment, similar // to what the UI and the UNO API does. aRegion.SetMark(); --aRegion.Start()->nContent; } rDoc.getIDocumentMarkAccess()->makeAnnotationMark(aRegion, OUString()); } else { SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: unexpected field"); } } else { SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: missing field"); } } else SAL_WARN("sw", "failed to make book region or point"); } break; case RES_FLTR_RDFMARK: { if (MakeBookRegionOrPoint(rEntry, rDoc, aRegion)) { SwFltRDFMark* pMark = static_cast(rEntry.pAttr.get()); if (aRegion.GetNode().IsTextNode()) { SwTextNode& rTextNode = *aRegion.GetNode().GetTextNode(); for (const std::pair& rAttribute : pMark->GetAttributes()) { OUString aTypeNS = rAttribute.first; OUString aMetadataFilePath = lcl_getTypePath(aTypeNS); if (aMetadataFilePath.isEmpty()) continue; SwRDFHelper::addTextNodeStatement(aTypeNS, aMetadataFilePath, rTextNode, rAttribute.first, rAttribute.second); } } } else SAL_WARN("sw", "failed to make book region or point"); } break; case RES_FLTR_TOX: { MakePoint(rEntry, rDoc, aRegion); SwPosition* pPoint = aRegion.GetPoint(); SwFltTOX* pTOXAttr = static_cast(rEntry.pAttr.get()); // test if on this node there had been a pagebreak BEFORE the // tox attribute was put on the stack SfxItemSet aBkSet( rDoc.GetAttrPool(), svl::Items{} ); SwContentNode* pNd = nullptr; if( !pTOXAttr->HadBreakItem() || !pTOXAttr->HadPageDescItem() ) { pNd = pPoint->nNode.GetNode().GetContentNode(); if( pNd ) { const SfxItemSet* pSet = pNd->GetpSwAttrSet(); const SfxPoolItem* pItem; if( pSet ) { if( !pTOXAttr->HadBreakItem() && SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) ) { aBkSet.Put( *pItem ); pNd->ResetAttr( RES_BREAK ); } if( !pTOXAttr->HadPageDescItem() && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) ) { aBkSet.Put( *pItem ); pNd->ResetAttr( RES_PAGEDESC ); } } } } // set (above saved and removed) the break item at the node following the TOX if (pNd && aBkSet.Count()) pNd->SetAttr(aBkSet); } break; case RES_FLTR_REDLINE: { if (rEntry.MakeRegion(rDoc, aRegion, SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark)) { rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete ); SwFltRedline& rFltRedline = *static_cast(rEntry.pAttr.get()); SwRedlineData aData(rFltRedline.eType, rFltRedline.nAutorNo, rFltRedline.aStamp, OUString(), nullptr ); rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(aData, aRegion), true ); rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::NONE | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete ); } } break; default: { // Revised for more complex situations should be considered if ( !bSdODChecked ) { bHasSdOD = HasSdOD(); bSdODChecked = true; } sal_Int32 nStart = rEntry.GetStartCP(); sal_Int32 nEnd = rEntry.GetEndCP(); if (nStart != -1 && nEnd != -1 && nEnd >= nStart ) { rEntry.SetIsParaEnd( IsParaEndInCPs(nStart,nEnd,bHasSdOD) ); } if (rEntry.MakeRegion(rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck)) { if (rEntry.IsParaEnd()) { rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.pAttr, SetAttrMode::DEFAULT, nullptr, true); } else { rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.pAttr); } } } break; } } bool SwFltControlStack::IsParaEndInCPs(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/,bool /*bSdOD*/) const { return false; } bool SwFltControlStack::CheckSdOD(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/) { return false; } SfxPoolItem* SwFltControlStack::GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos) { size_t nSize = m_Entries.size(); while (nSize) { // is it the looked-for attribute ? (only applies to locked, meaning // currently set attributes!!) SwFltStackEntry &rEntry = *m_Entries[--nSize]; if (rEntry.bOpen && rEntry.pAttr->Which() == nWhich) { if (pPos) *pPos = nSize; return rEntry.pAttr.get(); // Ok, so stop } } return nullptr; } const SfxPoolItem* SwFltControlStack::GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich) { SwFltPosition aFltPos(rPos); size_t nSize = m_Entries.size(); while (nSize) { SwFltStackEntry &rEntry = *m_Entries[--nSize]; if (rEntry.bOpen && rEntry.pAttr->Which() == nWhich && rEntry.m_aMkPos == aFltPos) { return rEntry.pAttr.get(); } } return nullptr; } void SwFltControlStack::Delete(const SwPaM &rPam) { const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); if( !rPam.HasMark() || *pStt >= *pEnd ) return; SwNodeIndex aStartNode(pStt->nNode, -1); const sal_Int32 nStartIdx = pStt->nContent.GetIndex(); SwNodeIndex aEndNode(pEnd->nNode, -1); const sal_Int32 nEndIdx = pEnd->nContent.GetIndex(); // We don't support deleting content that is over one node, or removing a node. OSL_ENSURE(aEndNode == aStartNode, "nodes must be the same, or this method extended"); if (aEndNode != aStartNode) return; for (size_t nSize = m_Entries.size(); nSize > 0;) { SwFltStackEntry& rEntry = *m_Entries[--nSize]; bool bEntryStartAfterSelStart = (rEntry.m_aMkPos.m_nNode == aStartNode && rEntry.m_aMkPos.m_nContent >= nStartIdx); bool bEntryStartBeforeSelEnd = (rEntry.m_aMkPos.m_nNode == aEndNode && rEntry.m_aMkPos.m_nContent <= nEndIdx); bool bEntryEndAfterSelStart = false; bool bEntryEndBeforeSelEnd = false; if (!rEntry.bOpen) { bEntryEndAfterSelStart = (rEntry.m_aPtPos.m_nNode == aStartNode && rEntry.m_aPtPos.m_nContent >= nStartIdx); bEntryEndBeforeSelEnd = (rEntry.m_aPtPos.m_nNode == aEndNode && rEntry.m_aPtPos.m_nContent <= nEndIdx); } bool bTotallyContained = false; if ( bEntryStartAfterSelStart && bEntryStartBeforeSelEnd && bEntryEndAfterSelStart && bEntryEndBeforeSelEnd ) { bTotallyContained = true; } if (bTotallyContained) { // after start, before end, delete DeleteAndDestroy(nSize); continue; } const sal_Int32 nContentDiff = nEndIdx - nStartIdx; // to be adjusted if (bEntryStartAfterSelStart) { if (bEntryStartBeforeSelEnd) { // move start to new start rEntry.m_aMkPos.SetPos(aStartNode, nStartIdx); } else rEntry.m_aMkPos.m_nContent -= nContentDiff; } if (bEntryEndAfterSelStart) { if (bEntryEndBeforeSelEnd) rEntry.m_aPtPos.SetPos(aStartNode, nStartIdx); else rEntry.m_aPtPos.m_nContent -= nContentDiff; } //That's what Open is, end equal to start, and nPtContent is invalid if (rEntry.bOpen) rEntry.m_aPtPos = rEntry.m_aMkPos; } } // methods of SwFltAnchor follow SwFltAnchor::SwFltAnchor(SwFrameFormat* pFormat) : SfxPoolItem(RES_FLTR_ANCHOR), pFrameFormat(pFormat) { pListener.reset(new SwFltAnchorListener(this)); pListener->StartListening(pFrameFormat->GetNotifier()); } SwFltAnchor::SwFltAnchor(const SwFltAnchor& rCpy) : SfxPoolItem(RES_FLTR_ANCHOR), pFrameFormat(rCpy.pFrameFormat) { pListener.reset(new SwFltAnchorListener(this)); pListener->StartListening(pFrameFormat->GetNotifier()); } SwFltAnchor::~SwFltAnchor() { } void SwFltAnchor::SetFrameFormat(SwFrameFormat * _pFrameFormat) { pFrameFormat = _pFrameFormat; } bool SwFltAnchor::operator==(const SfxPoolItem& rItem) const { return SfxPoolItem::operator==(rItem) && pFrameFormat == static_cast(rItem).pFrameFormat; } SwFltAnchor* SwFltAnchor::Clone(SfxItemPool*) const { return new SwFltAnchor(*this); } SwFltAnchorListener::SwFltAnchorListener(SwFltAnchor* pFltAnchor) : m_pFltAnchor(pFltAnchor) { } void SwFltAnchorListener::Notify(const SfxHint& rHint) { if (rHint.GetId() == SfxHintId::Dying) m_pFltAnchor->SetFrameFormat(nullptr); else if (auto pDrawFrameFormatHint = dynamic_cast(&rHint)) { if (pDrawFrameFormatHint->m_eId != sw::DrawFrameFormatHintId::DYING) return; m_pFltAnchor->SetFrameFormat(nullptr); } else if (auto pLegacyHint = dynamic_cast(&rHint)) { if(pLegacyHint->m_pNew->Which() != RES_FMT_CHG) return; auto pFormatChg = dynamic_cast(pLegacyHint->m_pNew); auto pFrameFormat = pFormatChg ? dynamic_cast(pFormatChg->pChangedFormat) : nullptr; if(pFrameFormat) m_pFltAnchor->SetFrameFormat(pFrameFormat); } } // methods of SwFltRedline follow bool SwFltRedline::operator==(const SfxPoolItem& rItem) const { return SfxPoolItem::operator==(rItem) && this == &rItem; } SwFltRedline* SwFltRedline::Clone( SfxItemPool* ) const { return new SwFltRedline(*this); } // methods of SwFltBookmark follow SwFltBookmark::SwFltBookmark( const OUString& rNa, const OUString& rVa, tools::Long nHand, const bool bIsTOCBookmark ) : SfxPoolItem( RES_FLTR_BOOKMARK ) , mnHandle( nHand ) , maName( rNa ) , maVal( rVa ) , mbIsTOCBookmark( bIsTOCBookmark ) { // eSrc: CHARSET_DONTKNOW for no transform at operator << // Upcase is always done. // Transform is never done at XXXStack.NewAttr(...). // otherwise: Src Charset from argument for aName // Src Charset from filter for aVal ( Text ) if ( IsTOCBookmark() && ! rNa.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) ) { maName = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix(); maName += rNa; } } bool SwFltBookmark::operator==(const SfxPoolItem& rItem) const { return SfxPoolItem::operator==(rItem) && maName == static_cast(rItem).maName && mnHandle == static_cast(rItem).mnHandle; } SwFltBookmark* SwFltBookmark::Clone(SfxItemPool*) const { return new SwFltBookmark(*this); } SwFltRDFMark::SwFltRDFMark() : SfxPoolItem(RES_FLTR_RDFMARK), m_nHandle(0) { } bool SwFltRDFMark::operator==(const SfxPoolItem& rItem) const { if (!SfxPoolItem::operator==(rItem)) return false; const SwFltRDFMark& rMark = static_cast(rItem); return m_nHandle == rMark.m_nHandle && m_aAttributes == rMark.m_aAttributes; } SwFltRDFMark* SwFltRDFMark::Clone(SfxItemPool*) const { return new SwFltRDFMark(*this); } void SwFltRDFMark::SetHandle(tools::Long nHandle) { m_nHandle = nHandle; } tools::Long SwFltRDFMark::GetHandle() const { return m_nHandle; } void SwFltRDFMark::SetAttributes(const std::vector< std::pair >& rAttributes) { m_aAttributes = rAttributes; } const std::vector< std::pair >& SwFltRDFMark::GetAttributes() const { return m_aAttributes; } // methods of SwFltTOX follow SwFltTOX::SwFltTOX(std::shared_ptr xBase) : SfxPoolItem(RES_FLTR_TOX), m_xTOXBase(std::move(xBase)), bHadBreakItem( false ), bHadPageDescItem( false ) { } bool SwFltTOX::operator==(const SfxPoolItem& rItem) const { return SfxPoolItem::operator==(rItem) && m_xTOXBase.get() == static_cast(rItem).m_xTOXBase.get(); } SwFltTOX* SwFltTOX::Clone(SfxItemPool*) const { return new SwFltTOX(*this); } // UpdatePageDescs needs to be called at end of parsing to make Writer actually // accept Pagedescs contents void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset) { // Update document page descriptors (only this way also left pages // get adjusted) // PageDesc "Standard" rDoc.ChgPageDesc(0, rDoc.GetPageDesc(0)); // PageDescs "Convert..." for (size_t i = nInPageDescOffset; i < rDoc.GetPageDescCnt(); ++i) rDoc.ChgPageDesc(i, rDoc.GetPageDesc(i)); } FrameDeleteWatch::FrameDeleteWatch(SwFrameFormat* pFormat) : m_pFormat(pFormat) { if(m_pFormat) StartListening(pFormat->GetNotifier()); } void FrameDeleteWatch::Notify(const SfxHint& rHint) { bool bDying = false; if (rHint.GetId() == SfxHintId::Dying) bDying = true; else if (auto pDrawFrameFormatHint = dynamic_cast(&rHint)) bDying = pDrawFrameFormatHint->m_eId == sw::DrawFrameFormatHintId::DYING; if (bDying) { m_pFormat = nullptr; EndListeningAll(); } } FrameDeleteWatch::~FrameDeleteWatch() { m_pFormat = nullptr; EndListeningAll(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */