android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / writerhelper.cxx
blob5a45a6dbb04b358fe369a74bfb1c6aa1aca60fef
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>
63 using namespace com::sun::star;
65 namespace
67 // #i98791# - adjust sorting
68 // Utility to sort SwTextFormatColl's by their assigned outline style list level
69 class outlinecmp
71 public:
72 bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const
74 // #i98791#
75 bool bResult( false );
76 const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() );
77 const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() );
78 if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle )
80 bResult = bIsBAssignedToOutlineStyle;
82 else if ( !bIsAAssignedToOutlineStyle )
84 // pA and pB are equal regarding the sorting criteria.
85 // Thus return value does not matter.
86 bResult = false;
88 else
90 bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel();
93 return bResult;
97 bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId)
99 return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId);
103 Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames
105 The crucial thing is that a ww8::Frame always has an anchor which
106 points to some content in the document. This is a requirement of exporting
107 to Word
109 ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys)
111 ww8::Frames aRet;
113 for(const auto& rFly : rFlys)
115 const SwFrameFormat &rEntry = rFly.GetFormat();
117 if (const SwNode* pAnchor = rEntry.GetAnchor().GetAnchorNode())
119 // the anchor position will be invalidated by SetRedlineFlags
120 // so set a dummy position and fix it in UpdateFramePositions
121 SwPosition const dummy(const_cast<SwNodes&>(pAnchor->GetNodes()));
122 aRet.emplace_back(rEntry, dummy);
124 else
126 SwPosition aPos(rFly.GetNode());
127 aRet.emplace_back(rEntry, aPos);
130 return aRet;
133 //Utility to test if a frame is anchored at a given node
134 class anchoredto
136 private:
137 const SwNode& mrNode;
138 public:
139 explicit anchoredto(const SwNode& rNode) : mrNode(rNode) {}
140 bool operator()(const ww8::Frame &rFrame) const
142 return (mrNode == rFrame.GetPosition().GetNode());
147 namespace ww8
149 //For i120928,size conversion before exporting graphic of bullet
150 Frame::Frame(const Graphic &rGrf, SwPosition aPos)
151 : mpFlyFrame(nullptr)
152 , maPos(std::move(aPos))
153 , meWriterType(eBulletGrf)
154 , mpStartFrameContent(nullptr)
155 , mbIsInline(true)
156 , mbForBullet(true)
157 , maGrf(rGrf)
159 const MapMode aMap100mm( MapUnit::Map100thMM );
160 Size aSize( rGrf.GetPrefSize() );
161 if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
163 aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm );
165 else
167 aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm );
169 maSize = aSize;
170 maLayoutSize = maSize;
173 Frame::Frame(const SwFrameFormat &rFormat, SwPosition aPos)
174 : mpFlyFrame(&rFormat)
175 , maPos(std::move(aPos))
176 , meWriterType(eTextBox)
177 , mpStartFrameContent(nullptr)
178 // #i43447# - move to initialization list
179 , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
180 // #i120928# - handle graphic of bullet within existing implementation
181 , mbForBullet(false)
183 switch (rFormat.Which())
185 case RES_FLYFRMFMT:
186 if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx())
188 SwNodeIndex aIdx(*pIdx, 1);
189 const SwNode &rNd = aIdx.GetNode();
190 // #i43447# - determine layout size
192 SwRect aLayRect( rFormat.FindLayoutRect() );
193 tools::Rectangle aRect( aLayRect.SVRect() );
194 // The Object is not rendered (e.g. something in unused
195 // header/footer) - thus, get the values from the format.
196 if ( aLayRect.IsEmpty() )
198 aRect.SetSize( rFormat.GetFrameSize().GetSize() );
200 maLayoutSize = aRect.GetSize();
202 switch (rNd.GetNodeType())
204 case SwNodeType::Grf:
205 meWriterType = eGraphic;
206 maSize = rNd.GetNoTextNode()->GetTwipSize();
207 break;
208 case SwNodeType::Ole:
209 meWriterType = eOle;
210 maSize = rNd.GetNoTextNode()->GetTwipSize();
211 break;
212 default:
213 meWriterType = eTextBox;
214 // #i43447# - Size equals layout size for text boxes
215 maSize = maLayoutSize;
216 break;
218 mpStartFrameContent = &rNd;
220 else
222 OSL_ENSURE(false, "Impossible");
223 meWriterType = eTextBox;
225 break;
226 default:
227 if (const SdrObject* pObj = rFormat.FindRealSdrObject())
229 if (pObj->GetObjInventor() == SdrInventor::FmForm)
230 meWriterType = eFormControl;
231 else
232 meWriterType = eDrawing;
233 maSize = pObj->GetSnapRect().GetSize();
234 maLayoutSize = maSize;
236 else
238 OSL_ENSURE(false, "Impossible");
239 meWriterType = eDrawing;
241 break;
246 void Frame::ForceTreatAsInline()
248 mbIsInline = true;
252 namespace sw
254 namespace hack
257 sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool,
258 const SfxItemPool &rSrcPool, sal_uInt16 nWhich)
260 sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich);
261 if (IsValidSlotWhich(nSlotId, nWhich))
262 nWhich = rDestPool.GetWhich(nSlotId);
263 else
264 nWhich = 0;
265 return nWhich;
268 sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet,
269 const SwDoc &rDoc, sal_uInt16 nWhich)
271 if (RES_WHICHHINT_END < rSet.GetRanges()[0].first)
273 nWhich = TransformWhichBetweenPools(*rSet.GetPool(),
274 rDoc.GetAttrPool(), nWhich);
276 return nWhich;
279 DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj,
280 SfxObjectShell &rPers)
281 : mxIPRef(rObj.GetObjRef()), mrPers(rPers),
282 mpGraphic( rObj.GetGraphic() )
284 rObj.AbandonObject();
287 bool DrawingOLEAdaptor::TransferToDoc( OUString &rName )
289 OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc");
290 if (!mxIPRef.is())
291 return false;
293 uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY );
294 if ( xChild.is() )
295 xChild->setParent( mrPers.GetModel() );
297 bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName );
298 if (bSuccess)
300 if ( mpGraphic )
301 ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic,
302 mrPers.GetEmbeddedObjectContainer(),
303 rName,
304 OUString() );
306 mxIPRef = nullptr;
309 return bSuccess;
312 DrawingOLEAdaptor::~DrawingOLEAdaptor()
314 if (!mxIPRef.is())
315 return;
317 OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" );
320 mxIPRef->close(true);
322 catch ( const css::util::CloseVetoException& )
326 mxIPRef = nullptr;
330 namespace util
332 SwTwips MakeSafePositioningValue(SwTwips nIn)
334 if (nIn > SHRT_MAX)
335 nIn = SHRT_MAX;
336 else if (nIn < SHRT_MIN)
337 nIn = SHRT_MIN;
338 return nIn;
341 void SetLayer::SendObjectToHell(SdrObject &rObject) const
343 SetObjectLayer(rObject, eHell);
346 void SetLayer::SendObjectToHeaven(SdrObject &rObject) const
348 SetObjectLayer(rObject, eHeaven);
351 void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const
353 if (SdrInventor::FmForm == rObject.GetObjInventor())
354 rObject.SetLayer(mnFormLayer);
355 else
357 switch (eLayer)
359 case eHeaven:
360 rObject.SetLayer(mnHeavenLayer);
361 break;
362 case eHell:
363 rObject.SetLayer(mnHellLayer);
364 break;
369 //SetLayer boilerplate begin
371 // #i38889# - by default put objects into the invisible layers.
372 SetLayer::SetLayer(const SwDoc &rDoc)
373 : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()),
374 mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()),
375 mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId())
378 //SetLayer boilerplate end
380 void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet )
382 if( bExportParentItemSet )
384 sal_uInt16 nTotal = rSet.TotalCount();
385 for( sal_uInt16 nItem =0; nItem < nTotal; ++nItem )
387 const SfxPoolItem* pItem = nullptr;
388 if( SfxItemState::SET == rSet.GetItemState( rSet.GetWhichByPos( nItem ), true, &pItem ) )
390 rItems[pItem->Which()] = pItem;
394 else if( rSet.Count())
396 SfxItemIter aIter(rSet);
397 if (const SfxPoolItem *pItem = aIter.GetCurItem())
400 rItems[pItem->Which()] = pItem;
401 while ((pItem = aIter.NextItem()));
404 // DeduplicateItems(rItems);
407 void DeduplicateItems(ww8::PoolItems & rItems)
409 if (rItems.find(RES_CHRATR_WEIGHT) != rItems.end()
410 && rItems.find(RES_CHRATR_CJK_WEIGHT) != rItems.end())
412 // avoid duplicate w:b element (DOC and DOCX map Western and
413 // CJK the same - inconsistently RTF maps CJK and CTL the same?)
414 rItems.erase(rItems.find(RES_CHRATR_CJK_WEIGHT));
418 const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems,
419 sal_uInt16 eType)
421 auto aIter = rItems.find(eType);
422 if (aIter != rItems.end())
423 return aIter->second;
424 return nullptr;
427 void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet)
429 if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat())
431 if (pCharFormat->GetAttrSet().Count())
433 SfxItemIter aIter(pCharFormat->GetAttrSet());
434 const SfxPoolItem *pItem = aIter.GetCurItem();
436 rSet.ClearItem(pItem->Which());
437 while ((pItem = aIter.NextItem()));
442 ww8::ParaStyles GetParaStyles(const SwDoc &rDoc)
444 ww8::ParaStyles aStyles;
445 typedef ww8::ParaStyles::size_type mysizet;
447 const SwTextFormatColls *pColls = rDoc.GetTextFormatColls();
448 mysizet nCount = pColls ? pColls->size() : 0;
449 aStyles.reserve(nCount);
450 for (mysizet nI = 0; nI < nCount; ++nI)
451 aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]);
452 return aStyles;
455 SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName)
457 // Search first in the Doc-Styles
458 SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName);
459 if (!pColl)
461 // Collection not found, try in Pool ?
462 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
463 SwGetPoolIdFromName::TxtColl);
464 if (n != SAL_MAX_UINT16) // found or standard
465 pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false);
467 return pColl;
470 SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName)
472 SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName);
473 if (!pFormat)
475 // Collection not found, try in Pool ?
476 sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
477 SwGetPoolIdFromName::ChrFmt);
478 if (n != SAL_MAX_UINT16) // found or standard
479 pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n);
481 return pFormat;
484 // #i98791# - adjust sorting algorithm
485 void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles)
487 std::sort(rStyles.begin(), rStyles.end(), outlinecmp());
491 Utility to extract FlyFormats from a document, potentially from a
492 selection.
494 ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/)
496 SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, true));
497 ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys));
498 return aRet;
501 void UpdateFramePositions(ww8::Frames & rFrames)
503 for (ww8::Frame & rFrame : rFrames)
505 SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor();
506 if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor())
508 rFrame.SetPosition(*pAnchor);
510 else
511 { // these don't need to be corrected, they're not in redlines
512 assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId());
517 ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode)
519 ww8::Frames aRet;
520 std::copy_if(rFrames.begin(), rFrames.end(),
521 std::back_inserter(aRet), anchoredto(rNode));
522 return aRet;
525 const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule,
526 int nLevel)
528 if (nLevel < 0 || nLevel >= MAXLEVEL)
530 OSL_FAIL("Invalid level");
531 return nullptr;
533 return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) ));
536 const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode)
538 const SwNumRule *pRule = nullptr;
539 if (
540 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
541 nullptr != (pRule = rTextNode.GetNumRule())
544 return GetNumFormatFromSwNumRuleLevel(*pRule,
545 rTextNode.GetActualListLevel());
548 if (
549 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
550 nullptr != (pRule = rTextNode.GetDoc().GetOutlineNumRule())
553 return GetNumFormatFromSwNumRuleLevel(*pRule,
554 rTextNode.GetActualListLevel());
557 return nullptr;
560 const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode)
562 return GetNormalNumRuleFromTextNode(rTextNode);
565 const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode)
567 const SwNumRule *pRule = nullptr;
569 if (
570 rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
571 nullptr != (pRule = rTextNode.GetNumRule())
574 return pRule;
576 return nullptr;
579 SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
581 const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx();
582 OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious");
583 if (!pIndex)
584 return nullptr;
585 SwNodeIndex aIdx(*pIndex, 1);
586 return aIdx.GetNode().GetNoTextNode();
589 bool HasPageBreak(const SwNode &rNd)
591 const SvxFormatBreakItem *pBreak = nullptr;
592 if (rNd.IsTableNode() && rNd.GetTableNode())
594 const SwTable& rTable = rNd.GetTableNode()->GetTable();
595 const SwFrameFormat* pApply = rTable.GetFrameFormat();
596 OSL_ENSURE(pApply, "impossible");
597 if (pApply)
598 pBreak = &pApply->GetFormatAttr(RES_BREAK);
600 else if (const SwContentNode *pNd = rNd.GetContentNode())
601 pBreak = &pNd->GetAttr(RES_BREAK);
603 return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore;
606 tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
608 if(1 == rPolyPoly.Count())
610 return rPolyPoly[0];
612 else
614 // This method will now just concatenate the polygons contained
615 // in the given PolyPolygon. Anything else which might be thought of
616 // for reducing to a single polygon will just need more power and
617 // cannot create more correct results.
618 sal_uInt32 nPointCount(0);
619 sal_uInt16 a;
621 for(a = 0; a < rPolyPoly.Count(); a++)
623 nPointCount += static_cast<sal_uInt32>(rPolyPoly[a].GetSize());
626 if(nPointCount > 0x0000ffff)
628 OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)");
629 nPointCount = 0x0000ffff;
632 tools::Polygon aRetval(o3tl::narrowing<sal_uInt16>(nPointCount));
633 sal_uInt32 nAppendIndex(0);
635 for(a = 0; a < rPolyPoly.Count(); a++)
637 const tools::Polygon& rCandidate = rPolyPoly[a];
639 for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++)
641 aRetval[o3tl::narrowing<sal_uInt16>(nAppendIndex++)] = rCandidate[b];
645 return aRetval;
649 tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop)
651 tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly));
652 const Size &rOrigSize = pNd->GetGraphic().GetPrefSize();
654 const SwAttrSet* pAttrSet = pNd->GetpSwAttrSet();
655 if (bCorrectCrop && pAttrSet)
657 if (pAttrSet->HasItem(RES_GRFATR_CROPGRF))
659 // Word's wrap polygon deals with a canvas which has the size of the already
660 // cropped graphic, do the opposite of correctCrop() in writerfilter/.
661 const SwCropGrf& rCrop = pAttrSet->GetCropGrf();
662 sal_Int32 nCropLeft = convertTwipToMm100(rCrop.GetLeft());
663 sal_Int32 nCropRight = convertTwipToMm100(rCrop.GetRight());
664 sal_Int32 nCropTop = convertTwipToMm100(rCrop.GetTop());
665 sal_Int32 nCropBottom = convertTwipToMm100(rCrop.GetBottom());
666 aPoly.Move(-nCropLeft, -nCropTop);
668 Fraction aScaleX(rOrigSize.getWidth(), rOrigSize.getWidth() - nCropLeft - nCropRight);
669 Fraction aScaleY(rOrigSize.getHeight(), rOrigSize.getHeight() - nCropTop - nCropBottom);
670 aPoly.Scale(double(aScaleX), double(aScaleY));
674 Fraction aMapPolyX(ww::nWrap100Percent, rOrigSize.Width());
675 Fraction aMapPolyY(ww::nWrap100Percent, rOrigSize.Height());
676 aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
679 a) stretch right bound by 15twips
680 b) shrink bottom bound to where it would have been in word
681 c) Move it to the left by 15twips
683 See the import for details
685 const Size &rSize = pNd->GetTwipSize();
686 Fraction aMoveHack(ww::nWrap100Percent, rSize.Width());
687 aMoveHack *= Fraction(15, 1);
688 tools::Long nMove(aMoveHack);
690 Fraction aHackX(ww::nWrap100Percent + nMove,
691 ww::nWrap100Percent);
692 Fraction aHackY(ww::nWrap100Percent - nMove,
693 ww::nWrap100Percent);
694 aPoly.Scale(double(aHackX), double(aHackY));
696 aPoly.Move(-nMove, 0);
697 return aPoly;
700 void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr)
702 OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline");
703 maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone())));
706 namespace {
708 class SameOpenRedlineType
710 private:
711 RedlineType meType;
712 public:
713 explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {}
714 bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const
716 const SwFltRedline *pTest = static_cast<const SwFltRedline *>
717 (pEntry->m_pAttr.get());
718 return (pEntry->m_bOpen && (pTest->m_eType == meType));
724 bool RedlineStack::close(const SwPosition& rPos, RedlineType eType)
726 //Search from end for same type
727 auto aResult = std::find_if(maStack.rbegin(), maStack.rend(),
728 SameOpenRedlineType(eType));
729 if (aResult != maStack.rend())
731 SwTextNode *const pNode(rPos.GetNode().GetTextNode());
732 sal_Int32 const nIndex(rPos.GetContentIndex());
733 // HACK to prevent overlap of field-mark and redline,
734 // which would destroy field-mark invariants when the redline
735 // is hidden: move the redline end one to the left
736 if (pNode && nIndex > 0
737 && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND)
739 SwPosition const end(*rPos.GetNode().GetTextNode(),
740 nIndex - 1);
741 sw::mark::IFieldmark *const pFieldMark(
742 rPos.GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(end));
743 SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark");
744 if (pFieldMark && pFieldMark->GetMarkPos().GetNodeIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1
745 && pFieldMark->GetMarkPos().GetContentIndex() < (*aResult)->m_aMkPos.m_nContent)
747 (*aResult)->SetEndPos(end);
748 return true;
751 (*aResult)->SetEndPos(rPos);
752 return true;
754 return false;
757 void RedlineStack::closeall(const SwPosition& rPos)
759 std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos));
762 void MoveAttrFieldmarkInserted(SwFltPosition& rMkPos, SwFltPosition& rPtPos, const SwPosition& rPos)
764 sal_Int32 const nInserted = 2; // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP
765 SwNodeOffset nPosNd = rPos.GetNodeIndex();
766 sal_Int32 nPosCt = rPos.GetContentIndex() - nInserted;
768 bool const isPoint(rMkPos == rPtPos);
769 if ((rMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
770 (nPosCt <= rMkPos.m_nContent))
772 rMkPos.m_nContent += nInserted;
773 SAL_WARN_IF(rMkPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
774 "sw.ww8", "redline ends after end of line");
775 if (isPoint) // sigh ... important special case...
777 rPtPos.m_nContent += nInserted;
778 return;
781 // for the end position, leave it alone if it's *on* the dummy
782 // char position, that should remain *before*
783 if ((rPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
784 (nPosCt < rPtPos.m_nContent))
786 rPtPos.m_nContent += nInserted;
787 SAL_WARN_IF(rPtPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
788 "sw.ww8", "range ends after end of line");
792 void RedlineStack::MoveAttrsFieldmarkInserted(const SwPosition& rPos)
794 for (size_t i = 0, nCnt = maStack.size(); i < nCnt; ++i)
796 SwFltStackEntry& rEntry = *maStack[i];
797 MoveAttrFieldmarkInserted(rEntry.m_aMkPos, rEntry.m_aPtPos, rPos);
801 void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry)
803 SwPaM aRegion(pEntry->m_aMkPos.m_nNode);
804 if (pEntry->MakeRegion(mrDoc, aRegion,
805 SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) &&
806 (*aRegion.GetPoint() != *aRegion.GetMark())
809 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert |
810 RedlineFlags::ShowDelete);
811 const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*>
812 (pEntry->m_pAttr.get());
814 SwRedlineData aData(pFltRedline->m_eType, pFltRedline->m_nAutorNo,
815 pFltRedline->m_aStamp, OUString(), nullptr);
817 SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion));
818 // the point node may be deleted in AppendRedline, so park
819 // the PaM somewhere safe
820 aRegion.DeleteMark();
821 aRegion.GetPoint()->Assign(*mrDoc.GetNodes()[SwNodeOffset(0)]);
822 mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
823 mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert |
824 RedlineFlags::ShowDelete );
826 pEntry.reset();
829 bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE,
830 const std::unique_ptr<SwFltStackEntry> & pTwoE) const
832 const SwFltRedline *pOne= static_cast<const SwFltRedline*>
833 (pOneE->m_pAttr.get());
834 const SwFltRedline *pTwo= static_cast<const SwFltRedline*>
835 (pTwoE->m_pAttr.get());
837 //Return the earlier time, if two have the same time, prioritize
838 //inserts over deletes
839 if (pOne->m_aStamp == pTwo->m_aStamp)
840 return (pOne->m_eType == RedlineType::Insert && pTwo->m_eType != RedlineType::Insert);
841 else
842 return (pOne->m_aStamp < pTwo->m_aStamp);
845 RedlineStack::~RedlineStack()
847 std::stable_sort(maStack.begin(), maStack.end(), CompareRedlines());
848 std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc));
851 sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm )
853 sal_uInt16 nRet;
854 auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm);
855 if (aIter != maAuthors.end())
856 nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin());
857 else
859 nRet = static_cast< sal_uInt16 >(maAuthors.size());
860 maAuthors.push_back(rNm);
862 return nRet;
866 namespace util
868 InsertedTableListener::InsertedTableListener(SwTableNode& rNode)
869 : m_pTableNode(&rNode)
871 StartListening(rNode.GetNotifier());
874 SwTableNode* InsertedTableListener::GetTableNode()
875 { return m_pTableNode; }
877 void InsertedTableListener::Notify(const SfxHint& rHint)
879 if(rHint.GetId() == SfxHintId::Dying)
880 m_pTableNode = nullptr;
883 InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc)
884 : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout())
887 void InsertedTablesManager::DelAndMakeTableFrames()
889 if (!mbHasRoot)
890 return;
891 for (auto& aTable : maTables)
893 // If already a layout exists, then the BoxFrames must recreated at this table
894 SwTableNode *pTable = aTable.first->GetTableNode();
895 OSL_ENSURE(pTable, "Why no expected table");
896 if (pTable)
898 SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat();
900 if (pFrameFormat != nullptr)
902 SwPosition *pIndex = aTable.second;
903 pTable->DelFrames();
904 pTable->MakeOwnFrames(pIndex);
910 void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM)
912 if (!mbHasRoot)
913 return;
914 //Associate this tablenode with this after position, replace an old
915 //node association if necessary
916 maTables.emplace(
917 std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)),
918 rPaM.GetPoint());
924 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */