diff options
Diffstat (limited to 'sw/source/filter/ww8/writerhelper.cxx')
-rw-r--r-- | sw/source/filter/ww8/writerhelper.cxx | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/sw/source/filter/ww8/writerhelper.cxx b/sw/source/filter/ww8/writerhelper.cxx new file mode 100644 index 000000000..9c3bae38f --- /dev/null +++ b/sw/source/filter/ww8/writerhelper.cxx @@ -0,0 +1,918 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <com/sun/star/util/CloseVetoException.hpp> + +#include <doc.hxx> +#include "writerhelper.hxx" +#include <msfilter.hxx> +#include <com/sun/star/container/XChild.hpp> + +#include <algorithm> +#include <svl/itemiter.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdoole2.hxx> +#include <tools/UnitConversion.hxx> +#include <editeng/formatbreakitem.hxx> +#include <osl/diagnose.h> +#include <ndtxt.hxx> +#include <ndnotxt.hxx> +#include <fmtcntnt.hxx> +#include <swtable.hxx> +#include <frmfmt.hxx> +#include <flypos.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <SwStyleNameMapper.hxx> +#include <docary.hxx> +#include <charfmt.hxx> +#include <fchrfmt.hxx> +#include <redline.hxx> +#include "types.hxx" +#include <svtools/embedhlp.hxx> +#include <numrule.hxx> +#include <vcl/svapp.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IMark.hxx> +#include <grfatr.hxx> + +using namespace com::sun::star; + +namespace +{ + // #i98791# - adjust sorting + // Utility to sort SwTextFormatColl's by their assigned outline style list level + class outlinecmp + { + public: + bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const + { + // #i98791# + bool bResult( false ); + const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() ); + const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() ); + if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle ) + { + bResult = bIsBAssignedToOutlineStyle; + } + else if ( !bIsAAssignedToOutlineStyle ) + { + // pA and pB are equal regarding the sorting criteria. + // Thus return value does not matter. + bResult = false; + } + else + { + bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel(); + } + + return bResult; + } + }; + + bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId) + { + return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId); + } + + /* + Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames + + The crucial thing is that a ww8::Frame always has an anchor which + points to some content in the document. This is a requirement of exporting + to Word + */ + ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys) + { + ww8::Frames aRet; + + for(const auto& rpFly : rFlys) + { + const SwFrameFormat &rEntry = rpFly->GetFormat(); + + if (const SwPosition* pAnchor = rEntry.GetAnchor().GetContentAnchor()) + { + // the anchor position will be invalidated by SetRedlineFlags + // so set a dummy position and fix it in UpdateFramePositions + SwPosition const dummy(SwNodeIndex( + const_cast<SwNodes&>(pAnchor->nNode.GetNodes()))); + aRet.emplace_back(rEntry, dummy); + } + else + { + SwPosition aPos(rpFly->GetNdIndex()); + + if (SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode()) + { + aPos.nContent.Assign(pTextNd, 0); + } + + aRet.emplace_back(rEntry, aPos); + } + } + return aRet; + } + + //Utility to test if a frame is anchored at a given node index + class anchoredto + { + private: + SwNodeOffset mnNode; + public: + explicit anchoredto(SwNodeOffset nNode) : mnNode(nNode) {} + bool operator()(const ww8::Frame &rFrame) const + { + return (mnNode == rFrame.GetPosition().nNode.GetNode().GetIndex()); + } + }; +} + +namespace ww8 +{ + //For i120928,size conversion before exporting graphic of bullet + Frame::Frame(const Graphic &rGrf, const SwPosition &rPos) + : mpFlyFrame(nullptr) + , maPos(rPos) + , meWriterType(eBulletGrf) + , mpStartFrameContent(nullptr) + , mbIsInline(true) + , mbForBullet(true) + , maGrf(rGrf) + { + const MapMode aMap100mm( MapUnit::Map100thMM ); + Size aSize( rGrf.GetPrefSize() ); + if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() ) + { + aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm ); + } + else + { + aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm ); + } + maSize = aSize; + maLayoutSize = maSize; + } + + Frame::Frame(const SwFrameFormat &rFormat, const SwPosition &rPos) + : mpFlyFrame(&rFormat) + , maPos(rPos) + , meWriterType(eTextBox) + , mpStartFrameContent(nullptr) + // #i43447# - move to initialization list + , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) ) + // #i120928# - handle graphic of bullet within existing implementation + , mbForBullet(false) + { + switch (rFormat.Which()) + { + case RES_FLYFRMFMT: + if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx()) + { + SwNodeIndex aIdx(*pIdx, 1); + const SwNode &rNd = aIdx.GetNode(); + // #i43447# - determine layout size + { + SwRect aLayRect( rFormat.FindLayoutRect() ); + tools::Rectangle aRect( aLayRect.SVRect() ); + // The Object is not rendered (e.g. something in unused + // header/footer) - thus, get the values from the format. + if ( aLayRect.IsEmpty() ) + { + aRect.SetSize( rFormat.GetFrameSize().GetSize() ); + } + maLayoutSize = aRect.GetSize(); + } + switch (rNd.GetNodeType()) + { + case SwNodeType::Grf: + meWriterType = eGraphic; + maSize = rNd.GetNoTextNode()->GetTwipSize(); + break; + case SwNodeType::Ole: + meWriterType = eOle; + maSize = rNd.GetNoTextNode()->GetTwipSize(); + break; + default: + meWriterType = eTextBox; + // #i43447# - Size equals layout size for text boxes + maSize = maLayoutSize; + break; + } + mpStartFrameContent = &rNd; + } + else + { + OSL_ENSURE(false, "Impossible"); + meWriterType = eTextBox; + } + break; + default: + if (const SdrObject* pObj = rFormat.FindRealSdrObject()) + { + if (pObj->GetObjInventor() == SdrInventor::FmForm) + meWriterType = eFormControl; + else + meWriterType = eDrawing; + maSize = pObj->GetSnapRect().GetSize(); + maLayoutSize = maSize; + } + else + { + OSL_ENSURE(false, "Impossible"); + meWriterType = eDrawing; + } + break; + } + } + + + void Frame::ForceTreatAsInline() + { + mbIsInline = true; + } +} + +namespace sw +{ + namespace hack + { + + sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool, + const SfxItemPool &rSrcPool, sal_uInt16 nWhich) + { + sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich); + if (IsValidSlotWhich(nSlotId, nWhich)) + nWhich = rDestPool.GetWhich(nSlotId); + else + nWhich = 0; + return nWhich; + } + + sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet, + const SwDoc &rDoc, sal_uInt16 nWhich) + { + if (RES_WHICHHINT_END < rSet.GetRanges()[0].first) + { + nWhich = TransformWhichBetweenPools(*rSet.GetPool(), + rDoc.GetAttrPool(), nWhich); + } + return nWhich; + } + + DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj, + SfxObjectShell &rPers) + : mxIPRef(rObj.GetObjRef()), mrPers(rPers), + mpGraphic( rObj.GetGraphic() ) + { + rObj.AbandonObject(); + } + + bool DrawingOLEAdaptor::TransferToDoc( OUString &rName ) + { + OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc"); + if (!mxIPRef.is()) + return false; + + uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( mrPers.GetModel() ); + + bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName ); + if (bSuccess) + { + if ( mpGraphic ) + ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic, + mrPers.GetEmbeddedObjectContainer(), + rName, + OUString() ); + + mxIPRef = nullptr; + } + + return bSuccess; + } + + DrawingOLEAdaptor::~DrawingOLEAdaptor() + { + if (!mxIPRef.is()) + return; + + OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" ); + try + { + mxIPRef->close(true); + } + catch ( const css::util::CloseVetoException& ) + { + } + + mxIPRef = nullptr; + } + } + + namespace util + { + SwTwips MakeSafePositioningValue(SwTwips nIn) + { + if (nIn > SHRT_MAX) + nIn = SHRT_MAX; + else if (nIn < SHRT_MIN) + nIn = SHRT_MIN; + return nIn; + } + + void SetLayer::SendObjectToHell(SdrObject &rObject) const + { + SetObjectLayer(rObject, eHell); + } + + void SetLayer::SendObjectToHeaven(SdrObject &rObject) const + { + SetObjectLayer(rObject, eHeaven); + } + + void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const + { + if (SdrInventor::FmForm == rObject.GetObjInventor()) + rObject.SetLayer(mnFormLayer); + else + { + switch (eLayer) + { + case eHeaven: + rObject.SetLayer(mnHeavenLayer); + break; + case eHell: + rObject.SetLayer(mnHellLayer); + break; + } + } + } + + //SetLayer boilerplate begin + + // #i38889# - by default put objects into the invisible layers. + SetLayer::SetLayer(const SwDoc &rDoc) + : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()), + mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()), + mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId()) + { + } + //SetLayer boilerplate end + + void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet ) + { + if( bExportParentItemSet ) + { + sal_uInt16 nTotal = rSet.TotalCount(); + for( sal_uInt16 nItem =0; nItem < nTotal; ++nItem ) + { + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( rSet.GetWhichByPos( nItem ), true, &pItem ) ) + { + rItems[pItem->Which()] = pItem; + } + } + } + else if( rSet.Count()) + { + SfxItemIter aIter(rSet); + if (const SfxPoolItem *pItem = aIter.GetCurItem()) + { + do + rItems[pItem->Which()] = pItem; + while ((pItem = aIter.NextItem())); + } + } + } + + const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems, + sal_uInt16 eType) + { + auto aIter = rItems.find(eType); + if (aIter != rItems.end()) + return aIter->second; + return nullptr; + } + + void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet) + { + if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat()) + { + if (pCharFormat->GetAttrSet().Count()) + { + SfxItemIter aIter(pCharFormat->GetAttrSet()); + const SfxPoolItem *pItem = aIter.GetCurItem(); + do + rSet.ClearItem(pItem->Which()); + while ((pItem = aIter.NextItem())); + } + } + } + + ww8::ParaStyles GetParaStyles(const SwDoc &rDoc) + { + ww8::ParaStyles aStyles; + typedef ww8::ParaStyles::size_type mysizet; + + const SwTextFormatColls *pColls = rDoc.GetTextFormatColls(); + mysizet nCount = pColls ? pColls->size() : 0; + aStyles.reserve(nCount); + for (mysizet nI = 0; nI < nCount; ++nI) + aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]); + return aStyles; + } + + SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName) + { + // Search first in the Doc-Styles + SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName); + if (!pColl) + { + // Collection not found, try in Pool ? + sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName, + SwGetPoolIdFromName::TxtColl); + if (n != SAL_MAX_UINT16) // found or standard + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false); + } + return pColl; + } + + SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName) + { + SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName); + if (!pFormat) + { + // Collection not found, try in Pool ? + sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName, + SwGetPoolIdFromName::ChrFmt); + if (n != SAL_MAX_UINT16) // found or standard + pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n); + } + return pFormat; + } + + // #i98791# - adjust sorting algorithm + void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles) + { + std::sort(rStyles.begin(), rStyles.end(), outlinecmp()); + } + + /* + Utility to extract FlyFormats from a document, potentially from a + selection. + */ + ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/) + { + SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, true)); + ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys)); + return aRet; + } + + void UpdateFramePositions(ww8::Frames & rFrames) + { + for (ww8::Frame & rFrame : rFrames) + { + SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor(); + if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor()) + { + rFrame.SetPosition(*pAnchor); + } + else + { // these don't need to be corrected, they're not in redlines + assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()); + } + } + } + + ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode) + { + ww8::Frames aRet; + std::copy_if(rFrames.begin(), rFrames.end(), + std::back_inserter(aRet), anchoredto(rNode.GetIndex())); + return aRet; + } + + const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule, + int nLevel) + { + if (nLevel < 0 || nLevel >= MAXLEVEL) + { + OSL_FAIL("Invalid level"); + return nullptr; + } + return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) )); + } + + const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode) + { + const SwNumRule *pRule = nullptr; + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetNumRule()) + ) + { + return GetNumFormatFromSwNumRuleLevel(*pRule, + rTextNode.GetActualListLevel()); + } + + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetDoc().GetOutlineNumRule()) + ) + { + return GetNumFormatFromSwNumRuleLevel(*pRule, + rTextNode.GetActualListLevel()); + } + + return nullptr; + } + + const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode) + { + return GetNormalNumRuleFromTextNode(rTextNode); + } + + const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode) + { + const SwNumRule *pRule = nullptr; + + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetNumRule()) + ) + { + return pRule; + } + return nullptr; + } + + SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat) + { + const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx(); + OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious"); + if (!pIndex) + return nullptr; + SwNodeIndex aIdx(*pIndex, 1); + return aIdx.GetNode().GetNoTextNode(); + } + + bool HasPageBreak(const SwNode &rNd) + { + const SvxFormatBreakItem *pBreak = nullptr; + if (rNd.IsTableNode() && rNd.GetTableNode()) + { + const SwTable& rTable = rNd.GetTableNode()->GetTable(); + const SwFrameFormat* pApply = rTable.GetFrameFormat(); + OSL_ENSURE(pApply, "impossible"); + if (pApply) + pBreak = &pApply->GetFormatAttr(RES_BREAK); + } + else if (const SwContentNode *pNd = rNd.GetContentNode()) + pBreak = &pNd->GetAttr(RES_BREAK); + + return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore; + } + + tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly) + { + if(1 == rPolyPoly.Count()) + { + return rPolyPoly[0]; + } + else + { + // This method will now just concatenate the polygons contained + // in the given PolyPolygon. Anything else which might be thought of + // for reducing to a single polygon will just need more power and + // cannot create more correct results. + sal_uInt32 nPointCount(0); + sal_uInt16 a; + + for(a = 0; a < rPolyPoly.Count(); a++) + { + nPointCount += static_cast<sal_uInt32>(rPolyPoly[a].GetSize()); + } + + if(nPointCount > 0x0000ffff) + { + OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)"); + nPointCount = 0x0000ffff; + } + + tools::Polygon aRetval(o3tl::narrowing<sal_uInt16>(nPointCount)); + sal_uInt32 nAppendIndex(0); + + for(a = 0; a < rPolyPoly.Count(); a++) + { + const tools::Polygon& rCandidate = rPolyPoly[a]; + + for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++) + { + aRetval[o3tl::narrowing<sal_uInt16>(nAppendIndex++)] = rCandidate[b]; + } + } + + return aRetval; + } + } + + tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop) + { + tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly)); + const Size &rOrigSize = pNd->GetGraphic().GetPrefSize(); + + const SwAttrSet* pAttrSet = pNd->GetpSwAttrSet(); + if (bCorrectCrop && pAttrSet) + { + if (pAttrSet->HasItem(RES_GRFATR_CROPGRF)) + { + // Word's wrap polygon deals with a canvas which has the size of the already + // cropped graphic, do the opposite of correctCrop() in writerfilter/. + const SwCropGrf& rCrop = pAttrSet->GetCropGrf(); + sal_Int32 nCropLeft = convertTwipToMm100(rCrop.GetLeft()); + sal_Int32 nCropRight = convertTwipToMm100(rCrop.GetRight()); + sal_Int32 nCropTop = convertTwipToMm100(rCrop.GetTop()); + sal_Int32 nCropBottom = convertTwipToMm100(rCrop.GetBottom()); + aPoly.Move(-nCropLeft, -nCropTop); + + Fraction aScaleX(rOrigSize.getWidth(), rOrigSize.getWidth() - nCropLeft - nCropRight); + Fraction aScaleY(rOrigSize.getHeight(), rOrigSize.getHeight() - nCropTop - nCropBottom); + aPoly.Scale(double(aScaleX), double(aScaleY)); + } + } + + Fraction aMapPolyX(ww::nWrap100Percent, rOrigSize.Width()); + Fraction aMapPolyY(ww::nWrap100Percent, rOrigSize.Height()); + aPoly.Scale(double(aMapPolyX), double(aMapPolyY)); + + /* + a) stretch right bound by 15twips + b) shrink bottom bound to where it would have been in word + c) Move it to the left by 15twips + + See the import for details + */ + const Size &rSize = pNd->GetTwipSize(); + Fraction aMoveHack(ww::nWrap100Percent, rSize.Width()); + aMoveHack *= Fraction(15, 1); + tools::Long nMove(aMoveHack); + + Fraction aHackX(ww::nWrap100Percent + nMove, + ww::nWrap100Percent); + Fraction aHackY(ww::nWrap100Percent - nMove, + ww::nWrap100Percent); + aPoly.Scale(double(aHackX), double(aHackY)); + + aPoly.Move(-nMove, 0); + return aPoly; + } + + void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr) + { + OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline"); + maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone()))); + } + + namespace { + + class SameOpenRedlineType + { + private: + RedlineType meType; + public: + explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {} + bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const + { + const SwFltRedline *pTest = static_cast<const SwFltRedline *> + (pEntry->m_pAttr.get()); + return (pEntry->m_bOpen && (pTest->m_eType == meType)); + } + }; + + } + + bool RedlineStack::close(const SwPosition& rPos, RedlineType eType) + { + //Search from end for same type + auto aResult = std::find_if(maStack.rbegin(), maStack.rend(), + SameOpenRedlineType(eType)); + if (aResult != maStack.rend()) + { + SwTextNode *const pNode(rPos.nNode.GetNode().GetTextNode()); + sal_Int32 const nIndex(rPos.nContent.GetIndex()); + // HACK to prevent overlap of field-mark and redline, + // which would destroy field-mark invariants when the redline + // is hidden: move the redline end one to the left + if (pNode && nIndex > 0 + && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND) + { + SwPosition const end(*rPos.nNode.GetNode().GetTextNode(), + nIndex - 1); + sw::mark::IFieldmark *const pFieldMark( + rPos.GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(end)); + SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark"); + if (pFieldMark && pFieldMark->GetMarkPos().nNode.GetIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1 + && pFieldMark->GetMarkPos().nContent.GetIndex() < (*aResult)->m_aMkPos.m_nContent) + { + (*aResult)->SetEndPos(end); + return true; + } + } + (*aResult)->SetEndPos(rPos); + return true; + } + return false; + } + + void RedlineStack::closeall(const SwPosition& rPos) + { + std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos)); + } + + void MoveAttrFieldmarkInserted(SwFltPosition& rMkPos, SwFltPosition& rPtPos, const SwPosition& rPos) + { + sal_Int32 const nInserted = 2; // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP + SwNodeOffset nPosNd = rPos.nNode.GetIndex(); + sal_Int32 nPosCt = rPos.nContent.GetIndex() - nInserted; + + bool const isPoint(rMkPos == rPtPos); + if ((rMkPos.m_nNode.GetIndex()+1 == nPosNd) && + (nPosCt <= rMkPos.m_nContent)) + { + rMkPos.m_nContent += nInserted; + SAL_WARN_IF(rMkPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(), + "sw.ww8", "redline ends after end of line"); + if (isPoint) // sigh ... important special case... + { + rPtPos.m_nContent += nInserted; + return; + } + } + // for the end position, leave it alone if it's *on* the dummy + // char position, that should remain *before* + if ((rPtPos.m_nNode.GetIndex()+1 == nPosNd) && + (nPosCt < rPtPos.m_nContent)) + { + rPtPos.m_nContent += nInserted; + SAL_WARN_IF(rPtPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(), + "sw.ww8", "range ends after end of line"); + } + } + + void RedlineStack::MoveAttrsFieldmarkInserted(const SwPosition& rPos) + { + for (size_t i = 0, nCnt = maStack.size(); i < nCnt; ++i) + { + SwFltStackEntry& rEntry = *maStack[i]; + MoveAttrFieldmarkInserted(rEntry.m_aMkPos, rEntry.m_aPtPos, rPos); + } + } + + void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry) + { + SwPaM aRegion(pEntry->m_aMkPos.m_nNode); + if (pEntry->MakeRegion(mrDoc, aRegion, + SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) && + (*aRegion.GetPoint() != *aRegion.GetMark()) + ) + { + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | + RedlineFlags::ShowDelete); + const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*> + (pEntry->m_pAttr.get()); + + SwRedlineData aData(pFltRedline->m_eType, pFltRedline->m_nAutorNo, + pFltRedline->m_aStamp, OUString(), nullptr); + + SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion)); + // the point node may be deleted in AppendRedline, so park + // the PaM somewhere safe + aRegion.DeleteMark(); + *aRegion.GetPoint() = SwPosition(SwNodeIndex(mrDoc.GetNodes())); + mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true); + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert | + RedlineFlags::ShowDelete ); + } + pEntry.reset(); + } + + bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE, + const std::unique_ptr<SwFltStackEntry> & pTwoE) const + { + const SwFltRedline *pOne= static_cast<const SwFltRedline*> + (pOneE->m_pAttr.get()); + const SwFltRedline *pTwo= static_cast<const SwFltRedline*> + (pTwoE->m_pAttr.get()); + + //Return the earlier time, if two have the same time, prioritize + //inserts over deletes + if (pOne->m_aStamp == pTwo->m_aStamp) + return (pOne->m_eType == RedlineType::Insert && pTwo->m_eType != RedlineType::Insert); + else + return (pOne->m_aStamp < pTwo->m_aStamp); + } + + RedlineStack::~RedlineStack() + { + std::stable_sort(maStack.begin(), maStack.end(), CompareRedlines()); + std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc)); + } + + sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm ) + { + sal_uInt16 nRet; + auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm); + if (aIter != maAuthors.end()) + nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin()); + else + { + nRet = static_cast< sal_uInt16 >(maAuthors.size()); + maAuthors.push_back(rNm); + } + return nRet; + } + } + + namespace util + { + InsertedTableListener::InsertedTableListener(SwTableNode& rNode) + : m_pTableNode(&rNode) + { + StartListening(rNode.GetNotifier()); + } + + SwTableNode* InsertedTableListener::GetTableNode() + { return m_pTableNode; } + + void InsertedTableListener::Notify(const SfxHint& rHint) + { + if(rHint.GetId() == SfxHintId::Dying) + m_pTableNode = nullptr; + } + + InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc) + : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()) + { } + + void InsertedTablesManager::DelAndMakeTableFrames() + { + if (!mbHasRoot) + return; + for (auto& aTable : maTables) + { + // If already a layout exists, then the BoxFrames must recreated at this table + SwTableNode *pTable = aTable.first->GetTableNode(); + OSL_ENSURE(pTable, "Why no expected table"); + if (pTable) + { + SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat(); + + if (pFrameFormat != nullptr) + { + SwNodeIndex *pIndex = aTable.second; + pTable->DelFrames(); + pTable->MakeOwnFrames(pIndex); + } + } + } + } + + void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM) + { + if (!mbHasRoot) + return; + //Associate this tablenode with this after position, replace an //old + //node association if necessary + maTables.emplace( + std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)), + &(rPaM.GetPoint()->nNode)); + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |