diff options
Diffstat (limited to 'sw/source/core/doc/doclay.cxx')
-rw-r--r-- | sw/source/core/doc/doclay.cxx | 1706 |
1 files changed, 1706 insertions, 0 deletions
diff --git a/sw/source/core/doc/doclay.cxx b/sw/source/core/doc/doclay.cxx new file mode 100644 index 000000000..b3275040a --- /dev/null +++ b/sw/source/core/doc/doclay.cxx @@ -0,0 +1,1706 @@ +/* -*- 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 <hintids.hxx> +#include <sot/exchange.hxx> +#include <svx/svdpage.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/opaqitem.hxx> +#include <osl/diagnose.h> +#include <svx/svdouno.hxx> +#include <editeng/frmdiritem.hxx> +#include <istype.hxx> +#include <swmodule.hxx> +#include <modcfg.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <SwStyleNameMapper.hxx> +#include <drawdoc.hxx> +#include <fchrfmt.hxx> +#include <frmatr.hxx> +#include <txatbase.hxx> +#include <fmtfld.hxx> +#include <fmtornt.hxx> +#include <fmtcntnt.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtsrnd.hxx> +#include <fmtflcnt.hxx> +#include <frmfmt.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <ndnotxt.hxx> +#include <ndole.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <swundo.hxx> +#include <flypos.hxx> +#include <UndoInsert.hxx> +#include <expfld.hxx> +#include <poolfmt.hxx> +#include <docary.hxx> +#include <swtable.hxx> +#include <tblsel.hxx> +#include <txtftn.hxx> +#include <ftnidx.hxx> +#include <ftninfo.hxx> +#include <pagedesc.hxx> +#include <strings.hrc> +#include <frameformats.hxx> +#include <tools/datetimeutils.hxx> +#include <comphelper/string.hxx> +#include <o3tl/string_view.hxx> + +#include <sortedobjs.hxx> + +#include <string_view> +#include <vector> + +using namespace ::com::sun::star; + +#define DEF_FLY_WIDTH 2268 // Default width for FlyFrames (2268 == 4cm) + +static bool lcl_IsItemSet(const SwContentNode & rNode, sal_uInt16 which) +{ + bool bResult = false; + + if (SfxItemState::SET == rNode.GetSwAttrSet().GetItemState(which)) + bResult = true; + + return bResult; +} + +SdrObject* SwDoc::CloneSdrObj( const SdrObject& rObj, bool bMoveWithinDoc, + bool bInsInPage ) +{ + // #i52858# - method name changed + SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 ); + if( !pPg ) + { + auto pNewPage = getIDocumentDrawModelAccess().GetDrawModel()->AllocPage( false ); + getIDocumentDrawModelAccess().GetDrawModel()->InsertPage( pNewPage.get() ); + pPg = pNewPage.get(); + } + + // TTTT Clone directly to target SdrModel + SdrObject *pObj(rObj.CloneSdrObject(*getIDocumentDrawModelAccess().GetDrawModel())); + + if( bMoveWithinDoc && SdrInventor::FmForm == pObj->GetObjInventor() ) + { + // We need to preserve the Name for Controls + uno::Reference< awt::XControlModel > xModel = static_cast<SdrUnoObj*>(pObj)->GetUnoControlModel(); + uno::Any aVal; + uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY); + static const OUStringLiteral sName(u"Name"); + if( xSet.is() ) + aVal = xSet->getPropertyValue( sName ); + if( bInsInPage ) + pPg->InsertObjectThenMakeNameUnique( pObj ); + if( xSet.is() ) + xSet->setPropertyValue( sName, aVal ); + } + else if( bInsInPage ) + pPg->InsertObjectThenMakeNameUnique( pObj ); + + // For drawing objects: set layer of cloned object to invisible layer + SdrLayerID nLayerIdForClone = rObj.GetLayer(); + if ( dynamic_cast<const SwFlyDrawObj*>( pObj) == nullptr && + dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr && + !isType<SdrObject>(pObj) ) + { + if ( getIDocumentDrawModelAccess().IsVisibleLayerId( nLayerIdForClone ) ) + { + nLayerIdForClone = getIDocumentDrawModelAccess().GetInvisibleLayerIdByVisibleOne( nLayerIdForClone ); + } + } + pObj->SetLayer( nLayerIdForClone ); + + return pObj; +} + +SwFlyFrameFormat* SwDoc::MakeFlySection_( const SwPosition& rAnchPos, + const SwContentNode& rNode, + RndStdIds eRequestId, + const SfxItemSet* pFlySet, + SwFrameFormat* pFrameFormat ) +{ + if( !pFrameFormat ) + pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ); + + OUString sName; + switch( rNode.GetNodeType() ) + { + case SwNodeType::Grf: sName = GetUniqueGrfName(); break; + case SwNodeType::Ole: sName = GetUniqueOLEName(); break; + default: sName = GetUniqueFrameName(); break; + } + SwFlyFrameFormat* pFormat = MakeFlyFrameFormat( sName, pFrameFormat ); + + // Create content and connect to the format. + // Create ContentNode and put it into the autotext selection. + SwNodeRange aRange( GetNodes().GetEndOfAutotext(), SwNodeOffset(-1), + GetNodes().GetEndOfAutotext() ); + GetNodes().SectionDown( &aRange, SwFlyStartNode ); + + pFormat->SetFormatAttr( SwFormatContent( rNode.StartOfSectionNode() )); + + const SwFormatAnchor* pAnchor = nullptr; + if( pFlySet ) + { + pAnchor = pFlySet->GetItemIfSet( RES_ANCHOR, false ); + if( SfxItemState::SET == pFlySet->GetItemState( RES_CNTNT, false )) + { + SfxItemSet aTmpSet( *pFlySet ); + aTmpSet.ClearItem( RES_CNTNT ); + pFormat->SetFormatAttr( aTmpSet ); + } + else + pFormat->SetFormatAttr( *pFlySet ); + } + + // Anchor not yet set? + RndStdIds eAnchorId; + // #i107811# Assure that at-page anchored fly frames have a page num or a + // content anchor set. + if ( !pAnchor || + ( RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId() && + !pAnchor->GetContentAnchor() ) || + ( RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId() && + !pAnchor->GetContentAnchor() && + pAnchor->GetPageNum() == 0 ) ) + { + // set it again, needed for Undo + SwFormatAnchor aAnch( pFormat->GetAnchor() ); + if (pAnchor && (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())) + { + SwPosition aPos( *rAnchPos.nNode.GetNode().FindFlyStartNode() ); + aAnch.SetAnchor( &aPos ); + eAnchorId = RndStdIds::FLY_AT_FLY; + } + else + { + if( eRequestId != aAnch.GetAnchorId() && + SfxItemState::SET != pFormat->GetItemState( RES_ANCHOR ) ) + { + aAnch.SetType( eRequestId ); + } + + eAnchorId = aAnch.GetAnchorId(); + if ( RndStdIds::FLY_AT_PAGE != eAnchorId || !pAnchor || aAnch.GetPageNum() == 0) + { + aAnch.SetAnchor( &rAnchPos ); + } + } + pFormat->SetFormatAttr( aAnch ); + } + else + eAnchorId = pFormat->GetAnchor().GetAnchorId(); + + if ( RndStdIds::FLY_AS_CHAR == eAnchorId ) + { + const sal_Int32 nStt = rAnchPos.nContent.GetIndex(); + SwTextNode * pTextNode = rAnchPos.nNode.GetNode().GetTextNode(); + + OSL_ENSURE(pTextNode!= nullptr, "There should be a SwTextNode!"); + + if (pTextNode != nullptr) + { + SwFormatFlyCnt aFormat( pFormat ); + // may fail if there's no space left or header/ftr + if (!pTextNode->InsertItem(aFormat, nStt, nStt)) + { // pFormat is dead now + return nullptr; + } + } + } + + if( SfxItemState::SET != pFormat->GetAttrSet().GetItemState( RES_FRM_SIZE )) + { + SwFormatFrameSize aFormatSize( SwFrameSize::Variable, 0, DEF_FLY_WIDTH ); + const SwNoTextNode* pNoTextNode = rNode.GetNoTextNode(); + if( pNoTextNode ) + { + // Set size + Size aSize( pNoTextNode->GetTwipSize() ); + if( MINFLY > aSize.Width() ) + aSize.setWidth( DEF_FLY_WIDTH ); + aFormatSize.SetWidth( aSize.Width() ); + if( aSize.Height() ) + { + aFormatSize.SetHeight( aSize.Height() ); + aFormatSize.SetHeightSizeType( SwFrameSize::Fixed ); + } + } + pFormat->SetFormatAttr( aFormatSize ); + } + + // Set up frames + if( getIDocumentLayoutAccess().GetCurrentViewShell() ) + pFormat->MakeFrames(); // ??? + + if (GetIDocumentUndoRedo().DoesUndo()) + { + SwNodeOffset nNodeIdx = rAnchPos.nNode.GetIndex(); + const sal_Int32 nCntIdx = rAnchPos.nContent.GetIndex(); + GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoInsLayFormat>( pFormat, nNodeIdx, nCntIdx )); + } + + getIDocumentState().SetModified(); + return pFormat; +} + +SwFlyFrameFormat* SwDoc::MakeFlySection( RndStdIds eAnchorType, + const SwPosition* pAnchorPos, + const SfxItemSet* pFlySet, + SwFrameFormat* pFrameFormat, bool bCalledFromShell ) +{ + SwFlyFrameFormat* pFormat = nullptr; + if ( !pAnchorPos && (RndStdIds::FLY_AT_PAGE != eAnchorType) ) + { + const SwFormatAnchor* pAnch; + if( (pFlySet && (pAnch = pFlySet->GetItemIfSet( RES_ANCHOR, false ))) || + ( pFrameFormat && (pAnch = pFrameFormat->GetItemIfSet(RES_ANCHOR)) ) ) + { + if ( RndStdIds::FLY_AT_PAGE != pAnch->GetAnchorId() ) + { + pAnchorPos = pAnch->GetContentAnchor(); + } + } + } + + if (pAnchorPos) + { + if( !pFrameFormat ) + pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ); + + sal_uInt16 nCollId = o3tl::narrowing<sal_uInt16>( + GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ? RES_POOLCOLL_TEXT : RES_POOLCOLL_FRAME ); + + /* If there is no adjust item in the paragraph style for the content node of the new fly section + propagate an existing adjust item at the anchor to the new content node. */ + SwContentNode * pNewTextNd = GetNodes().MakeTextNode + (SwNodeIndex( GetNodes().GetEndOfAutotext()), + getIDocumentStylePoolAccess().GetTextCollFromPool( nCollId )); + SwContentNode * pAnchorNode = pAnchorPos->nNode.GetNode().GetContentNode(); + // pAnchorNode from cursor must be valid, unless a whole table is selected (in which + // case the node is not a content node, and pAnchorNode is nullptr). In the latter case, + // bCalledFromShell is false. + assert(!bCalledFromShell || pAnchorNode); + + const SfxPoolItem * pItem = nullptr; + + if (bCalledFromShell && !lcl_IsItemSet(*pNewTextNd, RES_PARATR_ADJUST) && + SfxItemState::SET == pAnchorNode->GetSwAttrSet().GetItemState(RES_PARATR_ADJUST, true, &pItem)) + { + pNewTextNd->SetAttr(*pItem); + } + + pFormat = MakeFlySection_( *pAnchorPos, *pNewTextNd, + eAnchorType, pFlySet, pFrameFormat ); + } + return pFormat; +} + +SwFlyFrameFormat* SwDoc::MakeFlyAndMove( const SwPaM& rPam, const SfxItemSet& rSet, + const SwSelBoxes* pSelBoxes, + SwFrameFormat *pParent ) +{ + const SwFormatAnchor& rAnch = rSet.Get( RES_ANCHOR ); + + GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr ); + + SwFlyFrameFormat* pFormat = MakeFlySection( rAnch.GetAnchorId(), rPam.GetPoint(), + &rSet, pParent ); + + // If content is selected, it becomes the new frame's content. + // Namely, it is moved into the NodeArray's appropriate section. + + if( pFormat ) + { + do { // middle check loop + const SwFormatContent &rContent = pFormat->GetContent(); + OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." ); + SwNodeIndex aIndex( *(rContent.GetContentIdx()), 1 ); + SwContentNode *pNode = aIndex.GetNode().GetContentNode(); + + // Attention: Do not create an index on the stack, or we + // cannot delete ContentNode in the end! + SwPosition aPos( aIndex ); + aPos.nContent.Assign( pNode, 0 ); + + if( pSelBoxes && !pSelBoxes->empty() ) + { + // Table selection + // Copy parts of a table: create a table with the same width as the + // original one and move (copy and delete) the selected boxes. + // The size is corrected on a percentage basis. + + SwTableNode* pTableNd = const_cast<SwTableNode*>((*pSelBoxes)[0]-> + GetSttNd()->FindTableNode()); + if( !pTableNd ) + break; + + SwTable& rTable = pTableNd->GetTable(); + + // Did we select the whole table? + if( pSelBoxes->size() == rTable.GetTabSortBoxes().size() ) + { + // move the whole table + SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode(), SwNodeOffset(1) ); + + // If we move the whole table and it is located within a + // FlyFrame, the we create a TextNode after it. + // So that this FlyFrame is preserved. + if( aRg.aEnd.GetNode().IsEndNode() ) + GetNodes().MakeTextNode( aRg.aStart, + GetDfltTextFormatColl() ); + + getIDocumentContentOperations().MoveNodeRange( aRg, aPos.nNode, SwMoveFlags::DEFAULT ); + } + else + { + rTable.MakeCopy(*this, aPos, *pSelBoxes); + // Don't delete a part of a table with row span!! + // You could delete the content instead -> ToDo + //rTable.DeleteSel( this, *pSelBoxes, 0, 0, true, true ); + } + + // If the table is within the frame, then copy without the following TextNode + aIndex = rContent.GetContentIdx()->GetNode().EndOfSectionIndex() - 1; + OSL_ENSURE( aIndex.GetNode().GetTextNode(), + "a TextNode should be here" ); + aPos.nContent.Assign( nullptr, 0 ); // Deregister index! + GetNodes().Delete( aIndex ); + + // This is a hack: whilst FlyFrames/Headers/Footers are not undoable we delete all Undo objects + if( GetIDocumentUndoRedo().DoesUndo() ) + { + GetIDocumentUndoRedo().DelAllUndoObj(); + } + } + else + { + // copy all Pams and then delete all + bool bOldFlag = mbCopyIsMove; + bool const bOldUndo = GetIDocumentUndoRedo().DoesUndo(); + bool const bOldRedlineMove(getIDocumentRedlineAccess().IsRedlineMove()); + mbCopyIsMove = true; + GetIDocumentUndoRedo().DoUndo(false); + getIDocumentRedlineAccess().SetRedlineMove(true); + for(const SwPaM& rTmp : rPam.GetRingContainer()) + { + if( rTmp.HasMark() && + *rTmp.GetPoint() != *rTmp.GetMark() ) + { + // aPos is the newly created fly section, so definitely outside rPam, it's pointless to check that again. + getIDocumentContentOperations().CopyRange(*const_cast<SwPaM*>(&rTmp), aPos, SwCopyFlags::IsMoveToFly); + } + } + getIDocumentRedlineAccess().SetRedlineMove(bOldRedlineMove); + mbCopyIsMove = bOldFlag; + GetIDocumentUndoRedo().DoUndo(bOldUndo); + + for(const SwPaM& rTmp : rPam.GetRingContainer()) + { + if( rTmp.HasMark() && + *rTmp.GetPoint() != *rTmp.GetMark() ) + { + getIDocumentContentOperations().DeleteAndJoin( *const_cast<SwPaM*>(&rTmp) ); + } + } + } + } while( false ); + } + + getIDocumentState().SetModified(); + + GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr ); + + return pFormat; +} + + +/* + * paragraph frames - o.k. if the PaM includes the paragraph from the beginning + * to the beginning of the next paragraph at least + * frames at character - o.k. if the PaM starts at least at the same position + * as the frame + */ +static bool lcl_TstFlyRange( const SwPaM* pPam, const SwPosition* pFlyPos, + RndStdIds nAnchorId ) +{ + bool bOk = false; + const SwPaM* pTmp = pPam; + do { + const SwNodeOffset nFlyIndex = pFlyPos->nNode.GetIndex(); + const SwPosition* pPaMStart = pTmp->Start(); + const SwPosition* pPaMEnd = pTmp->End(); + const SwNodeOffset nPamStartIndex = pPaMStart->nNode.GetIndex(); + const SwNodeOffset nPamEndIndex = pPaMEnd->nNode.GetIndex(); + if (RndStdIds::FLY_AT_PARA == nAnchorId) + bOk = (nPamStartIndex < nFlyIndex && nPamEndIndex > nFlyIndex) || + (((nPamStartIndex == nFlyIndex) && (pPaMStart->nContent.GetIndex() == 0)) && + (nPamEndIndex > nFlyIndex)); + else + { + const sal_Int32 nFlyContentIndex = pFlyPos->nContent.GetIndex(); + const sal_Int32 nPamEndContentIndex = pPaMEnd->nContent.GetIndex(); + bOk = (nPamStartIndex < nFlyIndex && + (( nPamEndIndex > nFlyIndex )|| + ((nPamEndIndex == nFlyIndex) && + (nPamEndContentIndex > nFlyContentIndex))) ) + || + (((nPamStartIndex == nFlyIndex) && + (pPaMStart->nContent.GetIndex() <= nFlyContentIndex)) && + ((nPamEndIndex > nFlyIndex) || + (nPamEndContentIndex > nFlyContentIndex ))); + } + + if( bOk ) + break; + pTmp = pTmp->GetNext(); + } while( pPam != pTmp ); + return bOk; +} + +SwPosFlyFrames SwDoc::GetAllFlyFormats( const SwPaM* pCmpRange, bool bDrawAlso, + bool bAsCharAlso ) const +{ + SwPosFlyFrames aRetval; + + // collect all anchored somehow to paragraphs + for( auto pFly : *GetSpzFrameFormats() ) + { + bool bDrawFormat = bDrawAlso && RES_DRAWFRMFMT == pFly->Which(); + bool bFlyFormat = RES_FLYFRMFMT == pFly->Which(); + if( bFlyFormat || bDrawFormat ) + { + const SwFormatAnchor& rAnchor = pFly->GetAnchor(); + SwPosition const*const pAPos = rAnchor.GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) || + ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && bAsCharAlso))) + { + if( pCmpRange && + !lcl_TstFlyRange( pCmpRange, pAPos, rAnchor.GetAnchorId() )) + continue; // not a valid FlyFrame + aRetval.insert(std::make_shared<SwPosFlyFrame>(pAPos->nNode, pFly, aRetval.size())); + } + } + } + + // If we don't have a layout we can't get page anchored FlyFrames. + // Also, page anchored FlyFrames are only returned if no range is specified. + if( !getIDocumentLayoutAccess().GetCurrentViewShell() || pCmpRange ) + { + return aRetval; + } + + const SwPageFrame *pPage = static_cast<const SwPageFrame*>(getIDocumentLayoutAccess().GetCurrentLayout()->GetLower()); + while( pPage ) + { + if( pPage->GetSortedObjs() ) + { + const SwSortedObjs &rObjs = *pPage->GetSortedObjs(); + for(SwAnchoredObject* pAnchoredObj : rObjs) + { + SwFrameFormat *pFly; + if ( pAnchoredObj->DynCastFlyFrame() != nullptr ) + pFly = &(pAnchoredObj->GetFrameFormat()); + else if ( bDrawAlso ) + pFly = &(pAnchoredObj->GetFrameFormat()); + else + continue; + + const SwFormatAnchor& rAnchor = pFly->GetAnchor(); + if ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) && + (RndStdIds::FLY_AT_FLY != rAnchor.GetAnchorId()) && + (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())) + { + const SwContentFrame * pContentFrame = pPage->FindFirstBodyContent(); + if ( !pContentFrame ) + { + // Oops! An empty page. + // In order not to lose the whole frame (RTF) we + // look for the last Content before the page. + const SwPageFrame *pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev()); + while ( !pContentFrame && pPrv ) + { + pContentFrame = pPrv->FindFirstBodyContent(); + pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev()); + } + } + if ( pContentFrame ) + { + SwNodeIndex aIdx( pContentFrame->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pContentFrame)->GetTextNodeFirst() + : *static_cast<SwNoTextFrame const*>(pContentFrame)->GetNode() ); + aRetval.insert(std::make_shared<SwPosFlyFrame>(aIdx, pFly, aRetval.size())); + } + } + } + } + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + } + + return aRetval; +} + +/* #i6447# changed behaviour if lcl_CpyAttr: + + If the old item set contains the item to set (no inheritance) copy the item + into the new set. + + If the old item set contains the item by inheritance and the new set + contains the item, too: + If the two items differ copy the item from the old set to the new set. + + Otherwise the new set will not be changed. +*/ +static void lcl_CpyAttr( SfxItemSet &rNewSet, const SfxItemSet &rOldSet, sal_uInt16 nWhich ) +{ + const SfxPoolItem *pOldItem = nullptr; + + rOldSet.GetItemState( nWhich, false, &pOldItem); + if (pOldItem != nullptr) + rNewSet.Put( *pOldItem ); + else + { + pOldItem = rOldSet.GetItem( nWhich ); + if (pOldItem != nullptr) + { + const SfxPoolItem *pNewItem = rNewSet.GetItem( nWhich ); + if (pNewItem != nullptr) + { + if (*pOldItem != *pNewItem) + rNewSet.Put( *pOldItem ); + } + else { + OSL_FAIL("What am I doing here?"); + } + } + else { + OSL_FAIL("What am I doing here?"); + } + } + +} + +static SwFlyFrameFormat * +lcl_InsertLabel(SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable, + SwUndoInsertLabel *const pUndo, + SwLabelType const eType, std::u16string_view rText, std::u16string_view rSeparator, + const OUString& rNumberingSeparator, + const bool bBefore, const sal_uInt16 nId, const SwNodeOffset nNdIdx, + const OUString& rCharacterStyle, + const bool bCpyBrd ) +{ + ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + + bool bTable = false; // To save some code. + + // Get the field first, because we retrieve the TextColl via the field's name + OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(), + "FieldType index out of bounds." ); + SwFieldType *pType = (nId != USHRT_MAX) ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr; + OSL_ENSURE(!pType || pType->Which() == SwFieldIds::SetExp, "wrong Id for Label"); + + SwTextFormatColl * pColl = nullptr; + if( pType ) + { + for( auto i = pTextFormatCollTable->size(); i; ) + { + if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() ) + { + pColl = (*pTextFormatCollTable)[i]; + break; + } + } + OSL_ENSURE( pColl, "no text collection found" ); + } + + if( !pColl ) + { + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL ); + } + + SwTextNode *pNew = nullptr; + SwFlyFrameFormat* pNewFormat = nullptr; + + switch ( eType ) + { + case SwLabelType::Table: + bTable = true; + [[fallthrough]]; + case SwLabelType::Fly: + // At the FlySection's Beginning/End insert the corresponding Node with its Field. + // The Frame is created automatically. + { + SwStartNode *pSttNd = rDoc.GetNodes()[nNdIdx]->GetStartNode(); + OSL_ENSURE( pSttNd, "No StartNode in InsertLabel." ); + SwNodeOffset nNode; + if( bBefore ) + { + nNode = pSttNd->GetIndex(); + if( !bTable ) + ++nNode; + } + else + { + nNode = pSttNd->EndOfSectionIndex(); + if( bTable ) + ++nNode; + } + + if( pUndo ) + pUndo->SetNodePos( nNode ); + + // Create Node for labeling paragraph. + SwNodeIndex aIdx( rDoc.GetNodes(), nNode ); + pNew = rDoc.GetNodes().MakeTextNode( aIdx, pColl ); + } + break; + + case SwLabelType::Object: + { + // Destroy Frame, + // insert new Frame, + // insert the corresponding Node with Field into the new Frame, + // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame, + // create Frames. + + // Get the FlyFrame's Format and decouple the Layout. + SwFrameFormat *pOldFormat = rDoc.GetNodes()[nNdIdx]->GetFlyFormat(); + OSL_ENSURE( pOldFormat, "Couldn't find the Fly's Format." ); + // #i115719# + // <title> and <description> attributes are lost when calling <DelFrames()>. + // Thus, keep them and restore them after the calling <MakeFrames()> + auto pOldFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pOldFormat); + const OUString sTitle( pOldFlyFrameFormat + ? pOldFlyFrameFormat->GetObjTitle() + : OUString() ); + const OUString sDescription( pOldFlyFrameFormat + ? pOldFlyFrameFormat->GetObjDescription() + : OUString() ); + pOldFormat->DelFrames(); + + pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(), + rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool(RES_POOLFRM_FRAME) ); + + /* #i6447#: Only the selected items are copied from the old + format. */ + std::unique_ptr<SfxItemSet> pNewSet = pNewFormat->GetAttrSet().Clone(); + + // Copy only the set attributes. + // The others should apply from the Templates. + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PRINT ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_OPAQUE ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PROTECT ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_VERT_ORIENT ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_HORI_ORIENT ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_LR_SPACE ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_UL_SPACE ); + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_BACKGROUND ); + if( bCpyBrd ) + { + // If there's no BoxItem at graphic, but the new Format has one, then set the + // default item in the new Set. Because the graphic's size has never changed! + const SfxPoolItem *pItem; + if( SfxItemState::SET == pOldFormat->GetAttrSet(). + GetItemState( RES_BOX, true, &pItem )) + pNewSet->Put( *pItem ); + else if( SfxItemState::SET == pNewFormat->GetAttrSet(). + GetItemState( RES_BOX )) + pNewSet->Put( *GetDfltAttr( RES_BOX ) ); + + if( SfxItemState::SET == pOldFormat->GetAttrSet(). + GetItemState( RES_SHADOW, true, &pItem )) + pNewSet->Put( *pItem ); + else if( SfxItemState::SET == pNewFormat->GetAttrSet(). + GetItemState( RES_SHADOW )) + pNewSet->Put( *GetDfltAttr( RES_SHADOW ) ); + } + else + { + // Hard-set the attributes, because they could come from the Template + // and then size calculations could not be correct anymore. + pNewSet->Put( SvxBoxItem(RES_BOX) ); + pNewSet->Put( SvxShadowItem(RES_SHADOW) ); + } + + // Always transfer the anchor, which is a hard attribute anyways. + pNewSet->Put( pOldFormat->GetAnchor() ); + + // The new one should be changeable in its height. + std::unique_ptr<SwFormatFrameSize> aFrameSize(pOldFormat->GetFrameSize().Clone()); + aFrameSize->SetHeightSizeType( SwFrameSize::Minimum ); + pNewSet->Put( std::move(aFrameSize) ); + + SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection( + SwNodeIndex( rDoc.GetNodes().GetEndOfAutotext() ), + SwFlyStartNode, pColl ); + pNewSet->Put( SwFormatContent( pSttNd )); + + pNewFormat->SetFormatAttr( *pNewSet ); + + // InContents need to be treated in a special way: + // The TextAttribute needs to be destroyed. + // Unfortunately, this also destroys the Format next to the Frames. + // To avoid this, we disconnect the attribute from the Format. + + const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor(); + if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() ) + { + const SwPosition *pPos = rAnchor.GetContentAnchor(); + SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." ); + const sal_Int32 nIdx = pPos->nContent.GetIndex(); + SwTextAttr * const pHint = + pTextNode->GetTextAttrForCharAt(nIdx, RES_TXTATR_FLYCNT); + + assert(pHint && "Missing Hint."); + + OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT, + "Missing FlyInCnt-Hint." ); + OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pOldFormat, + "Wrong TextFlyCnt-Hint." ); + + const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat( + pNewFormat ); + } + + // The old one should not have a flow and it should be adjusted to above and + // middle. + // Also, the width should be 100% and it should also adjust the height, if changed. + pNewSet->ClearItem(); + + pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) ); + pNewSet->Put( SvxOpaqueItem( RES_OPAQUE, true ) ); + + sal_Int16 eVert = bBefore ? text::VertOrientation::BOTTOM : text::VertOrientation::TOP; + pNewSet->Put( SwFormatVertOrient( 0, eVert ) ); + pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER ) ); + + aFrameSize.reset(pOldFormat->GetFrameSize().Clone()); + + SwOLENode* pOleNode = rDoc.GetNodes()[nNdIdx + 1]->GetOLENode(); + bool isMath = false; + if(pOleNode) + { + svt::EmbeddedObjectRef& xRef = pOleNode->GetOLEObj().GetObject(); + if(xRef.is()) + { + SvGlobalName aCLSID( xRef->getClassID() ); + isMath = ( SotExchange::IsMath( aCLSID ) != 0 ); + } + } + aFrameSize->SetWidthPercent(isMath ? 0 : 100); + aFrameSize->SetHeightPercent(SwFormatFrameSize::SYNCED); + pNewSet->Put( std::move(aFrameSize) ); + + // Hard-set the attributes, because they could come from the Template + // and then size calculations could not be correct anymore. + if( bCpyBrd ) + { + pNewSet->Put( SvxBoxItem(RES_BOX) ); + pNewSet->Put( SvxShadowItem(RES_SHADOW) ); + } + pNewSet->Put( SvxLRSpaceItem(RES_LR_SPACE) ); + pNewSet->Put( SvxULSpaceItem(RES_UL_SPACE) ); + + // The old one is paragraph-bound to the paragraph in the new one. + SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA ); + SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 ); + pNew = aAnchIdx.GetNode().GetTextNode(); + SwPosition aPos( aAnchIdx ); + aAnch.SetAnchor( &aPos ); + pNewSet->Put( aAnch ); + + if( pUndo ) + pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat ); + else + pOldFormat->SetFormatAttr( *pNewSet ); + + pNewSet.reset(); + + // Have only the FlyFrames created. + // We leave this to established methods (especially for InCntFlys). + pNewFormat->MakeFrames(); + // #i115719# + if ( pOldFlyFrameFormat ) + { + pOldFlyFrameFormat->SetObjTitle( sTitle ); + pOldFlyFrameFormat->SetObjDescription( sDescription ); + } + } + break; + + default: + OSL_ENSURE(false, "unknown LabelType?"); + } + OSL_ENSURE( pNew, "No Label inserted" ); + if( pNew ) + { + // #i61007# order of captions + bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst(); + // Work up OUString + OUString aText; + if( bOrderNumberingFirst ) + { + aText = rNumberingSeparator; + } + if( pType) + { + aText += pType->GetName(); + if( !bOrderNumberingFirst ) + aText += " "; + } + sal_Int32 nIdx = aText.getLength(); + if( !rText.empty() ) + { + aText += rSeparator; + } + const sal_Int32 nSepIdx = aText.getLength(); + aText += rText; + + // Insert string + SwIndex aIdx( pNew, 0 ); + pNew->InsertText( aText, aIdx ); + + // Insert field + if(pType) + { + SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC); + if( bOrderNumberingFirst ) + nIdx = 0; + SwFormatField aFormat( aField ); + pNew->InsertItem( aFormat, nIdx, nIdx ); + if(!rCharacterStyle.isEmpty()) + { + SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle); + if( !pCharFormat ) + { + const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName(rCharacterStyle, SwGetPoolIdFromName::ChrFmt); + pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId ); + } + if (pCharFormat) + { + SwFormatCharFormat aCharFormat( pCharFormat ); + pNew->InsertItem( aCharFormat, 0, + nSepIdx + 1, SetAttrMode::DONTEXPAND ); + } + } + } + + if ( bTable ) + { + if ( bBefore ) + { + if ( !pNew->GetSwAttrSet().GetKeep().GetValue() ) + pNew->SetAttr( SvxFormatKeepItem( true, RES_KEEP ) ); + } + else + { + SwTableNode *const pNd = + rDoc.GetNodes()[nNdIdx]->GetStartNode()->GetTableNode(); + SwTable &rTable = pNd->GetTable(); + if ( !rTable.GetFrameFormat()->GetKeep().GetValue() ) + rTable.GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem( true, RES_KEEP ) ); + if ( pUndo ) + pUndo->SetUndoKeep(); + } + } + rDoc.getIDocumentState().SetModified(); + } + + return pNewFormat; +} + +SwFlyFrameFormat * +SwDoc::InsertLabel( + SwLabelType const eType, OUString const& rText, OUString const& rSeparator, + OUString const& rNumberingSeparator, + bool const bBefore, sal_uInt16 const nId, SwNodeOffset const nNdIdx, + OUString const& rCharacterStyle, + bool const bCpyBrd ) +{ + std::unique_ptr<SwUndoInsertLabel> pUndo; + if (GetIDocumentUndoRedo().DoesUndo()) + { + pUndo.reset(new SwUndoInsertLabel( + eType, rText, rSeparator, rNumberingSeparator, + bBefore, nId, rCharacterStyle, bCpyBrd, this )); + } + + SwFlyFrameFormat *const pNewFormat = lcl_InsertLabel(*this, mpTextFormatCollTable.get(), pUndo.get(), + eType, rText, rSeparator, rNumberingSeparator, bBefore, + nId, nNdIdx, rCharacterStyle, bCpyBrd); + + if (pUndo) + { + GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); + } + else + { + GetIDocumentUndoRedo().DelAllUndoObj(); + } + + return pNewFormat; +} + +static SwFlyFrameFormat * +lcl_InsertDrawLabel( SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable, + SwUndoInsertLabel *const pUndo, SwDrawFrameFormat *const pOldFormat, + OUString const& rText, + const OUString& rSeparator, + const OUString& rNumberSeparator, + const sal_uInt16 nId, + const OUString& rCharacterStyle, + SdrObject& rSdrObj ) +{ + ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + ::sw::DrawUndoGuard const drawUndoGuard(rDoc.GetIDocumentUndoRedo()); + + // Because we get by the TextColl's name, we need to create the field first. + OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(), + "FieldType index out of bounds" ); + SwFieldType *pType = nId != USHRT_MAX ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr; + OSL_ENSURE( !pType || pType->Which() == SwFieldIds::SetExp, "Wrong label id" ); + + SwTextFormatColl *pColl = nullptr; + if( pType ) + { + for( auto i = pTextFormatCollTable->size(); i; ) + { + if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() ) + { + pColl = (*pTextFormatCollTable)[i]; + break; + } + } + OSL_ENSURE( pColl, "no text collection found" ); + } + + if( !pColl ) + { + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL ); + } + + SwTextNode* pNew = nullptr; + SwFlyFrameFormat* pNewFormat = nullptr; + + // Destroy Frame, + // insert new Frame, + // insert the corresponding Node with Field into the new Frame, + // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame, + // create Frames. + + // Keep layer ID of drawing object before removing + // its frames. + // Note: The layer ID is passed to the undo and have to be the correct value. + // Removing the frames of the drawing object changes its layer. + const SdrLayerID nLayerId = rSdrObj.GetLayer(); + + pOldFormat->DelFrames(); + + // InContents need to be treated in a special way: + // The TextAttribute needs to be destroyed. + // Unfortunately, this also destroys the Format next to the Frames. + // To avoid this, we disconnect the attribute from the Format. + std::unique_ptr<SfxItemSet> pNewSet = pOldFormat->GetAttrSet().Clone( false ); + + // Protect the Frame's size and position + if ( rSdrObj.IsMoveProtect() || rSdrObj.IsResizeProtect() ) + { + SvxProtectItem aProtect(RES_PROTECT); + aProtect.SetContentProtect( false ); + aProtect.SetPosProtect( rSdrObj.IsMoveProtect() ); + aProtect.SetSizeProtect( rSdrObj.IsResizeProtect() ); + pNewSet->Put( aProtect ); + } + + // Take over the text wrap + lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND ); + + // Send the frame to the back, if needed. + // Consider the 'invisible' hell layer. + if ( rDoc.getIDocumentDrawModelAccess().GetHellId() != nLayerId && + rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() != nLayerId ) + { + SvxOpaqueItem aOpaque( RES_OPAQUE ); + aOpaque.SetValue( true ); + pNewSet->Put( aOpaque ); + } + + // Take over position + // #i26791# - use directly drawing object's positioning attributes + pNewSet->Put( pOldFormat->GetHoriOrient() ); + pNewSet->Put( pOldFormat->GetVertOrient() ); + + pNewSet->Put( pOldFormat->GetAnchor() ); + + // The new one should be variable in its height! + Size aSz( rSdrObj.GetCurrentBoundRect().GetSize() ); + SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, aSz.Width(), aSz.Height() ); + pNewSet->Put( aFrameSize ); + + // Apply the margin to the new Frame. + // Don't set a border, use the one from the Template. + pNewSet->Put( pOldFormat->GetLRSpace() ); + pNewSet->Put( pOldFormat->GetULSpace() ); + + SwStartNode* pSttNd = + rDoc.GetNodes().MakeTextSection( + SwNodeIndex( rDoc.GetNodes().GetEndOfAutotext() ), + SwFlyStartNode, pColl ); + + pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(), + rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ) ); + + // Set border and shadow to default if the template contains any. + if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState( RES_BOX )) + pNewSet->Put( *GetDfltAttr( RES_BOX ) ); + + if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState(RES_SHADOW)) + pNewSet->Put( *GetDfltAttr( RES_SHADOW ) ); + + pNewFormat->SetFormatAttr( SwFormatContent( pSttNd )); + pNewFormat->SetFormatAttr( *pNewSet ); + + const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor(); + if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() ) + { + const SwPosition *pPos = rAnchor.GetContentAnchor(); + SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode(); + OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." ); + const sal_Int32 nIdx = pPos->nContent.GetIndex(); + SwTextAttr * const pHint = + pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT ); + + assert(pHint && "Missing Hint."); + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT, + "Missing FlyInCnt-Hint." ); + OSL_ENSURE( pHint->GetFlyCnt(). + GetFrameFormat() == static_cast<SwFrameFormat*>(pOldFormat), + "Wrong TextFlyCnt-Hint." ); +#endif + const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat( pNewFormat ); + } + + // The old one should not have a flow + // and it should be adjusted to above and middle. + pNewSet->ClearItem(); + + pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) ); + if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetHellId()) + { + // Consider drawing objects in the 'invisible' hell layer + rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() ); + } + else if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()) + { + rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() ); + } + pNewSet->Put( SvxLRSpaceItem( RES_LR_SPACE ) ); + pNewSet->Put( SvxULSpaceItem( RES_UL_SPACE ) ); + + // #i26791# - set position of the drawing object, which is labeled. + pNewSet->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) ); + pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ) ); + + // The old one is paragraph-bound to the new one's paragraph. + SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA ); + SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 ); + pNew = aAnchIdx.GetNode().GetTextNode(); + SwPosition aPos( aAnchIdx ); + aAnch.SetAnchor( &aPos ); + pNewSet->Put( aAnch ); + + if( pUndo ) + { + pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat ); + // #i26791# - position no longer needed + pUndo->SetDrawObj( nLayerId ); + } + else + pOldFormat->SetFormatAttr( *pNewSet ); + + pNewSet.reset(); + + // Have only the FlyFrames created. + // We leave this to established methods (especially for InCntFlys). + pNewFormat->MakeFrames(); + + OSL_ENSURE( pNew, "No Label inserted" ); + + if( pNew ) + { + //#i61007# order of captions + bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst(); + + // prepare string + OUString aText; + if( bOrderNumberingFirst ) + { + aText = rNumberSeparator; + } + if ( pType ) + { + aText += pType->GetName(); + if( !bOrderNumberingFirst ) + aText += " "; + } + sal_Int32 nIdx = aText.getLength(); + aText += rSeparator; + const sal_Int32 nSepIdx = aText.getLength(); + aText += rText; + + // insert text + SwIndex aIdx( pNew, 0 ); + pNew->InsertText( aText, aIdx ); + + // insert field + if ( pType ) + { + SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC ); + if( bOrderNumberingFirst ) + nIdx = 0; + SwFormatField aFormat( aField ); + pNew->InsertItem( aFormat, nIdx, nIdx ); + if ( !rCharacterStyle.isEmpty() ) + { + SwCharFormat * pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle); + if ( !pCharFormat ) + { + const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName( rCharacterStyle, SwGetPoolIdFromName::ChrFmt ); + pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId ); + } + if ( pCharFormat ) + { + SwFormatCharFormat aCharFormat( pCharFormat ); + pNew->InsertItem( aCharFormat, 0, nSepIdx + 1, + SetAttrMode::DONTEXPAND ); + } + } + } + } + + return pNewFormat; +} + +SwFlyFrameFormat* SwDoc::InsertDrawLabel( + OUString const& rText, + OUString const& rSeparator, + OUString const& rNumberSeparator, + sal_uInt16 const nId, + OUString const& rCharacterStyle, + SdrObject& rSdrObj ) +{ + SwDrawContact *const pContact = + static_cast<SwDrawContact*>(GetUserCall( &rSdrObj )); + if (!pContact) + return nullptr; + OSL_ENSURE( RES_DRAWFRMFMT == pContact->GetFormat()->Which(), + "InsertDrawLabel(): not a DrawFrameFormat" ); + + SwDrawFrameFormat* pOldFormat = static_cast<SwDrawFrameFormat *>(pContact->GetFormat()); + if (!pOldFormat) + return nullptr; + + std::unique_ptr<SwUndoInsertLabel> pUndo; + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().ClearRedo(); + pUndo.reset(new SwUndoInsertLabel( + SwLabelType::Draw, rText, rSeparator, rNumberSeparator, false, + nId, rCharacterStyle, false, this )); + } + + SwFlyFrameFormat *const pNewFormat = lcl_InsertDrawLabel( + *this, mpTextFormatCollTable.get(), pUndo.get(), pOldFormat, + rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, rSdrObj); + + if (pUndo) + { + GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); + } + else + { + GetIDocumentUndoRedo().DelAllUndoObj(); + } + + return pNewFormat; +} + +static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, std::u16string_view rName, std::u16string_view rCmpName) +{ + if (o3tl::starts_with(rName, rCmpName)) + { + // Only get and set the Flag + const sal_Int32 nNum = o3tl::toInt32(rName.substr(nNmLen)) - 1; + if (nNum >= 0) + rSetFlags.push_back(nNum); + } +} + +static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, const SdrObject& rObj, const OUString& rCmpName) +{ + OUString sName = rObj.GetName(); + lcl_collectUsedNums(rSetFlags, nNmLen, sName, rCmpName); + // tdf#122487 take groups into account, iterate and recurse through their + // contents for name collision check + if (!rObj.IsGroupObject()) + return; + + const SdrObjList* pSub(rObj.GetSubList()); + assert(pSub && "IsGroupObject is implemented as GetSubList != nullptr"); + const size_t nCount = pSub->GetObjCount(); + for (size_t i = 0; i < nCount; ++i) + { + SdrObject* pObj = pSub->GetObj(i); + if (!pObj) + continue; + lcl_collectUsedNums(rSetFlags, nNmLen, *pObj, rCmpName); + } +} + +namespace +{ + int first_available_number(std::vector<unsigned int>& numbers) + { + std::sort(numbers.begin(), numbers.end()); + auto last = std::unique(numbers.begin(), numbers.end()); + numbers.erase(last, numbers.end()); + + for (size_t i = 0; i < numbers.size(); ++i) + { + if (numbers[i] != i) + return i; + } + + return numbers.size(); + } +} + +static OUString lcl_GetUniqueFlyName(const SwDoc& rDoc, TranslateId pDefStrId, sal_uInt16 eType, std::u16string_view rPrefix = std::u16string_view(), SwNodeType nNdTyp = SwNodeType::NONE) +{ + assert(eType >= RES_FMT_BEGIN && eType < RES_FMT_END); + if (rDoc.IsInMailMerge()) + { + OUString newName = "MailMergeFly" + + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US ) + + OUString::number( rDoc.GetSpzFrameFormats()->size() + 1 ); + return newName; + } + + if (!rPrefix.empty()) + { + // Generate a name that makes it possible to know this is a copy of which original name, + // e.g. 'Picture 1 Copy 1'. + assert(nNdTyp != SwNodeType::NONE); + sal_Int32 nCnt = 1; + OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rPrefix); + OUString aTmp; + while(nCnt < SAL_MAX_INT32) + { + aTmp = aPrefix + OUString::number(nCnt); + ++nCnt; + if (!rDoc.FindFlyByName(aTmp, nNdTyp)) + { + break; + } + } + return aTmp; + } + + OUString aName(SwResId(pDefStrId)); + sal_Int32 nNmLen = aName.getLength(); + + const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats(); + + std::vector<unsigned int> aUsedNums; + aUsedNums.reserve(rFormats.size()); + + for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n ) + { + const SwFrameFormat* pFlyFormat = rFormats[ n ]; + if (eType != pFlyFormat->Which()) + continue; + if (eType == RES_DRAWFRMFMT) + { + const SdrObject *pObj = pFlyFormat->FindSdrObject(); + if (pObj) + lcl_collectUsedNums(aUsedNums, nNmLen, *pObj, aName); + } + + OUString sName = pFlyFormat->GetName(); + lcl_collectUsedNums(aUsedNums, nNmLen, sName, aName); + } + + // All numbers are flagged accordingly, so determine the right one + SwFrameFormats::size_type nNum = first_available_number(aUsedNums) + 1; + return aName + OUString::number(nNum); +} + +OUString SwDoc::GetUniqueGrfName(std::u16string_view rPrefix) const +{ + return lcl_GetUniqueFlyName(*this, STR_GRAPHIC_DEFNAME, RES_FLYFRMFMT, rPrefix, SwNodeType::Grf); +} + +OUString SwDoc::GetUniqueOLEName() const +{ + return lcl_GetUniqueFlyName(*this, STR_OBJECT_DEFNAME, RES_FLYFRMFMT); +} + +OUString SwDoc::GetUniqueFrameName() const +{ + return lcl_GetUniqueFlyName(*this, STR_FRAME_DEFNAME, RES_FLYFRMFMT); +} + +OUString SwDoc::GetUniqueShapeName() const +{ + return lcl_GetUniqueFlyName(*this, STR_SHAPE_DEFNAME, RES_DRAWFRMFMT); +} + +OUString SwDoc::GetUniqueDrawObjectName() const +{ + return lcl_GetUniqueFlyName(*this, TranslateId(nullptr, "DrawObject"), RES_DRAWFRMFMT); +} + +const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType nNdTyp ) const +{ + auto it = GetSpzFrameFormats()->findByTypeAndName( RES_FLYFRMFMT, rName ); + if( it == GetSpzFrameFormats()->typeAndNameEnd() ) + return nullptr; + + const SwFrameFormat* pFlyFormat = *it; + assert( RES_FLYFRMFMT == pFlyFormat->Which() && pFlyFormat->GetName() == rName ); + const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx(); + if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() ) + { + if( nNdTyp != SwNodeType::NONE ) + { + // query for the right NodeType + const SwNode* pNd = GetNodes()[ pIdx->GetIndex()+1 ]; + if( nNdTyp == SwNodeType::Text + ? !pNd->IsNoTextNode() + : nNdTyp == pNd->GetNodeType() ) + return static_cast<const SwFlyFrameFormat*>(pFlyFormat); + } + else + return static_cast<const SwFlyFrameFormat*>(pFlyFormat); + } + return nullptr; +} + +void SwDoc::SetFlyName( SwFlyFrameFormat& rFormat, const OUString& rName ) +{ + if (rFormat.GetName() == rName) + { + return; + } + OUString sName( rName ); + if( sName.isEmpty() || FindFlyByName( sName ) ) + { + TranslateId pTyp = STR_FRAME_DEFNAME; + const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx(); + if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() ) + { + switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() ) + { + case SwNodeType::Grf: + pTyp = STR_GRAPHIC_DEFNAME; + break; + case SwNodeType::Ole: + pTyp = STR_OBJECT_DEFNAME; + break; + default: break; + } + } + sName = lcl_GetUniqueFlyName(*this, pTyp, RES_FLYFRMFMT); + } + rFormat.SetName( sName, true ); + getIDocumentState().SetModified(); +} + +void SwDoc::SetAllUniqueFlyNames() +{ + sal_Int32 n, nFlyNum = 0, nGrfNum = 0, nOLENum = 0; + + const OUString sFlyNm(SwResId(STR_FRAME_DEFNAME)); + const OUString sGrfNm(SwResId(STR_GRAPHIC_DEFNAME)); + const OUString sOLENm(SwResId(STR_OBJECT_DEFNAME)); + + n = GetSpzFrameFormats()->size(); + if( 255 < n ) + n = 255; + SwFrameFormatsV aArr; + aArr.reserve( n ); + SwFrameFormat* pFlyFormat; + bool bContainsAtPageObjWithContentAnchor = false; + + for( n = GetSpzFrameFormats()->size(); n; ) + { + pFlyFormat = (*GetSpzFrameFormats())[ --n ]; + if( RES_FLYFRMFMT == pFlyFormat->Which() ) + { + const OUString& aNm = pFlyFormat->GetName(); + if ( !aNm.isEmpty() ) + { + sal_Int32 *pNum = nullptr; + sal_Int32 nLen = 0; + if ( aNm.startsWith(sGrfNm) ) + { + nLen = sGrfNm.getLength(); + pNum = &nGrfNum; + } + else if( aNm.startsWith(sFlyNm) ) + { + nLen = sFlyNm.getLength(); + pNum = &nFlyNum; + } + else if( aNm.startsWith(sOLENm) ) + { + nLen = sOLENm.getLength(); + pNum = &nOLENum; + } + + if ( pNum ) + { + const sal_Int32 nNewLen = o3tl::toInt32(aNm.subView( nLen )); + if (*pNum < nNewLen) + *pNum = nNewLen; + } + } + else + // we want to set that afterwards + aArr.push_back( pFlyFormat ); + + } + if ( !bContainsAtPageObjWithContentAnchor ) + { + const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor(); + if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) && + rAnchor.GetContentAnchor() ) + { + bContainsAtPageObjWithContentAnchor = true; + } + } + } + SetContainsAtPageObjWithContentAnchor( bContainsAtPageObjWithContentAnchor ); + + for( n = aArr.size(); n; ) + { + pFlyFormat = aArr[ --n ]; + const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx(); + if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() ) + { + switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() ) + { + case SwNodeType::Grf: + pFlyFormat->SetName( sGrfNm + OUString::number( ++nGrfNum )); + break; + case SwNodeType::Ole: + pFlyFormat->SetName( sOLENm + OUString::number( ++nOLENum )); + break; + default: + pFlyFormat->SetName( sFlyNm + OUString::number( ++nFlyNum )); + break; + } + } + } + aArr.clear(); + + if( GetFootnoteIdxs().empty() ) + return; + + SwTextFootnote::SetUniqueSeqRefNo( *this ); + // #i52775# Chapter footnotes did not get updated correctly. + // Calling UpdateAllFootnote() instead of UpdateFootnote() solves this problem, + // but I do not dare to call UpdateAllFootnote() in all cases: Safety first. + if ( FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum ) + { + GetFootnoteIdxs().UpdateAllFootnote(); + } + else + { + SwNodeIndex aTmp( GetNodes() ); + GetFootnoteIdxs().UpdateFootnote( aTmp ); + } +} + +bool SwDoc::IsInHeaderFooter( const SwNodeIndex& rIdx ) const +{ + // That can also be a Fly in a Fly in the Header. + // Is also used by sw3io, to determine if a Redline object is + // in the Header or Footer. + // Because Redlines are also attached to Start and EndNode, + // the Index must not necessarily be from a ContentNode. + SwNode* pNd = &rIdx.GetNode(); + const SwNode* pFlyNd = pNd->FindFlyStartNode(); + while( pFlyNd ) + { + // get up by using the Anchor +#if OSL_DEBUG_LEVEL > 0 + std::vector<const SwFrameFormat*> checkFormats; + for( auto pFormat : *GetSpzFrameFormats() ) + { + const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx(); + if( pIdx && pFlyNd == &pIdx->GetNode() ) + checkFormats.push_back( pFormat ); + } +#endif + std::vector<SwFrameFormat*> const & rFlys(pFlyNd->GetAnchoredFlys()); + bool bFound(false); + for (size_t i = 0; i < rFlys.size(); ++i) + { + const SwFrameFormat *const pFormat = rFlys[i]; + const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx(); + if( pIdx && pFlyNd == &pIdx->GetNode() ) + { +#if OSL_DEBUG_LEVEL > 0 + auto checkPos = std::find( + checkFormats.begin(), checkFormats.end(), pFormat ); + assert( checkPos != checkFormats.end()); + checkFormats.erase( checkPos ); +#endif + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + if ((RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) || + !rAnchor.GetContentAnchor() ) + { + return false; + } + + pNd = &rAnchor.GetContentAnchor()->nNode.GetNode(); + pFlyNd = pNd->FindFlyStartNode(); + bFound = true; + break; + } + } + if (!bFound) + { + OSL_ENSURE(mbInReading, "Found a FlySection but not a Format!"); + return false; + } + } + + return nullptr != pNd->FindHeaderStartNode() || + nullptr != pNd->FindFooterStartNode(); +} + +SvxFrameDirection SwDoc::GetTextDirection( const SwPosition& rPos, + const Point* pPt ) const +{ + SvxFrameDirection nRet = SvxFrameDirection::Unknown; + + SwContentNode *pNd = rPos.nNode.GetNode().GetContentNode(); + + // #i42921# - use new method <SwContentNode::GetTextDirection(..)> + if ( pNd ) + { + nRet = pNd->GetTextDirection( rPos, pPt ); + } + if ( nRet == SvxFrameDirection::Unknown ) + { + const SvxFrameDirectionItem* pItem = nullptr; + if( pNd ) + { + // Are we in a FlyFrame? Then look at that for the correct attribute + const SwFrameFormat* pFlyFormat = pNd->GetFlyFormat(); + while( pFlyFormat ) + { + pItem = &pFlyFormat->GetFrameDir(); + if( SvxFrameDirection::Environment == pItem->GetValue() ) + { + pItem = nullptr; + const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor(); + if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) && + pAnchor->GetContentAnchor()) + { + pFlyFormat = pAnchor->GetContentAnchor()->nNode. + GetNode().GetFlyFormat(); + } + else + pFlyFormat = nullptr; + } + else + pFlyFormat = nullptr; + } + + if( !pItem ) + { + const SwPageDesc* pPgDsc = pNd->FindPageDesc(); + if( pPgDsc ) + pItem = &pPgDsc->GetMaster().GetFrameDir(); + } + } + if( !pItem ) + pItem = &GetAttrPool().GetDefaultItem( RES_FRAMEDIR ); + nRet = pItem->GetValue(); + } + return nRet; +} + +bool SwDoc::IsInVerticalText( const SwPosition& rPos ) const +{ + const SvxFrameDirection nDir = GetTextDirection( rPos ); + return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir; +} + +o3tl::sorted_vector<SwRootFrame*> SwDoc::GetAllLayouts() +{ + o3tl::sorted_vector<SwRootFrame*> aAllLayouts; + SwViewShell *pStart = getIDocumentLayoutAccess().GetCurrentViewShell(); + if(pStart) + { + for(const SwViewShell& rShell : pStart->GetRingContainer()) + { + if(rShell.GetLayout()) + aAllLayouts.insert(rShell.GetLayout()); + } + } + return aAllLayouts; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |