android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / doclay.cxx
blobe71c7892398a40dc0ecff22bc5a938d416940fc4
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 <hintids.hxx>
21 #include <sot/exchange.hxx>
22 #include <svx/svdpage.hxx>
23 #include <editeng/keepitem.hxx>
24 #include <editeng/ulspitem.hxx>
25 #include <editeng/lrspitem.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/shaditem.hxx>
28 #include <editeng/protitem.hxx>
29 #include <editeng/opaqitem.hxx>
30 #include <osl/diagnose.h>
31 #include <svx/svdouno.hxx>
32 #include <editeng/frmdiritem.hxx>
33 #include <istype.hxx>
34 #include <swmodule.hxx>
35 #include <modcfg.hxx>
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/embed/XEmbeddedObject.hpp>
38 #include <SwStyleNameMapper.hxx>
39 #include <drawdoc.hxx>
40 #include <fchrfmt.hxx>
41 #include <frmatr.hxx>
42 #include <txatbase.hxx>
43 #include <fmtfld.hxx>
44 #include <fmtornt.hxx>
45 #include <fmtcntnt.hxx>
46 #include <fmtanchr.hxx>
47 #include <fmtfsize.hxx>
48 #include <fmtsrnd.hxx>
49 #include <fmtflcnt.hxx>
50 #include <frmfmt.hxx>
51 #include <pam.hxx>
52 #include <ndtxt.hxx>
53 #include <ndnotxt.hxx>
54 #include <ndole.hxx>
55 #include <doc.hxx>
56 #include <IDocumentUndoRedo.hxx>
57 #include <IDocumentRedlineAccess.hxx>
58 #include <DocumentSettingManager.hxx>
59 #include <IDocumentDrawModelAccess.hxx>
60 #include <IDocumentFieldsAccess.hxx>
61 #include <IDocumentState.hxx>
62 #include <IDocumentLayoutAccess.hxx>
63 #include <IDocumentStylePoolAccess.hxx>
64 #include <rootfrm.hxx>
65 #include <pagefrm.hxx>
66 #include <cntfrm.hxx>
67 #include <txtfrm.hxx>
68 #include <notxtfrm.hxx>
69 #include <dflyobj.hxx>
70 #include <dcontact.hxx>
71 #include <swundo.hxx>
72 #include <flypos.hxx>
73 #include <UndoInsert.hxx>
74 #include <expfld.hxx>
75 #include <poolfmt.hxx>
76 #include <docary.hxx>
77 #include <swtable.hxx>
78 #include <tblsel.hxx>
79 #include <txtftn.hxx>
80 #include <ftnidx.hxx>
81 #include <ftninfo.hxx>
82 #include <pagedesc.hxx>
83 #include <strings.hrc>
84 #include <frameformats.hxx>
85 #include <tools/datetimeutils.hxx>
86 #include <comphelper/string.hxx>
87 #include <o3tl/string_view.hxx>
89 #include <sortedobjs.hxx>
91 #include <string_view>
92 #include <vector>
94 using namespace ::com::sun::star;
96 #define DEF_FLY_WIDTH 2268 // Default width for FlyFrames (2268 == 4cm)
98 static bool lcl_IsItemSet(const SwContentNode & rNode, sal_uInt16 which)
100 bool bResult = false;
102 if (SfxItemState::SET == rNode.GetSwAttrSet().GetItemState(which))
103 bResult = true;
105 return bResult;
108 rtl::Reference<SdrObject> SwDoc::CloneSdrObj( const SdrObject& rObj, bool bMoveWithinDoc,
109 bool bInsInPage )
111 // #i52858# - method name changed
112 SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 );
113 if( !pPg )
115 auto pNewPage = getIDocumentDrawModelAccess().GetDrawModel()->AllocPage( false );
116 getIDocumentDrawModelAccess().GetDrawModel()->InsertPage( pNewPage.get() );
117 pPg = pNewPage.get();
120 // TTTT Clone directly to target SdrModel
121 rtl::Reference<SdrObject> pObj(rObj.CloneSdrObject(*getIDocumentDrawModelAccess().GetDrawModel()));
123 if( bMoveWithinDoc && SdrInventor::FmForm == pObj->GetObjInventor() )
125 // We need to preserve the Name for Controls
126 uno::Reference< awt::XControlModel > xModel = static_cast<SdrUnoObj*>(pObj.get())->GetUnoControlModel();
127 uno::Any aVal;
128 uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY);
129 static const OUStringLiteral sName(u"Name");
130 if( xSet.is() )
131 aVal = xSet->getPropertyValue( sName );
132 if( bInsInPage )
133 pPg->InsertObjectThenMakeNameUnique( pObj.get() );
134 if( xSet.is() )
135 xSet->setPropertyValue( sName, aVal );
137 else if( bInsInPage )
138 pPg->InsertObjectThenMakeNameUnique( pObj.get() );
140 // For drawing objects: set layer of cloned object to invisible layer
141 SdrLayerID nLayerIdForClone = rObj.GetLayer();
142 if ( dynamic_cast<const SwFlyDrawObj*>( pObj.get() ) == nullptr &&
143 dynamic_cast<const SwVirtFlyDrawObj*>( pObj.get() ) == nullptr &&
144 pObj->GetObjIdentifier() != SdrObjKind::NewFrame )
146 if ( getIDocumentDrawModelAccess().IsVisibleLayerId( nLayerIdForClone ) )
148 nLayerIdForClone = getIDocumentDrawModelAccess().GetInvisibleLayerIdByVisibleOne( nLayerIdForClone );
151 pObj->SetLayer( nLayerIdForClone );
153 return pObj;
156 SwFlyFrameFormat* SwDoc::MakeFlySection_( const SwPosition& rAnchPos,
157 const SwContentNode& rNode,
158 RndStdIds eRequestId,
159 const SfxItemSet* pFlySet,
160 SwFrameFormat* pFrameFormat )
162 if( !pFrameFormat )
163 pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
165 OUString sName;
166 switch( rNode.GetNodeType() )
168 case SwNodeType::Grf: sName = GetUniqueGrfName(); break;
169 case SwNodeType::Ole: sName = GetUniqueOLEName(); break;
170 default: sName = GetUniqueFrameName(); break;
172 SwFlyFrameFormat* pFormat = MakeFlyFrameFormat( sName, pFrameFormat );
174 // Create content and connect to the format.
175 // Create ContentNode and put it into the autotext selection.
176 SwNodeRange aRange( GetNodes().GetEndOfAutotext(), SwNodeOffset(-1),
177 GetNodes().GetEndOfAutotext() );
178 GetNodes().SectionDown( &aRange, SwFlyStartNode );
180 pFormat->SetFormatAttr( SwFormatContent( rNode.StartOfSectionNode() ));
182 const SwFormatAnchor* pAnchor = nullptr;
183 if( pFlySet )
185 pAnchor = pFlySet->GetItemIfSet( RES_ANCHOR, false );
186 if( SfxItemState::SET == pFlySet->GetItemState( RES_CNTNT, false ))
188 SfxItemSet aTmpSet( *pFlySet );
189 aTmpSet.ClearItem( RES_CNTNT );
190 pFormat->SetFormatAttr( aTmpSet );
192 else
193 pFormat->SetFormatAttr( *pFlySet );
196 // Anchor not yet set?
197 RndStdIds eAnchorId;
198 // #i107811# Assure that at-page anchored fly frames have a page num or a
199 // content anchor set.
200 if ( !pAnchor ||
201 ( RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId() &&
202 !pAnchor->GetAnchorNode() ) ||
203 ( RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId() &&
204 !pAnchor->GetAnchorNode() &&
205 pAnchor->GetPageNum() == 0 ) )
207 // set it again, needed for Undo
208 SwFormatAnchor aAnch( pFormat->GetAnchor() );
209 if (pAnchor && (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()))
211 SwPosition aPos( *rAnchPos.GetNode().FindFlyStartNode() );
212 aAnch.SetAnchor( &aPos );
213 eAnchorId = RndStdIds::FLY_AT_FLY;
215 else
217 if( eRequestId != aAnch.GetAnchorId() &&
218 SfxItemState::SET != pFormat->GetItemState( RES_ANCHOR ) )
220 aAnch.SetType( eRequestId );
223 eAnchorId = aAnch.GetAnchorId();
224 if ( RndStdIds::FLY_AT_PAGE != eAnchorId || !pAnchor || aAnch.GetPageNum() == 0)
226 aAnch.SetAnchor( &rAnchPos );
229 pFormat->SetFormatAttr( aAnch );
231 else
232 eAnchorId = pFormat->GetAnchor().GetAnchorId();
234 if ( RndStdIds::FLY_AS_CHAR == eAnchorId )
236 const sal_Int32 nStt = rAnchPos.GetContentIndex();
237 SwTextNode * pTextNode = rAnchPos.GetNode().GetTextNode();
239 OSL_ENSURE(pTextNode!= nullptr, "There should be a SwTextNode!");
241 if (pTextNode != nullptr)
243 SwFormatFlyCnt aFormat( pFormat );
244 // may fail if there's no space left or header/ftr
245 if (!pTextNode->InsertItem(aFormat, nStt, nStt))
246 { // pFormat is dead now
247 return nullptr;
252 if( SfxItemState::SET != pFormat->GetAttrSet().GetItemState( RES_FRM_SIZE ))
254 SwFormatFrameSize aFormatSize( SwFrameSize::Variable, 0, DEF_FLY_WIDTH );
255 const SwNoTextNode* pNoTextNode = rNode.GetNoTextNode();
256 if( pNoTextNode )
258 // Set size
259 Size aSize( pNoTextNode->GetTwipSize() );
260 if( MINFLY > aSize.Width() )
261 aSize.setWidth( DEF_FLY_WIDTH );
262 aFormatSize.SetWidth( aSize.Width() );
263 if( aSize.Height() )
265 aFormatSize.SetHeight( aSize.Height() );
266 aFormatSize.SetHeightSizeType( SwFrameSize::Fixed );
269 pFormat->SetFormatAttr( aFormatSize );
272 // Set up frames
273 if( getIDocumentLayoutAccess().GetCurrentViewShell() )
274 pFormat->MakeFrames(); // ???
276 if (GetIDocumentUndoRedo().DoesUndo())
278 SwNodeOffset nNodeIdx = rAnchPos.GetNodeIndex();
279 const sal_Int32 nCntIdx = rAnchPos.GetContentIndex();
280 GetIDocumentUndoRedo().AppendUndo(
281 std::make_unique<SwUndoInsLayFormat>( pFormat, nNodeIdx, nCntIdx ));
284 getIDocumentState().SetModified();
285 return pFormat;
288 SwFlyFrameFormat* SwDoc::MakeFlySection( RndStdIds eAnchorType,
289 const SwPosition* pAnchorPos,
290 const SfxItemSet* pFlySet,
291 SwFrameFormat* pFrameFormat, bool bCalledFromShell )
293 SwFlyFrameFormat* pFormat = nullptr;
294 if ( !pAnchorPos && (RndStdIds::FLY_AT_PAGE != eAnchorType) )
296 const SwFormatAnchor* pAnch;
297 if( (pFlySet && (pAnch = pFlySet->GetItemIfSet( RES_ANCHOR, false ))) ||
298 ( pFrameFormat && (pAnch = pFrameFormat->GetItemIfSet(RES_ANCHOR)) ) )
300 if ( RndStdIds::FLY_AT_PAGE != pAnch->GetAnchorId() )
302 pAnchorPos = pAnch->GetContentAnchor();
307 if (pAnchorPos)
309 if( !pFrameFormat )
310 pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
312 sal_uInt16 nCollId = o3tl::narrowing<sal_uInt16>(
313 GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ? RES_POOLCOLL_TEXT : RES_POOLCOLL_FRAME );
315 /* If there is no adjust item in the paragraph style for the content node of the new fly section
316 propagate an existing adjust item at the anchor to the new content node. */
317 SwContentNode * pNewTextNd = GetNodes().MakeTextNode
318 ( GetNodes().GetEndOfAutotext(),
319 getIDocumentStylePoolAccess().GetTextCollFromPool( nCollId ));
320 SwContentNode * pAnchorNode = pAnchorPos->GetNode().GetContentNode();
321 // pAnchorNode from cursor must be valid, unless a whole table is selected (in which
322 // case the node is not a content node, and pAnchorNode is nullptr). In the latter case,
323 // bCalledFromShell is false.
324 assert(!bCalledFromShell || pAnchorNode);
326 const SfxPoolItem * pItem = nullptr;
328 if (bCalledFromShell && !lcl_IsItemSet(*pNewTextNd, RES_PARATR_ADJUST) &&
329 SfxItemState::SET == pAnchorNode->GetSwAttrSet().GetItemState(RES_PARATR_ADJUST, true, &pItem))
331 pNewTextNd->SetAttr(*pItem);
334 pFormat = MakeFlySection_( *pAnchorPos, *pNewTextNd,
335 eAnchorType, pFlySet, pFrameFormat );
337 return pFormat;
340 SwFlyFrameFormat* SwDoc::MakeFlyAndMove( const SwPaM& rPam, const SfxItemSet& rSet,
341 const SwSelBoxes* pSelBoxes,
342 SwFrameFormat *pParent )
344 const SwFormatAnchor& rAnch = rSet.Get( RES_ANCHOR );
346 GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr );
348 SwFlyFrameFormat* pFormat = MakeFlySection( rAnch.GetAnchorId(), rPam.GetPoint(),
349 &rSet, pParent );
351 // If content is selected, it becomes the new frame's content.
352 // Namely, it is moved into the NodeArray's appropriate section.
354 if( pFormat )
356 do { // middle check loop
357 const SwFormatContent &rContent = pFormat->GetContent();
358 OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." );
359 SwNodeIndex aIndex( *(rContent.GetContentIdx()), 1 );
361 // Attention: Do not create an index on the stack, or we
362 // cannot delete ContentNode in the end!
363 std::optional<SwPosition> oPos( std::in_place, aIndex );
365 if( pSelBoxes && !pSelBoxes->empty() )
367 // Table selection
368 // Copy parts of a table: create a table with the same width as the
369 // original one and move (copy and delete) the selected boxes.
370 // The size is corrected on a percentage basis.
372 SwTableNode* pTableNd = const_cast<SwTableNode*>((*pSelBoxes)[0]->
373 GetSttNd()->FindTableNode());
374 if( !pTableNd )
375 break;
377 SwTable& rTable = pTableNd->GetTable();
379 // Did we select the whole table?
380 if( pSelBoxes->size() == rTable.GetTabSortBoxes().size() )
382 // move the whole table
383 SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode(), SwNodeOffset(1) );
385 // If we move the whole table and it is located within a
386 // FlyFrame, the we create a TextNode after it.
387 // So that this FlyFrame is preserved.
388 if( aRg.aEnd.GetNode().IsEndNode() )
389 GetNodes().MakeTextNode( aRg.aStart.GetNode(),
390 GetDfltTextFormatColl() );
392 getIDocumentContentOperations().MoveNodeRange( aRg, oPos->GetNode(), SwMoveFlags::DEFAULT );
394 else
396 rTable.MakeCopy(*this, *oPos, *pSelBoxes);
397 // Don't delete a part of a table with row span!!
398 // You could delete the content instead -> ToDo
399 //rTable.DeleteSel( this, *pSelBoxes, 0, 0, true, true );
402 // If the table is within the frame, then copy without the following TextNode
403 aIndex = rContent.GetContentIdx()->GetNode().EndOfSectionIndex() - 1;
404 OSL_ENSURE( aIndex.GetNode().GetTextNode(),
405 "a TextNode should be here" );
406 oPos.reset(); // Deregister index!
407 GetNodes().Delete( aIndex );
409 // This is a hack: whilst FlyFrames/Headers/Footers are not undoable we delete all Undo objects
410 if( GetIDocumentUndoRedo().DoesUndo() )
412 GetIDocumentUndoRedo().DelAllUndoObj();
415 else
417 // copy all Pams and then delete all
418 bool bOldFlag = mbCopyIsMove;
419 bool const bOldUndo = GetIDocumentUndoRedo().DoesUndo();
420 bool const bOldRedlineMove(getIDocumentRedlineAccess().IsRedlineMove());
421 mbCopyIsMove = true;
422 GetIDocumentUndoRedo().DoUndo(false);
423 getIDocumentRedlineAccess().SetRedlineMove(true);
424 for(const SwPaM& rTmp : rPam.GetRingContainer())
426 if( rTmp.HasMark() &&
427 *rTmp.GetPoint() != *rTmp.GetMark() )
429 // aPos is the newly created fly section, so definitely outside rPam, it's pointless to check that again.
430 getIDocumentContentOperations().CopyRange(*const_cast<SwPaM*>(&rTmp), *oPos, SwCopyFlags::IsMoveToFly);
433 getIDocumentRedlineAccess().SetRedlineMove(bOldRedlineMove);
434 mbCopyIsMove = bOldFlag;
435 GetIDocumentUndoRedo().DoUndo(bOldUndo);
437 for(const SwPaM& rTmp : rPam.GetRingContainer())
439 if( rTmp.HasMark() &&
440 *rTmp.GetPoint() != *rTmp.GetMark() )
442 getIDocumentContentOperations().DeleteAndJoin( *const_cast<SwPaM*>(&rTmp) );
446 } while( false );
449 getIDocumentState().SetModified();
451 GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr );
453 return pFormat;
458 * paragraph frames - o.k. if the PaM includes the paragraph from the beginning
459 * to the beginning of the next paragraph at least
460 * frames at character - o.k. if the PaM starts at least at the same position
461 * as the frame
463 static bool lcl_TstFlyRange( const SwPaM* pPam, const SwFormatAnchor& rFlyFormatAnchor )
465 bool bOk = false;
466 const SwPaM* pTmp = pPam;
467 do {
468 const SwNodeOffset nFlyIndex = rFlyFormatAnchor.GetAnchorNode()->GetIndex();
469 auto [pPaMStart, pPaMEnd] = pTmp->StartEnd(); // SwPosition*
470 const SwNodeOffset nPamStartIndex = pPaMStart->GetNodeIndex();
471 const SwNodeOffset nPamEndIndex = pPaMEnd->GetNodeIndex();
472 if (RndStdIds::FLY_AT_PARA == rFlyFormatAnchor.GetAnchorId())
473 bOk = (nPamStartIndex < nFlyIndex && nPamEndIndex > nFlyIndex) ||
474 (((nPamStartIndex == nFlyIndex) && (pPaMStart->GetContentIndex() == 0)) &&
475 (nPamEndIndex > nFlyIndex));
476 else
478 const sal_Int32 nFlyContentIndex = rFlyFormatAnchor.GetAnchorContentOffset();
479 const sal_Int32 nPamEndContentIndex = pPaMEnd->GetContentIndex();
480 bOk = (nPamStartIndex < nFlyIndex &&
481 (( nPamEndIndex > nFlyIndex )||
482 ((nPamEndIndex == nFlyIndex) &&
483 (nPamEndContentIndex > nFlyContentIndex))) )
485 (((nPamStartIndex == nFlyIndex) &&
486 (pPaMStart->GetContentIndex() <= nFlyContentIndex)) &&
487 ((nPamEndIndex > nFlyIndex) ||
488 (nPamEndContentIndex > nFlyContentIndex )));
491 if( bOk )
492 break;
493 pTmp = pTmp->GetNext();
494 } while( pPam != pTmp );
495 return bOk;
498 SwPosFlyFrames SwDoc::GetAllFlyFormats( const SwPaM* pCmpRange, bool bDrawAlso,
499 bool bAsCharAlso ) const
501 SwPosFlyFrames aRetval;
502 const SwStartNode* pDirectFly = nullptr;
503 if (pCmpRange && *pCmpRange->GetPoint() == *pCmpRange->GetMark()
504 && (pCmpRange->GetPoint()->GetNode().IsOLENode()
505 || pCmpRange->GetPoint()->GetNode().IsGrfNode()))
507 pDirectFly = pCmpRange->GetPoint()->GetNode().FindFlyStartNode();
510 // collect all anchored somehow to paragraphs
511 for(sw::SpzFrameFormat* pFly: *GetSpzFrameFormats())
513 bool bDrawFormat = bDrawAlso && RES_DRAWFRMFMT == pFly->Which();
514 bool bFlyFormat = RES_FLYFRMFMT == pFly->Which();
515 if( bFlyFormat || bDrawFormat )
517 const SwFormatAnchor& rAnchor = pFly->GetAnchor();
518 SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
519 if (!pAnchorNode)
520 continue;
521 if (pDirectFly)
523 const SwFormatContent& rContent = pFly->GetContent();
524 const SwNodeIndex* pContentNodeIndex = rContent.GetContentIdx();
525 if (pContentNodeIndex && pContentNodeIndex->GetIndex() == pDirectFly->GetIndex())
527 aRetval.insert(SwPosFlyFrame(*pAnchorNode, pFly, aRetval.size()));
528 break;
530 continue;
532 if ( (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
533 (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
534 (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
535 ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && bAsCharAlso) )
537 if( pCmpRange && !lcl_TstFlyRange( pCmpRange, rAnchor ))
538 continue; // not a valid FlyFrame
539 aRetval.insert(SwPosFlyFrame(*pAnchorNode, pFly, aRetval.size()));
544 // If we don't have a layout we can't get page anchored FlyFrames.
545 // Also, page anchored FlyFrames are only returned if no range is specified.
546 if( !getIDocumentLayoutAccess().GetCurrentViewShell() || pCmpRange )
548 return aRetval;
551 const SwPageFrame *pPage = static_cast<const SwPageFrame*>(getIDocumentLayoutAccess().GetCurrentLayout()->GetLower());
552 while( pPage )
554 if( pPage->GetSortedObjs() )
556 const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
557 for(SwAnchoredObject* pAnchoredObj : rObjs)
559 SwFrameFormat *pFly;
560 if ( pAnchoredObj->DynCastFlyFrame() != nullptr )
561 pFly = &(pAnchoredObj->GetFrameFormat());
562 else if ( bDrawAlso )
563 pFly = &(pAnchoredObj->GetFrameFormat());
564 else
565 continue;
567 const SwFormatAnchor& rAnchor = pFly->GetAnchor();
568 if ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
569 (RndStdIds::FLY_AT_FLY != rAnchor.GetAnchorId()) &&
570 (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId()))
572 const SwContentFrame * pContentFrame = pPage->FindFirstBodyContent();
573 if ( !pContentFrame )
575 // Oops! An empty page.
576 // In order not to lose the whole frame (RTF) we
577 // look for the last Content before the page.
578 const SwPageFrame *pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev());
579 while ( !pContentFrame && pPrv )
581 pContentFrame = pPrv->FindFirstBodyContent();
582 pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev());
585 if ( pContentFrame )
587 const SwNode* pNd( pContentFrame->IsTextFrame()
588 ? static_cast<SwTextFrame const*>(pContentFrame)->GetTextNodeFirst()
589 : static_cast<SwNoTextFrame const*>(pContentFrame)->GetNode() );
590 aRetval.insert(SwPosFlyFrame(*pNd, pFly, aRetval.size()));
595 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
598 return aRetval;
601 /* #i6447# changed behaviour if lcl_CpyAttr:
603 If the old item set contains the item to set (no inheritance) copy the item
604 into the new set.
606 If the old item set contains the item by inheritance and the new set
607 contains the item, too:
608 If the two items differ copy the item from the old set to the new set.
610 Otherwise the new set will not be changed.
612 static void lcl_CpyAttr( SfxItemSet &rNewSet, const SfxItemSet &rOldSet, sal_uInt16 nWhich )
614 const SfxPoolItem *pOldItem = nullptr;
616 rOldSet.GetItemState( nWhich, false, &pOldItem);
617 if (pOldItem != nullptr)
618 rNewSet.Put( *pOldItem );
619 else
621 pOldItem = rOldSet.GetItem( nWhich );
622 if (pOldItem != nullptr)
624 const SfxPoolItem *pNewItem = rNewSet.GetItem( nWhich );
625 if (pNewItem != nullptr)
627 if (*pOldItem != *pNewItem)
628 rNewSet.Put( *pOldItem );
630 else {
631 OSL_FAIL("What am I doing here?");
634 else {
635 OSL_FAIL("What am I doing here?");
641 static SwFlyFrameFormat *
642 lcl_InsertLabel(SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
643 SwUndoInsertLabel *const pUndo,
644 SwLabelType const eType, std::u16string_view rText, std::u16string_view rSeparator,
645 const OUString& rNumberingSeparator,
646 const bool bBefore, const sal_uInt16 nId, const SwNodeOffset nNdIdx,
647 const OUString& rCharacterStyle,
648 const bool bCpyBrd )
650 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
652 bool bTable = false; // To save some code.
654 // Get the field first, because we retrieve the TextColl via the field's name
655 OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
656 "FieldType index out of bounds." );
657 SwFieldType *pType = (nId != USHRT_MAX) ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
658 OSL_ENSURE(!pType || pType->Which() == SwFieldIds::SetExp, "wrong Id for Label");
660 SwTextFormatColl * pColl = nullptr;
661 if( pType )
663 for( auto i = pTextFormatCollTable->size(); i; )
665 if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
667 pColl = (*pTextFormatCollTable)[i];
668 break;
671 OSL_ENSURE( pColl, "no text collection found" );
674 if( !pColl )
676 pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
679 SwTextNode *pNew = nullptr;
680 SwFlyFrameFormat* pNewFormat = nullptr;
682 switch ( eType )
684 case SwLabelType::Table:
685 bTable = true;
686 [[fallthrough]];
687 case SwLabelType::Fly:
688 // At the FlySection's Beginning/End insert the corresponding Node with its Field.
689 // The Frame is created automatically.
691 SwStartNode *pSttNd = rDoc.GetNodes()[nNdIdx]->GetStartNode();
692 OSL_ENSURE( pSttNd, "No StartNode in InsertLabel." );
693 SwNodeOffset nNode;
694 if( bBefore )
696 nNode = pSttNd->GetIndex();
697 if( !bTable )
698 ++nNode;
700 else
702 nNode = pSttNd->EndOfSectionIndex();
703 if( bTable )
704 ++nNode;
707 if( pUndo )
708 pUndo->SetNodePos( nNode );
710 // Create Node for labeling paragraph.
711 SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
712 pNew = rDoc.GetNodes().MakeTextNode( aIdx.GetNode(), pColl );
714 break;
716 case SwLabelType::Object:
718 // Destroy Frame,
719 // insert new Frame,
720 // insert the corresponding Node with Field into the new Frame,
721 // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
722 // create Frames.
724 // Get the FlyFrame's Format and decouple the Layout.
725 SwFrameFormat *pOldFormat = rDoc.GetNodes()[nNdIdx]->GetFlyFormat();
726 OSL_ENSURE( pOldFormat, "Couldn't find the Fly's Format." );
727 // #i115719#
728 // <title> and <description> attributes are lost when calling <DelFrames()>.
729 // Thus, keep them and restore them after the calling <MakeFrames()>
730 auto pOldFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pOldFormat);
731 const OUString sTitle( pOldFlyFrameFormat
732 ? pOldFlyFrameFormat->GetObjTitle()
733 : OUString() );
734 const OUString sDescription( pOldFlyFrameFormat
735 ? pOldFlyFrameFormat->GetObjDescription()
736 : OUString() );
737 pOldFormat->DelFrames();
739 pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
740 rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool(RES_POOLFRM_FRAME) );
742 /* #i6447#: Only the selected items are copied from the old
743 format. */
744 std::unique_ptr<SfxItemSet> pNewSet = pNewFormat->GetAttrSet().Clone();
746 // Copy only the set attributes.
747 // The others should apply from the Templates.
748 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PRINT );
749 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_OPAQUE );
750 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PROTECT );
751 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
752 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_VERT_ORIENT );
753 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_HORI_ORIENT );
754 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_LR_SPACE );
755 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_UL_SPACE );
756 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_BACKGROUND );
757 if( bCpyBrd )
759 // If there's no BoxItem at graphic, but the new Format has one, then set the
760 // default item in the new Set. Because the graphic's size has never changed!
761 const SfxPoolItem *pItem;
762 if( SfxItemState::SET == pOldFormat->GetAttrSet().
763 GetItemState( RES_BOX, true, &pItem ))
764 pNewSet->Put( *pItem );
765 else if( SfxItemState::SET == pNewFormat->GetAttrSet().
766 GetItemState( RES_BOX ))
767 pNewSet->Put( *GetDfltAttr( RES_BOX ) );
769 if( SfxItemState::SET == pOldFormat->GetAttrSet().
770 GetItemState( RES_SHADOW, true, &pItem ))
771 pNewSet->Put( *pItem );
772 else if( SfxItemState::SET == pNewFormat->GetAttrSet().
773 GetItemState( RES_SHADOW ))
774 pNewSet->Put( *GetDfltAttr( RES_SHADOW ) );
776 else
778 // Hard-set the attributes, because they could come from the Template
779 // and then size calculations could not be correct anymore.
780 pNewSet->Put( SvxBoxItem(RES_BOX) );
781 pNewSet->Put( SvxShadowItem(RES_SHADOW) );
784 // Always transfer the anchor, which is a hard attribute anyways.
785 pNewSet->Put( pOldFormat->GetAnchor() );
787 // The new one should be changeable in its height.
788 std::unique_ptr<SwFormatFrameSize> aFrameSize(pOldFormat->GetFrameSize().Clone());
789 aFrameSize->SetHeightSizeType( SwFrameSize::Minimum );
790 pNewSet->Put( std::move(aFrameSize) );
792 SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection(
793 rDoc.GetNodes().GetEndOfAutotext(),
794 SwFlyStartNode, pColl );
795 pNewSet->Put( SwFormatContent( pSttNd ));
797 pNewFormat->SetFormatAttr( *pNewSet );
799 // InContents need to be treated in a special way:
800 // The TextAttribute needs to be destroyed.
801 // Unfortunately, this also destroys the Format next to the Frames.
802 // To avoid this, we disconnect the attribute from the Format.
804 const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
805 if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
807 SwTextNode *pTextNode = rAnchor.GetAnchorNode()->GetTextNode();
808 OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
809 const sal_Int32 nIdx = rAnchor.GetAnchorContentOffset();
810 SwTextAttr * const pHint =
811 pTextNode->GetTextAttrForCharAt(nIdx, RES_TXTATR_FLYCNT);
813 assert(pHint && "Missing Hint.");
815 OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
816 "Missing FlyInCnt-Hint." );
817 OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pOldFormat,
818 "Wrong TextFlyCnt-Hint." );
820 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat(
821 pNewFormat );
824 // The old one should not have a flow and it should be adjusted to above and
825 // middle.
826 // Also, the width should be 100% and it should also adjust the height, if changed.
827 pNewSet->ClearItem();
829 pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
830 pNewSet->Put( SvxOpaqueItem( RES_OPAQUE, true ) );
832 sal_Int16 eVert = bBefore ? text::VertOrientation::BOTTOM : text::VertOrientation::TOP;
833 pNewSet->Put( SwFormatVertOrient( 0, eVert ) );
834 pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER ) );
836 aFrameSize.reset(pOldFormat->GetFrameSize().Clone());
838 SwOLENode* pOleNode = rDoc.GetNodes()[nNdIdx + 1]->GetOLENode();
839 bool isMath = false;
840 if(pOleNode)
842 svt::EmbeddedObjectRef& xRef = pOleNode->GetOLEObj().GetObject();
843 if(xRef.is())
845 SvGlobalName aCLSID( xRef->getClassID() );
846 isMath = ( SotExchange::IsMath( aCLSID ) != 0 );
849 aFrameSize->SetWidthPercent(isMath ? 0 : 100);
850 aFrameSize->SetHeightPercent(SwFormatFrameSize::SYNCED);
851 pNewSet->Put( std::move(aFrameSize) );
853 // Hard-set the attributes, because they could come from the Template
854 // and then size calculations could not be correct anymore.
855 if( bCpyBrd )
857 pNewSet->Put( SvxBoxItem(RES_BOX) );
858 pNewSet->Put( SvxShadowItem(RES_SHADOW) );
860 pNewSet->Put( SvxLRSpaceItem(RES_LR_SPACE) );
861 pNewSet->Put( SvxULSpaceItem(RES_UL_SPACE) );
863 // The old one is paragraph-bound to the paragraph in the new one.
864 SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
865 SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
866 pNew = aAnchIdx.GetNode().GetTextNode();
867 SwPosition aPos( aAnchIdx );
868 aAnch.SetAnchor( &aPos );
869 pNewSet->Put( aAnch );
871 if( pUndo )
872 pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat );
873 else
874 pOldFormat->SetFormatAttr( *pNewSet );
876 pNewSet.reset();
878 // Have only the FlyFrames created.
879 // We leave this to established methods (especially for InCntFlys).
880 pNewFormat->MakeFrames();
881 // #i115719#
882 if ( pOldFlyFrameFormat )
884 pOldFlyFrameFormat->SetObjTitle( sTitle );
885 pOldFlyFrameFormat->SetObjDescription( sDescription );
888 break;
890 default:
891 OSL_ENSURE(false, "unknown LabelType?");
893 OSL_ENSURE( pNew, "No Label inserted" );
894 if( pNew )
896 // #i61007# order of captions
897 bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
898 // Work up OUString
899 OUString aText;
900 if( bOrderNumberingFirst )
902 aText = rNumberingSeparator;
904 if( pType)
906 aText += pType->GetName();
907 if( !bOrderNumberingFirst )
908 aText += " ";
910 sal_Int32 nIdx = aText.getLength();
911 if( !rText.empty() )
913 aText += rSeparator;
915 const sal_Int32 nSepIdx = aText.getLength();
916 aText += rText;
918 // Insert string
919 SwContentIndex aIdx( pNew, 0 );
920 pNew->InsertText( aText, aIdx );
922 // Insert field
923 if(pType)
925 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC);
926 if( bOrderNumberingFirst )
927 nIdx = 0;
928 SwFormatField aFormat( aField );
929 pNew->InsertItem( aFormat, nIdx, nIdx );
930 if(!rCharacterStyle.isEmpty())
932 SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
933 if( !pCharFormat )
935 const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName(rCharacterStyle, SwGetPoolIdFromName::ChrFmt);
936 pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
938 if (pCharFormat)
940 SwFormatCharFormat aCharFormat( pCharFormat );
941 pNew->InsertItem( aCharFormat, 0,
942 nSepIdx + 1, SetAttrMode::DONTEXPAND );
947 if ( bTable )
949 if ( bBefore )
951 if ( !pNew->GetSwAttrSet().GetKeep().GetValue() )
952 pNew->SetAttr( SvxFormatKeepItem( true, RES_KEEP ) );
954 else
956 SwTableNode *const pNd =
957 rDoc.GetNodes()[nNdIdx]->GetStartNode()->GetTableNode();
958 SwTable &rTable = pNd->GetTable();
959 if ( !rTable.GetFrameFormat()->GetKeep().GetValue() )
960 rTable.GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem( true, RES_KEEP ) );
961 if ( pUndo )
962 pUndo->SetUndoKeep();
965 rDoc.getIDocumentState().SetModified();
968 return pNewFormat;
971 SwFlyFrameFormat *
972 SwDoc::InsertLabel(
973 SwLabelType const eType, OUString const& rText, OUString const& rSeparator,
974 OUString const& rNumberingSeparator,
975 bool const bBefore, sal_uInt16 const nId, SwNodeOffset const nNdIdx,
976 OUString const& rCharacterStyle,
977 bool const bCpyBrd )
979 std::unique_ptr<SwUndoInsertLabel> pUndo;
980 if (GetIDocumentUndoRedo().DoesUndo())
982 pUndo.reset(new SwUndoInsertLabel(
983 eType, rText, rSeparator, rNumberingSeparator,
984 bBefore, nId, rCharacterStyle, bCpyBrd, this ));
987 SwFlyFrameFormat *const pNewFormat = lcl_InsertLabel(*this, mpTextFormatCollTable.get(), pUndo.get(),
988 eType, rText, rSeparator, rNumberingSeparator, bBefore,
989 nId, nNdIdx, rCharacterStyle, bCpyBrd);
991 if (pUndo)
993 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
995 else
997 GetIDocumentUndoRedo().DelAllUndoObj();
1000 return pNewFormat;
1003 static SwFlyFrameFormat *
1004 lcl_InsertDrawLabel( SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
1005 SwUndoInsertLabel *const pUndo, SwDrawFrameFormat *const pOldFormat,
1006 OUString const& rText,
1007 const OUString& rSeparator,
1008 const OUString& rNumberSeparator,
1009 const sal_uInt16 nId,
1010 const OUString& rCharacterStyle,
1011 SdrObject& rSdrObj )
1013 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1014 ::sw::DrawUndoGuard const drawUndoGuard(rDoc.GetIDocumentUndoRedo());
1016 // Because we get by the TextColl's name, we need to create the field first.
1017 OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
1018 "FieldType index out of bounds" );
1019 SwFieldType *pType = nId != USHRT_MAX ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
1020 OSL_ENSURE( !pType || pType->Which() == SwFieldIds::SetExp, "Wrong label id" );
1022 SwTextFormatColl *pColl = nullptr;
1023 if( pType )
1025 for( auto i = pTextFormatCollTable->size(); i; )
1027 if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
1029 pColl = (*pTextFormatCollTable)[i];
1030 break;
1033 OSL_ENSURE( pColl, "no text collection found" );
1036 if( !pColl )
1038 pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
1041 SwTextNode* pNew = nullptr;
1042 SwFlyFrameFormat* pNewFormat = nullptr;
1044 // Destroy Frame,
1045 // insert new Frame,
1046 // insert the corresponding Node with Field into the new Frame,
1047 // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
1048 // create Frames.
1050 // Keep layer ID of drawing object before removing
1051 // its frames.
1052 // Note: The layer ID is passed to the undo and have to be the correct value.
1053 // Removing the frames of the drawing object changes its layer.
1054 const SdrLayerID nLayerId = rSdrObj.GetLayer();
1056 pOldFormat->DelFrames();
1058 // InContents need to be treated in a special way:
1059 // The TextAttribute needs to be destroyed.
1060 // Unfortunately, this also destroys the Format next to the Frames.
1061 // To avoid this, we disconnect the attribute from the Format.
1062 std::unique_ptr<SfxItemSet> pNewSet = pOldFormat->GetAttrSet().Clone( false );
1064 // Protect the Frame's size and position
1065 if ( rSdrObj.IsMoveProtect() || rSdrObj.IsResizeProtect() )
1067 SvxProtectItem aProtect(RES_PROTECT);
1068 aProtect.SetContentProtect( false );
1069 aProtect.SetPosProtect( rSdrObj.IsMoveProtect() );
1070 aProtect.SetSizeProtect( rSdrObj.IsResizeProtect() );
1071 pNewSet->Put( aProtect );
1074 // Take over the text wrap
1075 lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
1077 // Send the frame to the back, if needed.
1078 // Consider the 'invisible' hell layer.
1079 if ( rDoc.getIDocumentDrawModelAccess().GetHellId() != nLayerId &&
1080 rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() != nLayerId )
1082 SvxOpaqueItem aOpaque( RES_OPAQUE );
1083 aOpaque.SetValue( true );
1084 pNewSet->Put( aOpaque );
1087 // Take over position
1088 // #i26791# - use directly drawing object's positioning attributes
1089 pNewSet->Put( pOldFormat->GetHoriOrient() );
1090 pNewSet->Put( pOldFormat->GetVertOrient() );
1092 pNewSet->Put( pOldFormat->GetAnchor() );
1094 // The new one should be variable in its height!
1095 Size aSz( rSdrObj.GetCurrentBoundRect().GetSize() );
1096 SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, aSz.Width(), aSz.Height() );
1097 pNewSet->Put( aFrameSize );
1099 // Apply the margin to the new Frame.
1100 // Don't set a border, use the one from the Template.
1101 pNewSet->Put( pOldFormat->GetLRSpace() );
1102 pNewSet->Put( pOldFormat->GetULSpace() );
1104 SwStartNode* pSttNd =
1105 rDoc.GetNodes().MakeTextSection(
1106 rDoc.GetNodes().GetEndOfAutotext(),
1107 SwFlyStartNode, pColl );
1109 pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
1110 rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ) );
1112 // Set border and shadow to default if the template contains any.
1113 if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState( RES_BOX ))
1114 pNewSet->Put( *GetDfltAttr( RES_BOX ) );
1116 if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState(RES_SHADOW))
1117 pNewSet->Put( *GetDfltAttr( RES_SHADOW ) );
1119 pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
1120 pNewFormat->SetFormatAttr( *pNewSet );
1122 const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
1123 if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
1125 SwTextNode *pTextNode = rAnchor.GetAnchorNode()->GetTextNode();
1126 OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
1127 const sal_Int32 nIdx = rAnchor.GetAnchorContentOffset();
1128 SwTextAttr * const pHint =
1129 pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
1131 assert(pHint && "Missing Hint.");
1133 #if OSL_DEBUG_LEVEL > 0
1134 OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
1135 "Missing FlyInCnt-Hint." );
1136 OSL_ENSURE( pHint->GetFlyCnt().
1137 GetFrameFormat() == static_cast<SwFrameFormat*>(pOldFormat),
1138 "Wrong TextFlyCnt-Hint." );
1139 #endif
1140 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat( pNewFormat );
1143 // The old one should not have a flow
1144 // and it should be adjusted to above and middle.
1145 pNewSet->ClearItem();
1147 pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
1148 if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetHellId())
1150 // Consider drawing objects in the 'invisible' hell layer
1151 rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
1153 else if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId())
1155 rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
1157 pNewSet->Put( SvxLRSpaceItem( RES_LR_SPACE ) );
1158 pNewSet->Put( SvxULSpaceItem( RES_UL_SPACE ) );
1160 // #i26791# - set position of the drawing object, which is labeled.
1161 pNewSet->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) );
1162 pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ) );
1164 // The old one is paragraph-bound to the new one's paragraph.
1165 SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
1166 SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
1167 pNew = aAnchIdx.GetNode().GetTextNode();
1168 SwPosition aPos( aAnchIdx );
1169 aAnch.SetAnchor( &aPos );
1170 pNewSet->Put( aAnch );
1172 if( pUndo )
1174 pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat );
1175 // #i26791# - position no longer needed
1176 pUndo->SetDrawObj( nLayerId );
1178 else
1179 pOldFormat->SetFormatAttr( *pNewSet );
1181 pNewSet.reset();
1183 // Have only the FlyFrames created.
1184 // We leave this to established methods (especially for InCntFlys).
1185 pNewFormat->MakeFrames();
1187 OSL_ENSURE( pNew, "No Label inserted" );
1189 if( pNew )
1191 //#i61007# order of captions
1192 bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
1194 // prepare string
1195 OUString aText;
1196 if( bOrderNumberingFirst )
1198 aText = rNumberSeparator;
1200 if ( pType )
1202 aText += pType->GetName();
1203 if( !bOrderNumberingFirst )
1204 aText += " ";
1206 sal_Int32 nIdx = aText.getLength();
1207 aText += rSeparator;
1208 const sal_Int32 nSepIdx = aText.getLength();
1209 aText += rText;
1211 // insert text
1212 SwContentIndex aIdx( pNew, 0 );
1213 pNew->InsertText( aText, aIdx );
1215 // insert field
1216 if ( pType )
1218 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC );
1219 if( bOrderNumberingFirst )
1220 nIdx = 0;
1221 SwFormatField aFormat( aField );
1222 pNew->InsertItem( aFormat, nIdx, nIdx );
1223 if ( !rCharacterStyle.isEmpty() )
1225 SwCharFormat * pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
1226 if ( !pCharFormat )
1228 const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName( rCharacterStyle, SwGetPoolIdFromName::ChrFmt );
1229 pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
1231 if ( pCharFormat )
1233 SwFormatCharFormat aCharFormat( pCharFormat );
1234 pNew->InsertItem( aCharFormat, 0, nSepIdx + 1,
1235 SetAttrMode::DONTEXPAND );
1241 return pNewFormat;
1244 SwFlyFrameFormat* SwDoc::InsertDrawLabel(
1245 OUString const& rText,
1246 OUString const& rSeparator,
1247 OUString const& rNumberSeparator,
1248 sal_uInt16 const nId,
1249 OUString const& rCharacterStyle,
1250 SdrObject& rSdrObj )
1252 SwDrawContact *const pContact =
1253 static_cast<SwDrawContact*>(GetUserCall( &rSdrObj ));
1254 if (!pContact)
1255 return nullptr;
1256 OSL_ENSURE( RES_DRAWFRMFMT == pContact->GetFormat()->Which(),
1257 "InsertDrawLabel(): not a DrawFrameFormat" );
1259 SwDrawFrameFormat* pOldFormat = static_cast<SwDrawFrameFormat *>(pContact->GetFormat());
1260 if (!pOldFormat)
1261 return nullptr;
1263 std::unique_ptr<SwUndoInsertLabel> pUndo;
1264 if (GetIDocumentUndoRedo().DoesUndo())
1266 GetIDocumentUndoRedo().ClearRedo();
1267 pUndo.reset(new SwUndoInsertLabel(
1268 SwLabelType::Draw, rText, rSeparator, rNumberSeparator, false,
1269 nId, rCharacterStyle, false, this ));
1272 SwFlyFrameFormat *const pNewFormat = lcl_InsertDrawLabel(
1273 *this, mpTextFormatCollTable.get(), pUndo.get(), pOldFormat,
1274 rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, rSdrObj);
1276 if (pUndo)
1278 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1280 else
1282 GetIDocumentUndoRedo().DelAllUndoObj();
1285 return pNewFormat;
1288 static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, std::u16string_view rName, std::u16string_view rCmpName)
1290 if (o3tl::starts_with(rName, rCmpName))
1292 // Only get and set the Flag
1293 const sal_Int32 nNum = o3tl::toInt32(rName.substr(nNmLen)) - 1;
1294 if (nNum >= 0)
1295 rSetFlags.push_back(nNum);
1299 static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, const SdrObject& rObj, const OUString& rCmpName)
1301 OUString sName = rObj.GetName();
1302 lcl_collectUsedNums(rSetFlags, nNmLen, sName, rCmpName);
1303 // tdf#122487 take groups into account, iterate and recurse through their
1304 // contents for name collision check
1305 if (!rObj.IsGroupObject())
1306 return;
1308 const SdrObjList* pSub(rObj.GetSubList());
1309 assert(pSub && "IsGroupObject is implemented as GetSubList != nullptr");
1310 const size_t nCount = pSub->GetObjCount();
1311 for (size_t i = 0; i < nCount; ++i)
1313 SdrObject* pObj = pSub->GetObj(i);
1314 if (!pObj)
1315 continue;
1316 lcl_collectUsedNums(rSetFlags, nNmLen, *pObj, rCmpName);
1320 namespace
1322 int first_available_number(std::vector<unsigned int>& numbers)
1324 std::sort(numbers.begin(), numbers.end());
1325 auto last = std::unique(numbers.begin(), numbers.end());
1326 numbers.erase(last, numbers.end());
1328 for (size_t i = 0; i < numbers.size(); ++i)
1330 if (numbers[i] != i)
1331 return i;
1334 return numbers.size();
1338 static OUString lcl_GetUniqueFlyName(const SwDoc& rDoc, TranslateId pDefStrId, sal_uInt16 eType, std::u16string_view rPrefix = std::u16string_view(), SwNodeType nNdTyp = SwNodeType::NONE)
1340 assert(eType >= RES_FMT_BEGIN && eType < RES_FMT_END);
1341 if (rDoc.IsInMailMerge())
1343 OUString newName = "MailMergeFly"
1344 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
1345 + OUString::number( rDoc.GetSpzFrameFormats()->size() + 1 );
1346 return newName;
1349 if (!rPrefix.empty())
1351 // Generate a name that makes it possible to know this is a copy of which original name,
1352 // e.g. 'Picture 1 Copy 1'.
1353 assert(nNdTyp != SwNodeType::NONE);
1354 sal_Int32 nCnt = 1;
1355 OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rPrefix);
1356 OUString aTmp;
1357 while(nCnt < SAL_MAX_INT32)
1359 aTmp = aPrefix + OUString::number(nCnt);
1360 ++nCnt;
1361 if (!rDoc.FindFlyByName(aTmp, nNdTyp))
1363 break;
1366 return aTmp;
1369 OUString aName(SwResId(pDefStrId));
1370 sal_Int32 nNmLen = aName.getLength();
1372 std::vector<unsigned int> aUsedNums;
1373 aUsedNums.reserve(rDoc.GetSpzFrameFormats()->size());
1375 for(sw::SpzFrameFormat* pFlyFormat: *rDoc.GetSpzFrameFormats())
1377 if (eType != pFlyFormat->Which())
1378 continue;
1379 if (eType == RES_DRAWFRMFMT)
1381 const SdrObject *pObj = pFlyFormat->FindSdrObject();
1382 if (pObj)
1383 lcl_collectUsedNums(aUsedNums, nNmLen, *pObj, aName);
1386 OUString sName = pFlyFormat->GetName();
1387 lcl_collectUsedNums(aUsedNums, nNmLen, sName, aName);
1390 // All numbers are flagged accordingly, so determine the right one
1391 auto nNum = first_available_number(aUsedNums) + 1;
1392 return aName + OUString::number(nNum);
1395 OUString SwDoc::GetUniqueGrfName(std::u16string_view rPrefix) const
1397 return lcl_GetUniqueFlyName(*this, STR_GRAPHIC_DEFNAME, RES_FLYFRMFMT, rPrefix, SwNodeType::Grf);
1400 OUString SwDoc::GetUniqueOLEName() const
1402 return lcl_GetUniqueFlyName(*this, STR_OBJECT_DEFNAME, RES_FLYFRMFMT);
1405 OUString SwDoc::GetUniqueFrameName() const
1407 return lcl_GetUniqueFlyName(*this, STR_FRAME_DEFNAME, RES_FLYFRMFMT);
1410 OUString SwDoc::GetUniqueShapeName() const
1412 return lcl_GetUniqueFlyName(*this, STR_SHAPE_DEFNAME, RES_DRAWFRMFMT);
1415 OUString SwDoc::GetUniqueDrawObjectName() const
1417 return lcl_GetUniqueFlyName(*this, TranslateId(nullptr, "DrawObject"), RES_DRAWFRMFMT);
1420 const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType nNdTyp ) const
1422 auto it = GetSpzFrameFormats()->findByTypeAndName( RES_FLYFRMFMT, rName );
1423 if( it == GetSpzFrameFormats()->typeAndNameEnd() )
1424 return nullptr;
1426 const SwFrameFormat* pFlyFormat = *it;
1427 assert( RES_FLYFRMFMT == pFlyFormat->Which() && pFlyFormat->GetName() == rName );
1428 const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
1429 if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
1431 if( nNdTyp != SwNodeType::NONE )
1433 // query for the right NodeType
1434 const SwNode* pNd = GetNodes()[ pIdx->GetIndex()+1 ];
1435 if( nNdTyp == SwNodeType::Text
1436 ? !pNd->IsNoTextNode()
1437 : nNdTyp == pNd->GetNodeType() )
1438 return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
1440 else
1441 return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
1443 return nullptr;
1446 void SwDoc::SetFlyName( SwFlyFrameFormat& rFormat, const OUString& rName )
1448 if (rFormat.GetName() == rName)
1450 return;
1452 OUString sName( rName );
1453 if( sName.isEmpty() || FindFlyByName( sName ) )
1455 TranslateId pTyp = STR_FRAME_DEFNAME;
1456 const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx();
1457 if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
1459 switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
1461 case SwNodeType::Grf:
1462 pTyp = STR_GRAPHIC_DEFNAME;
1463 break;
1464 case SwNodeType::Ole:
1465 pTyp = STR_OBJECT_DEFNAME;
1466 break;
1467 default: break;
1470 sName = lcl_GetUniqueFlyName(*this, pTyp, RES_FLYFRMFMT);
1472 rFormat.SetFormatName( sName, true );
1473 getIDocumentState().SetModified();
1476 void SwDoc::SetAllUniqueFlyNames()
1478 sal_Int32 n, nFlyNum = 0, nGrfNum = 0, nOLENum = 0;
1480 const OUString sFlyNm(SwResId(STR_FRAME_DEFNAME));
1481 const OUString sGrfNm(SwResId(STR_GRAPHIC_DEFNAME));
1482 const OUString sOLENm(SwResId(STR_OBJECT_DEFNAME));
1484 n = GetSpzFrameFormats()->size();
1485 if( 255 < n )
1486 n = 255;
1487 SwFrameFormatsV aArr;
1488 aArr.reserve( n );
1489 SwFrameFormat* pFlyFormat;
1490 bool bContainsAtPageObjWithContentAnchor = false;
1492 for( n = GetSpzFrameFormats()->size(); n; )
1494 pFlyFormat = (*GetSpzFrameFormats())[ --n ];
1495 if( RES_FLYFRMFMT == pFlyFormat->Which() )
1497 const OUString& aNm = pFlyFormat->GetName();
1498 if ( !aNm.isEmpty() )
1500 sal_Int32 *pNum = nullptr;
1501 sal_Int32 nLen = 0;
1502 if ( aNm.startsWith(sGrfNm) )
1504 nLen = sGrfNm.getLength();
1505 pNum = &nGrfNum;
1507 else if( aNm.startsWith(sFlyNm) )
1509 nLen = sFlyNm.getLength();
1510 pNum = &nFlyNum;
1512 else if( aNm.startsWith(sOLENm) )
1514 nLen = sOLENm.getLength();
1515 pNum = &nOLENum;
1518 if ( pNum )
1520 const sal_Int32 nNewLen = o3tl::toInt32(aNm.subView( nLen ));
1521 if (*pNum < nNewLen)
1522 *pNum = nNewLen;
1525 else
1526 // we want to set that afterwards
1527 aArr.push_back( pFlyFormat );
1530 if ( !bContainsAtPageObjWithContentAnchor )
1532 const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
1533 if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) &&
1534 rAnchor.GetAnchorNode() )
1536 bContainsAtPageObjWithContentAnchor = true;
1540 SetContainsAtPageObjWithContentAnchor( bContainsAtPageObjWithContentAnchor );
1542 for( n = aArr.size(); n; )
1544 pFlyFormat = aArr[ --n ];
1545 const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
1546 if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
1548 switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
1550 case SwNodeType::Grf:
1551 pFlyFormat->SetFormatName( sGrfNm + OUString::number( ++nGrfNum ));
1552 break;
1553 case SwNodeType::Ole:
1554 pFlyFormat->SetFormatName( sOLENm + OUString::number( ++nOLENum ));
1555 break;
1556 default:
1557 pFlyFormat->SetFormatName( sFlyNm + OUString::number( ++nFlyNum ));
1558 break;
1562 aArr.clear();
1564 if( GetFootnoteIdxs().empty() )
1565 return;
1567 SwTextFootnote::SetUniqueSeqRefNo( *this );
1568 // #i52775# Chapter footnotes did not get updated correctly.
1569 // Calling UpdateAllFootnote() instead of UpdateFootnote() solves this problem,
1570 // but I do not dare to call UpdateAllFootnote() in all cases: Safety first.
1571 if ( FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
1573 GetFootnoteIdxs().UpdateAllFootnote();
1575 else
1577 SwNodeIndex aTmp( GetNodes() );
1578 GetFootnoteIdxs().UpdateFootnote( aTmp.GetNode() );
1582 bool SwDoc::IsInHeaderFooter( const SwNode& rIdx ) const
1584 // That can also be a Fly in a Fly in the Header.
1585 // Is also used by sw3io, to determine if a Redline object is
1586 // in the Header or Footer.
1587 // Because Redlines are also attached to Start and EndNode,
1588 // the Index must not necessarily be from a ContentNode.
1589 const SwNode* pNd = &rIdx;
1590 const SwNode* pFlyNd = pNd->FindFlyStartNode();
1591 while( pFlyNd )
1593 // get up by using the Anchor
1594 #if OSL_DEBUG_LEVEL > 0
1595 std::vector<const SwFrameFormat*> checkFormats;
1596 for(sw::SpzFrameFormat* pFormat: *GetSpzFrameFormats())
1598 const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
1599 if( pIdx && pFlyNd == &pIdx->GetNode() )
1600 checkFormats.push_back( pFormat );
1602 #endif
1603 std::vector<SwFrameFormat*> const & rFlys(pFlyNd->GetAnchoredFlys());
1604 bool bFound(false);
1605 for (size_t i = 0; i < rFlys.size(); ++i)
1607 const SwFrameFormat *const pFormat = rFlys[i];
1608 const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
1609 if( pIdx && pFlyNd == &pIdx->GetNode() )
1611 #if OSL_DEBUG_LEVEL > 0
1612 auto checkPos = std::find(
1613 checkFormats.begin(), checkFormats.end(), pFormat );
1614 assert( checkPos != checkFormats.end());
1615 checkFormats.erase( checkPos );
1616 #endif
1617 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1618 if ((RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) ||
1619 !rAnchor.GetAnchorNode() )
1621 return false;
1624 pNd = rAnchor.GetAnchorNode();
1625 pFlyNd = pNd->FindFlyStartNode();
1626 bFound = true;
1627 break;
1630 if (!bFound)
1632 OSL_ENSURE(mbInReading, "Found a FlySection but not a Format!");
1633 return false;
1637 return nullptr != pNd->FindHeaderStartNode() ||
1638 nullptr != pNd->FindFooterStartNode();
1641 SvxFrameDirection SwDoc::GetTextDirection( const SwPosition& rPos,
1642 const Point* pPt ) const
1644 SvxFrameDirection nRet = SvxFrameDirection::Unknown;
1646 SwContentNode *pNd = rPos.GetNode().GetContentNode();
1648 // #i42921# - use new method <SwContentNode::GetTextDirection(..)>
1649 if ( pNd )
1651 nRet = pNd->GetTextDirection( rPos, pPt );
1653 if ( nRet == SvxFrameDirection::Unknown )
1655 const SvxFrameDirectionItem* pItem = nullptr;
1656 if( pNd )
1658 // Are we in a FlyFrame? Then look at that for the correct attribute
1659 const SwFrameFormat* pFlyFormat = pNd->GetFlyFormat();
1660 while( pFlyFormat )
1662 pItem = &pFlyFormat->GetFrameDir();
1663 if( SvxFrameDirection::Environment == pItem->GetValue() )
1665 pItem = nullptr;
1666 const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor();
1667 if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
1668 pAnchor->GetAnchorNode())
1670 pFlyFormat = pAnchor->GetAnchorNode()->GetFlyFormat();
1672 else
1673 pFlyFormat = nullptr;
1675 else
1676 pFlyFormat = nullptr;
1679 if( !pItem )
1681 const SwPageDesc* pPgDsc = pNd->FindPageDesc();
1682 if( pPgDsc )
1683 pItem = &pPgDsc->GetMaster().GetFrameDir();
1686 if( !pItem )
1687 pItem = &GetAttrPool().GetDefaultItem( RES_FRAMEDIR );
1688 nRet = pItem->GetValue();
1690 return nRet;
1693 bool SwDoc::IsInVerticalText( const SwPosition& rPos ) const
1695 const SvxFrameDirection nDir = GetTextDirection( rPos );
1696 return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir;
1699 o3tl::sorted_vector<SwRootFrame*> SwDoc::GetAllLayouts()
1701 o3tl::sorted_vector<SwRootFrame*> aAllLayouts;
1702 SwViewShell *pStart = getIDocumentLayoutAccess().GetCurrentViewShell();
1703 if(pStart)
1705 for(const SwViewShell& rShell : pStart->GetRingContainer())
1707 if(rShell.GetLayout())
1708 aAllLayouts.insert(rShell.GetLayout());
1711 return aAllLayouts;
1714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */