Revert "tdf#158280 Replace usage of InputDialog with SvxNameDialog"
[LibreOffice.git] / sw / source / filter / ww8 / writerhelper.cxx
blobb8a47b329245e03b0bf0a6df49a5ee14de1aa452
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <com/sun/star/util/CloseVetoException.hpp>
25 #include <doc.hxx>
26 #include "writerhelper.hxx"
27 #include <msfilter.hxx>
28 #include <com/sun/star/container/XChild.hpp>
30 #include <algorithm>
31 #include <svl/itemiter.hxx>
32 #include <svx/svdobj.hxx>
33 #include <svx/svdoole2.hxx>
34 #include <tools/UnitConversion.hxx>
35 #include <editeng/formatbreakitem.hxx>
36 #include <osl/diagnose.h>
37 #include <ndtxt.hxx>
38 #include <ndnotxt.hxx>
39 #include <fmtcntnt.hxx>
40 #include <swtable.hxx>
41 #include <frmfmt.hxx>
42 #include <flypos.hxx>
43 #include <fmtanchr.hxx>
44 #include <fmtfsize.hxx>
45 #include <SwStyleNameMapper.hxx>
46 #include <docary.hxx>
47 #include <charfmt.hxx>
48 #include <fchrfmt.hxx>
49 #include <redline.hxx>
50 #include "types.hxx"
51 #include <svtools/embedhlp.hxx>
52 #include <numrule.hxx>
53 #include <utility>
54 #include <vcl/svapp.hxx>
55 #include <IDocumentDrawModelAccess.hxx>
56 #include <IDocumentLayoutAccess.hxx>
57 #include <IDocumentRedlineAccess.hxx>
58 #include <IDocumentStylePoolAccess.hxx>
59 #include <IDocumentMarkAccess.hxx>
60 #include <IMark.hxx>
61 #include <grfatr.hxx>
62 #include <poolfmt.hxx>
64 using namespace com::sun::star;
66 namespace
68 // #i98791# - adjust sorting
69 // Utility to sort SwTextFormatColl's by their assigned outline style list level
70 class outlinecmp
72 public:
73 bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const
75 // #i98791#
76 bool bResult( false );
77 const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() );
78 const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() );
79 if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle )
81 bResult = bIsBAssignedToOutlineStyle;
83 else if ( !bIsAAssignedToOutlineStyle )
85 // pA and pB are equal regarding the sorting criteria.
86 // Thus return value does not matter.
87 bResult = false;
89 else
91 bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel();
94 return bResult;
98 bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId)
100 return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId);
104 Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames
106 The crucial thing is that a ww8::Frame always has an anchor which
107 points to some content in the document. This is a requirement of exporting
108 to Word
110 ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys)
112 ww8::Frames aRet;
114 for(const auto& rFly : rFlys)
116 const SwFrameFormat &rEntry = rFly.GetFormat();
117 const SwFormat* pParent = rEntry.DerivedFrom();
118 const SwFormatAnchor& rAnchor = rEntry.GetAnchor();
119 // keep only Inline Heading frames from the frames anchored as characters
120 bool bAsChar = rAnchor.GetAnchorId() ==
121 static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER);
122 if ( bAsChar &&
123 !(pParent && pParent->GetPoolFormatId() == RES_POOLFRM_INLINE_HEADING) )
125 continue;
127 if (const SwNode* pAnchor = rAnchor.GetAnchorNode())
129 // the anchor position will be invalidated by SetRedlineFlags
130 // so set a dummy position and fix it in UpdateFramePositions
131 SwPosition const dummy(const_cast<SwNodes&>(pAnchor->GetNodes()));
132 aRet.emplace_back(rEntry, dummy);
134 else
136 SwPosition aPos(rFly.GetNode());
137 aRet.emplace_back(rEntry, aPos);
140 return aRet;
143 //Utility to test if a frame is anchored at a given node
144 class anchoredto
146 private:
147 const SwNode& mrNode;
148 public:
149 explicit anchoredto(const SwNode& rNode) : mrNode(rNode) {}
150 bool operator()(const ww8::Frame &rFrame) const
152 return (mrNode == rFrame.GetPosition().GetNode());
157 namespace ww8
159 //For i120928,size conversion before exporting graphic of bullet
160 Frame::Frame(const Graphic &rGrf, SwPosition aPos)
161 : mpFlyFrame(nullptr)
162 , maPos(std::move(aPos))
163 , meWriterType(eBulletGrf)
164 , mpStartFrameContent(nullptr)
165 , mbIsInline(true)
166 , mbForBullet(true)
167 , maGrf(rGrf)
169 const MapMode aMap100mm( MapUnit::Map100thMM );
170 Size aSize( rGrf.GetPrefSize() );
171 if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
173 aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm );
175 else
177 aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm );
179 maSize = aSize;
180 maLayoutSize = maSize;
183 Frame::Frame(const SwFrameFormat &rFormat, SwPosition aPos)
184 : mpFlyFrame(&rFormat)
185 , maPos(std::move(aPos))
186 , meWriterType(eTextBox)
187 , mpStartFrameContent(nullptr)
188 // #i43447# - move to initialization list
189 , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
190 // #i120928# - handle graphic of bullet within existing implementation
191 , mbForBullet(false)
193 switch (rFormat.Which())
195 case RES_FLYFRMFMT:
196 if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx())
198 SwNodeIndex aIdx(*pIdx, 1);
199 const SwNode &rNd = aIdx.GetNode();
200 // #i43447# - determine layout size
202 SwRect aLayRect( rFormat.FindLayoutRect() );
203 tools::Rectangle aRect( aLayRect.SVRect() );
204 // The Object is not rendered (e.g. something in unused
205 // header/footer) - thus, get the values from the format.
206 if ( aLayRect.IsEmpty() )
208 aRect.SetSize( rFormat.GetFrameSize().GetSize() );
210 maLayoutSize = aRect.GetSize();
212 switch (rNd.GetNodeType())
214 case SwNodeType::Grf:
215 meWriterType = eGraphic;
216 maSize = rNd.GetNoTextNode()->GetTwipSize();
217 break;
218 case SwNodeType::Ole:
219 meWriterType = eOle;
220 maSize = rNd.GetNoTextNode()->GetTwipSize();
221 break;
222 default:
223 meWriterType = eTextBox;
224 // #i43447# - Size equals layout size for text boxes
225 maSize = maLayoutSize;
226 break;
228 mpStartFrameContent = &rNd;
230 else
232 OSL_ENSURE(false, "Impossible");
233 meWriterType = eTextBox;
235 break;
236 default:
237 if (const SdrObject* pObj = rFormat.FindRealSdrObject())
239 if (pObj->GetObjInventor() == SdrInventor::FmForm)
240 meWriterType = eFormControl;
241 else
242 meWriterType = eDrawing;
243 maSize = pObj->GetSnapRect().GetSize();
244 maLayoutSize = maSize;
246 else
248 OSL_ENSURE(false, "Impossible");
249 meWriterType = eDrawing;
251 break;
256 void Frame::ForceTreatAsInline()
258 mbIsInline = true;
262 namespace sw
264 namespace hack
267 sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool,
268 const SfxItemPool &rSrcPool, sal_uInt16 nWhich)
270 sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich);
271 if (IsValidSlotWhich(nSlotId, nWhich))
272 nWhich = rDestPool.GetWhichIDFromSlotID(nSlotId);
273 else
274 nWhich = 0;
275 return nWhich;
278 sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet,
279 const SwDoc &rDoc, sal_uInt16 nWhich)
281 if (RES_WHICHHINT_END < rSet.GetRanges()[0].first)
283 nWhich = TransformWhichBetweenPools(*rSet.GetPool(),
284 rDoc.GetAttrPool(), nWhich);
286 return nWhich;
289 DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj,
290 SfxObjectShell &rPers)
291 : mxIPRef(rObj.GetObjRef()), mrPers(rPers),
292 mpGraphic( rObj.GetGraphic() )
294 rObj.AbandonObject();
297 bool DrawingOLEAdaptor::TransferToDoc( OUString &rName )
299 OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc");
300 if (!mxIPRef.is())
301 return false;
303 uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY );
304 if ( xChild.is() )
305 xChild->setParent( mrPers.GetModel() );
307 bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName );
308 if (bSuccess)
310 if ( mpGraphic )
311 ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic,
312 mrPers.GetEmbeddedObjectContainer(),
313 rName,
314 OUString() );
316 mxIPRef = nullptr;
319 return bSuccess;
322 DrawingOLEAdaptor::~DrawingOLEAdaptor()
324 if (!mxIPRef.is())
325 return;
327 OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" );
330 mxIPRef->close(true);
332 catch ( const css::util::CloseVetoException& )
336 mxIPRef = nullptr;
340 namespace util
342 SwTwips MakeSafePositioningValue(SwTwips nIn)
344 if (nIn > SHRT_MAX)
345 nIn = SHRT_MAX;
346 else if (nIn < SHRT_MIN)
347 nIn = SHRT_MIN;
348 return nIn;
351 void SetLayer::SendObjectToHell(SdrObject &rObject) const
353 SetObjectLayer(rObject, eHell);
356 void SetLayer::SendObjectToHeaven(SdrObject &rObject) const
358 SetObjectLayer(rObject, eHeaven);
361 void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const
363 if (SdrInventor::FmForm == rObject.GetObjInventor())
364 rObject.SetLayer(mnFormLayer);
365 else
367 switch (eLayer)
369 case eHeaven:
370 rObject.SetLayer(mnHeavenLayer);
371 break;
372 case eHell:
373 rObject.SetLayer(mnHellLayer);
374 break;
379 //SetLayer boilerplate begin
381 // #i38889# - by default put objects into the invisible layers.
382 SetLayer::SetLayer(const SwDoc &rDoc)
383 : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()),
384 mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()),
385 mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId())
388 //SetLayer boilerplate end
390 void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet )
392 if( bExportParentItemSet )
394 for (SfxItemIter aIter(rSet); !aIter.IsAtEnd(); aIter.NextItem())
396 const SfxPoolItem* pItem(nullptr);
397 if(SfxItemState::SET == aIter.GetItemState(true, &pItem))
398 rItems[aIter.GetCurWhich()] = pItem;
401 else if( rSet.Count())
403 for (SfxItemIter aIter(rSet); !aIter.IsAtEnd(); aIter.NextItem())
404 rItems[aIter.GetCurWhich()] = aIter.GetCurItem();
406 // DeduplicateItems(rItems);
409 const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems,
410 sal_uInt16 eType)
412 auto aIter = rItems.find(eType);
413 if (aIter != rItems.end())
414 return aIter->second;
415 return nullptr;
418 void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet)
420 if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat())
422 if (pCharFormat->GetAttrSet().Count())
424 SfxItemIter aIter(pCharFormat->GetAttrSet());
425 const SfxPoolItem *pItem = aIter.GetCurItem();
427 rSet.ClearItem(pItem->Which());
428 while ((pItem = aIter.NextItem()));
433 ww8::ParaStyles GetParaStyles(const SwDoc &rDoc)
435 ww8::ParaStyles aStyles;
436 typedef ww8::ParaStyles::size_type mysizet;
438 const SwTextFormatColls *pColls = rDoc.GetTextFormatColls();
439 mysizet nCount = pColls ? pColls->size() : 0;
440 aStyles.reserve(nCount);
441 for (mysizet nI = 0; nI < nCount; ++nI)
442 aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]);
443 return aStyles;
446 SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName)
448 // Search first in the Doc-Styles
449 SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName);
450 if (!pColl)
452 // Collection not found, try in Pool ?
453 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
454 SwGetPoolIdFromName::TxtColl);
455 if (n != SAL_MAX_UINT16) // found or standard
456 pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false);
458 return pColl;
461 SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName)
463 SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName);
464 if (!pFormat)
466 // Collection not found, try in Pool ?
467 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
468 SwGetPoolIdFromName::ChrFmt);
469 if (n != SAL_MAX_UINT16) // found or standard
470 pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n);
472 return pFormat;
475 // #i98791# - adjust sorting algorithm
476 void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles)
478 std::sort(rStyles.begin(), rStyles.end(), outlinecmp());
482 Utility to extract FlyFormats from a document, potentially from a
483 selection.
485 ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/)
487 SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, /*bDrawAlso=*/true, /*bAsCharAlso=*/true));
488 ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys));
489 return aRet;
492 void UpdateFramePositions(ww8::Frames & rFrames)
494 for (ww8::Frame & rFrame : rFrames)
496 SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor();
497 if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor())
499 rFrame.SetPosition(*pAnchor);
501 else
502 { // these don't need to be corrected, they're not in redlines
503 assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId());
508 ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode)
510 ww8::Frames aRet;
511 std::copy_if(rFrames.begin(), rFrames.end(),
512 std::back_inserter(aRet), anchoredto(rNode));
513 return aRet;
516 const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule,
517 int nLevel)
519 if (nLevel < 0 || nLevel >= MAXLEVEL)
521 OSL_FAIL("Invalid level");
522 return nullptr;
524 return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) ));
527 const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode)
529 const SwNumRule *pRule = nullptr;
530 if (
531 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
532 nullptr != (pRule = rTextNode.GetNumRule())
535 return GetNumFormatFromSwNumRuleLevel(*pRule,
536 rTextNode.GetActualListLevel());
539 if (
540 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
541 nullptr != (pRule = rTextNode.GetDoc().GetOutlineNumRule())
544 return GetNumFormatFromSwNumRuleLevel(*pRule,
545 rTextNode.GetActualListLevel());
548 return nullptr;
551 const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode)
553 return GetNormalNumRuleFromTextNode(rTextNode);
556 const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode)
558 const SwNumRule *pRule = nullptr;
560 if (
561 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
562 nullptr != (pRule = rTextNode.GetNumRule())
565 return pRule;
567 return nullptr;
570 SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
572 const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx();
573 OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious");
574 if (!pIndex)
575 return nullptr;
576 SwNodeIndex aIdx(*pIndex, 1);
577 return aIdx.GetNode().GetNoTextNode();
580 bool HasPageBreak(const SwNode &rNd)
582 const SvxFormatBreakItem *pBreak = nullptr;
583 if (rNd.IsTableNode() && rNd.GetTableNode())
585 const SwTable& rTable = rNd.GetTableNode()->GetTable();
586 const SwFrameFormat* pApply = rTable.GetFrameFormat();
587 OSL_ENSURE(pApply, "impossible");
588 if (pApply)
589 pBreak = &pApply->GetFormatAttr(RES_BREAK);
591 else if (const SwContentNode *pNd = rNd.GetContentNode())
592 pBreak = &pNd->GetAttr(RES_BREAK);
594 return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore;
597 tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
599 if(1 == rPolyPoly.Count())
601 return rPolyPoly[0];
603 else
605 // This method will now just concatenate the polygons contained
606 // in the given PolyPolygon. Anything else which might be thought of
607 // for reducing to a single polygon will just need more power and
608 // cannot create more correct results.
609 sal_uInt32 nPointCount(0);
611 for( auto const& rPoly : rPolyPoly )
613 nPointCount += static_cast<sal_uInt32>(rPoly.GetSize());
616 if(nPointCount > 0x0000ffff)
618 OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)");
619 nPointCount = 0x0000ffff;
622 tools::Polygon aRetval(o3tl::narrowing<sal_uInt16>(nPointCount));
623 sal_uInt32 nAppendIndex(0);
625 for( auto const& rCandidate : rPolyPoly )
627 for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++)
629 aRetval[o3tl::narrowing<sal_uInt16>(nAppendIndex++)] = rCandidate[b];
633 return aRetval;
637 tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop)
639 tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly));
640 const Size aOrigSize = pNd->GetGraphic().GetPrefSize();
642 const SwAttrSet* pAttrSet = pNd->GetpSwAttrSet();
643 if (bCorrectCrop && pAttrSet)
645 if (pAttrSet->HasItem(RES_GRFATR_CROPGRF))
647 // Word's wrap polygon deals with a canvas which has the size of the already
648 // cropped graphic, do the opposite of correctCrop() in writerfilter/.
649 const SwCropGrf& rCrop = pAttrSet->GetCropGrf();
650 sal_Int32 nCropLeft = convertTwipToMm100(rCrop.GetLeft());
651 sal_Int32 nCropRight = convertTwipToMm100(rCrop.GetRight());
652 sal_Int32 nCropTop = convertTwipToMm100(rCrop.GetTop());
653 sal_Int32 nCropBottom = convertTwipToMm100(rCrop.GetBottom());
654 aPoly.Move(-nCropLeft, -nCropTop);
656 Fraction aScaleX(aOrigSize.getWidth(), aOrigSize.getWidth() - nCropLeft - nCropRight);
657 Fraction aScaleY(aOrigSize.getHeight(), aOrigSize.getHeight() - nCropTop - nCropBottom);
658 aPoly.Scale(double(aScaleX), double(aScaleY));
662 Fraction aMapPolyX(ww::nWrap100Percent, aOrigSize.Width());
663 Fraction aMapPolyY(ww::nWrap100Percent, aOrigSize.Height());
664 aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
667 a) stretch right bound by 15twips
668 b) shrink bottom bound to where it would have been in word
669 c) Move it to the left by 15twips
671 See the import for details
673 const Size aSize = pNd->GetTwipSize();
674 Fraction aMoveHack(ww::nWrap100Percent, aSize.Width());
675 aMoveHack *= Fraction(15, 1);
676 tools::Long nMove(aMoveHack);
678 Fraction aHackX(ww::nWrap100Percent + nMove,
679 ww::nWrap100Percent);
680 Fraction aHackY(ww::nWrap100Percent - nMove,
681 ww::nWrap100Percent);
682 aPoly.Scale(double(aHackX), double(aHackY));
684 aPoly.Move(-nMove, 0);
685 return aPoly;
688 void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr)
690 OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline");
691 maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone())));
694 namespace {
696 class SameOpenRedlineType
698 private:
699 RedlineType meType;
700 public:
701 explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {}
702 bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const
704 const SwFltRedline *pTest = static_cast<const SwFltRedline *>
705 (pEntry->m_pAttr.get());
706 return (pEntry->m_bOpen && (pTest->m_eType == meType));
712 bool RedlineStack::close(const SwPosition& rPos, RedlineType eType)
714 //Search from end for same type
715 auto aResult = std::find_if(maStack.rbegin(), maStack.rend(),
716 SameOpenRedlineType(eType));
717 if (aResult != maStack.rend())
719 SwTextNode *const pNode(rPos.GetNode().GetTextNode());
720 sal_Int32 const nIndex(rPos.GetContentIndex());
721 // HACK to prevent overlap of field-mark and redline,
722 // which would destroy field-mark invariants when the redline
723 // is hidden: move the redline end one to the left
724 if (pNode && nIndex > 0
725 && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND)
727 SwPosition const end(*rPos.GetNode().GetTextNode(),
728 nIndex - 1);
729 sw::mark::Fieldmark *const pFieldMark(
730 rPos.GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(end));
731 SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark");
732 if (pFieldMark && pFieldMark->GetMarkPos().GetNodeIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1
733 && pFieldMark->GetMarkPos().GetContentIndex() < (*aResult)->m_aMkPos.m_nContent)
735 (*aResult)->SetEndPos(end);
736 return true;
739 (*aResult)->SetEndPos(rPos);
740 return true;
742 return false;
745 void RedlineStack::closeall(const SwPosition& rPos)
747 std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos));
750 void MoveAttrFieldmarkInserted(SwFltPosition& rMkPos, SwFltPosition& rPtPos, const SwPosition& rPos)
752 sal_Int32 const nInserted = 2; // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP
753 SwNodeOffset nPosNd = rPos.GetNodeIndex();
754 sal_Int32 nPosCt = rPos.GetContentIndex() - nInserted;
756 bool const isPoint(rMkPos == rPtPos);
757 if ((rMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
758 (nPosCt <= rMkPos.m_nContent))
760 rMkPos.m_nContent += nInserted;
761 SAL_WARN_IF(rMkPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
762 "sw.ww8", "redline ends after end of line");
763 if (isPoint) // sigh ... important special case...
765 rPtPos.m_nContent += nInserted;
766 return;
769 // for the end position, leave it alone if it's *on* the dummy
770 // char position, that should remain *before*
771 if ((rPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
772 (nPosCt < rPtPos.m_nContent))
774 rPtPos.m_nContent += nInserted;
775 SAL_WARN_IF(rPtPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
776 "sw.ww8", "range ends after end of line");
780 void RedlineStack::MoveAttrsFieldmarkInserted(const SwPosition& rPos)
782 for (size_t i = 0, nCnt = maStack.size(); i < nCnt; ++i)
784 SwFltStackEntry& rEntry = *maStack[i];
785 MoveAttrFieldmarkInserted(rEntry.m_aMkPos, rEntry.m_aPtPos, rPos);
789 void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry)
791 SwPaM aRegion(pEntry->m_aMkPos.m_nNode);
792 if (pEntry->MakeRegion(aRegion,
793 SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) &&
794 (*aRegion.GetPoint() != *aRegion.GetMark())
797 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert |
798 RedlineFlags::ShowDelete);
799 const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*>
800 (pEntry->m_pAttr.get());
802 SwRedlineData aData(pFltRedline->m_eType, pFltRedline->m_nAutorNo,
803 pFltRedline->m_aStamp, 0, OUString(), nullptr);
805 SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion));
806 // the point node may be deleted in AppendRedline, so park
807 // the PaM somewhere safe
808 aRegion.DeleteMark();
809 aRegion.GetPoint()->Assign(*mrDoc.GetNodes()[SwNodeOffset(0)]);
810 mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
811 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert |
812 RedlineFlags::ShowDelete );
814 pEntry.reset();
817 bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE,
818 const std::unique_ptr<SwFltStackEntry> & pTwoE) const
820 const SwFltRedline *pOne= static_cast<const SwFltRedline*>
821 (pOneE->m_pAttr.get());
822 const SwFltRedline *pTwo= static_cast<const SwFltRedline*>
823 (pTwoE->m_pAttr.get());
825 //Return the earlier time, if two have the same time, prioritize
826 //inserts over deletes
827 if (pOne->m_aStamp == pTwo->m_aStamp)
828 return (pOne->m_eType == RedlineType::Insert && pTwo->m_eType != RedlineType::Insert);
829 else
830 return (pOne->m_aStamp < pTwo->m_aStamp);
833 void RedlineStack::ImplDestroy()
835 std::stable_sort(maStack.begin(), maStack.end(), CompareRedlines());
836 std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc));
839 RedlineStack::~RedlineStack()
841 suppress_fun_call_w_exception(ImplDestroy());
844 sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm )
846 sal_uInt16 nRet;
847 auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm);
848 if (aIter != maAuthors.end())
849 nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin());
850 else
852 nRet = static_cast< sal_uInt16 >(maAuthors.size());
853 maAuthors.push_back(rNm);
855 return nRet;
859 namespace util
861 InsertedTableListener::InsertedTableListener(SwTableNode& rNode)
862 : m_pTableNode(&rNode)
864 StartListening(rNode.GetNotifier());
867 SwTableNode* InsertedTableListener::GetTableNode()
868 { return m_pTableNode; }
870 void InsertedTableListener::Notify(const SfxHint& rHint)
872 if(rHint.GetId() == SfxHintId::Dying)
873 m_pTableNode = nullptr;
876 InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc)
877 : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout())
880 void InsertedTablesManager::DelAndMakeTableFrames()
882 if (!mbHasRoot)
883 return;
884 for (auto& aTable : maTables)
886 // If already a layout exists, then the BoxFrames must recreated at this table
887 SwTableNode *pTable = aTable.first->GetTableNode();
888 OSL_ENSURE(pTable, "Why no expected table");
889 if (pTable)
891 SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat();
893 if (pFrameFormat != nullptr)
895 SwPosition *pIndex = aTable.second;
896 pTable->DelFrames();
897 pTable->MakeOwnFrames(pIndex);
903 void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM)
905 if (!mbHasRoot)
906 return;
907 //Associate this tablenode with this after position, replace an old
908 //node association if necessary
909 maTables.emplace(
910 std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)),
911 rPaM.GetPoint());
917 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */