ITEM: Refactor ItemType
[LibreOffice.git] / sw / source / core / doc / textboxhelper.cxx
blob653bc2849624918fe519926b96ae177124c2dedd
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/.
8 */
10 #include <textboxhelper.hxx>
11 #include <dcontact.hxx>
12 #include <fmtcntnt.hxx>
13 #include <fmtanchr.hxx>
14 #include <fmtcnct.hxx>
15 #include <fmtornt.hxx>
16 #include <fmtfsize.hxx>
17 #include <doc.hxx>
18 #include <IDocumentLayoutAccess.hxx>
19 #include <IDocumentSettingAccess.hxx>
20 #include <IDocumentState.hxx>
21 #include <docsh.hxx>
22 #include <unocoll.hxx>
23 #include <unoframe.hxx>
24 #include <unodraw.hxx>
25 #include <unotextrange.hxx>
26 #include <cmdid.h>
27 #include <unomid.h>
28 #include <unoprnms.hxx>
29 #include <mvsave.hxx>
30 #include <fmtsrnd.hxx>
31 #include <fmtfollowtextflow.hxx>
32 #include <frmfmt.hxx>
33 #include <frameformats.hxx>
34 #include <dflyobj.hxx>
35 #include <swtable.hxx>
37 #include <editeng/unoprnms.hxx>
38 #include <editeng/memberids.h>
39 #include <svx/svdoashp.hxx>
40 #include <svx/svdpage.hxx>
41 #include <svl/itemiter.hxx>
42 #include <comphelper/sequenceashashmap.hxx>
43 #include <sal/log.hxx>
44 #include <tools/UnitConversion.hxx>
45 #include <svx/swframetypes.hxx>
46 #include <drawdoc.hxx>
47 #include <IDocumentUndoRedo.hxx>
48 #include <IDocumentDrawModelAccess.hxx>
49 #include <frmatr.hxx>
51 #include <com/sun/star/document/XActionLockable.hpp>
52 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
53 #include <com/sun/star/text/SizeType.hpp>
54 #include <com/sun/star/text/WrapTextMode.hpp>
55 #include <com/sun/star/text/XTextDocument.hpp>
56 #include <com/sun/star/text/XTextFrame.hpp>
57 #include <com/sun/star/table/BorderLine2.hpp>
58 #include <com/sun/star/text/WritingMode.hpp>
59 #include <com/sun/star/text/WritingMode2.hpp>
60 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
61 #include <com/sun/star/style/ParagraphAdjust.hpp>
62 #include <unotxdoc.hxx>
64 using namespace com::sun::star;
66 void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
68 assert(pShape);
69 assert(pObject);
70 assert(pShape == ::FindFrameFormat(pObject));
72 // If TextBox wasn't enabled previously
73 if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
74 return;
76 // Store the current text content of the shape
77 OUString sCopyableText;
79 if (bCopyText)
81 if (pObject)
83 uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
84 auto xCur = xSrcCnt->createTextCursor();
85 xCur->gotoStart(false);
86 xCur->gotoEnd(true);
87 sCopyableText = xCur->getText()->getString();
91 // Create the associated TextFrame and insert it into the document.
92 uno::Reference<text::XTextContent> xTextFrame(
93 SwXServiceProvider::MakeInstance(SwServiceType::TypeTextFrame, *pShape->GetDoc()),
94 uno::UNO_QUERY);
96 uno::Reference<text::XTextRange> xAnchor;
97 uno::Reference<text::XTextContent> xAnchorProvider(pObject->getWeakUnoShape().get(),
98 uno::UNO_QUERY);
99 assert(xAnchorProvider.is());
100 if (xAnchorProvider.is())
101 xAnchor = xAnchorProvider->getAnchor();
103 uno::Reference<text::XTextContentAppend> xTextContentAppend;
104 if (xAnchor)
105 xTextContentAppend.set(xAnchor->getText(), uno::UNO_QUERY);
107 if (!xTextContentAppend)
109 if (SwDocShell* pShell = pShape->GetDoc()->GetDocShell())
111 rtl::Reference<SwXTextDocument> xTextDocument(pShell->GetBaseModel());
112 xTextContentAppend.set(xTextDocument->getText(), uno::UNO_QUERY_THROW);
116 if (xAnchor)
118 // insertTextContentWithProperties would fail if xAnchor is in a different XText
119 assert(xAnchor->getText() == xTextContentAppend);
120 xTextContentAppend->insertTextContentWithProperties(xTextFrame, {}, xAnchor);
122 else
124 xTextContentAppend->appendTextContent(xTextFrame, uno::Sequence<beans::PropertyValue>());
127 // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
128 uno::Reference<text::XTextFrame> xRealTextFrame(xTextFrame, uno::UNO_QUERY);
129 auto pTextFrame = dynamic_cast<SwXTextFrame*>(xRealTextFrame.get());
130 assert(nullptr != pTextFrame);
131 SwFrameFormat* pFormat = pTextFrame->GetFrameFormat();
133 assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
134 assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
136 if (!pShape->GetOtherTextBoxFormats())
138 auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
139 pTextBox->AddTextBox(pObject, pFormat);
140 pShape->SetOtherTextBoxFormats(pTextBox);
141 pFormat->SetOtherTextBoxFormats(pTextBox);
143 else
145 auto& pTextBox = pShape->GetOtherTextBoxFormats();
146 pTextBox->AddTextBox(pObject, pFormat);
147 pFormat->SetOtherTextBoxFormats(pTextBox);
149 // Initialize properties.
150 uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
151 uno::Any aEmptyBorder{ table::BorderLine2() };
152 xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
153 xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
154 xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
155 xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
157 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
159 xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
161 xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
163 uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
164 assert(!xNamed->getName().isEmpty());
165 (void)xNamed;
167 // Link its text range to the original shape.
168 uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW);
169 SwUnoInternalPaM aInternalPaM(*pShape->GetDoc());
170 if (sw::XTextRangeToSwPaM(aInternalPaM, xTextBox))
172 SwAttrSet aSet(pShape->GetAttrSet());
173 SwFormatContent aContent(aInternalPaM.GetPointNode().StartOfSectionNode());
174 aSet.Put(aContent);
175 pShape->SetFormatAttr(aSet);
178 DoTextBoxZOrderCorrection(pShape, pObject);
180 // Also initialize the properties, which are not constant, but inherited from the shape's ones.
181 uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
182 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);
184 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
185 syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
186 xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
187 syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
188 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
189 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
190 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
191 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
192 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
193 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
194 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
195 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
196 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
197 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
198 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
199 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
200 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
201 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
202 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
203 // tdf#162075 shape word wrap to frame width type on shape creation.
204 bool bTextWordwrap = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
205 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
206 uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), pObject);
207 syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
208 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
209 text::WritingMode eMode;
210 if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
211 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);
213 changeAnchor(pShape, pObject);
214 syncTextBoxSize(pShape, pObject);
216 // Check if the shape had text before and move it to the new textframe
217 if (!bCopyText || sCopyableText.isEmpty())
218 return;
220 if (pObject)
222 auto pSourceText = DynCastSdrTextObj(pObject);
223 uno::Reference<text::XTextRange> xDestText(xRealTextFrame, uno::UNO_QUERY);
225 xDestText->setString(sCopyableText);
227 if (pSourceText)
228 pSourceText->SetText(OUString());
230 pShape->GetDoc()->getIDocumentState().SetModified();
234 void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
235 const uno::Reference<text::XTextFrame>& xNew)
237 // Do not set invalid data
238 assert(pShapeFormat && pObj && xNew);
239 // Firstly find the format of the new textbox.
240 SwFrameFormat* pFormat = nullptr;
241 if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
242 pFormat = pTextFrame->GetFrameFormat();
243 if (!pFormat)
244 return;
246 // If there is a format, check if the shape already has a textbox assigned to.
247 if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
249 // If it has a texbox, destroy it.
250 if (pTextBoxNode->GetTextBox(pObj))
251 pTextBoxNode->DelTextBox(pObj, true);
252 // And set the new one.
253 pTextBoxNode->AddTextBox(pObj, pFormat);
254 pFormat->SetOtherTextBoxFormats(pTextBoxNode);
256 else
258 // If the shape do not have a texbox node and textbox,
259 // create that for the shape.
260 auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
261 pTextBox->AddTextBox(pObj, pFormat);
262 pShapeFormat->SetOtherTextBoxFormats(pTextBox);
263 pFormat->SetOtherTextBoxFormats(pTextBox);
265 // Initialize its properties
266 uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
267 uno::Any aEmptyBorder{ table::BorderLine2() };
268 xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
269 xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
270 xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
271 xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
272 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
273 xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
274 xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
275 // Add a new name to it
276 uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
277 assert(!xNamed->getName().isEmpty());
278 (void)xNamed;
279 // And sync. properties.
280 uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
281 syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObj);
282 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
283 syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
284 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj);
285 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
286 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj);
287 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
288 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
289 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
290 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj);
291 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
292 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
293 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
294 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
295 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
296 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
297 syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
298 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
299 drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
300 if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
301 ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
302 != beans::PropertyState::PropertyState_DEFAULT_VALUE)
304 aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
305 .get<drawing::TextVerticalAdjust>();
307 xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::Any(aVertAdj));
308 text::WritingMode eMode;
309 if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
310 syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
312 // Do sync for the new textframe.
313 synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
314 synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
316 updateTextBoxMargin(pObj);
319 void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
321 // If a TextBox was enabled previously
322 auto& pTextBox = pShape->GetOtherTextBoxFormats();
323 if (pTextBox)
325 // Unlink the TextBox's text range from the original shape.
326 // Delete the associated TextFrame.
327 pTextBox->DelTextBox(pObject, true);
331 bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
332 const SdrObject* pObject)
334 DBG_TESTSOLARMUTEX();
335 assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
336 if (!pFormat || pFormat->Which() != nType)
337 return false;
339 auto& pTextBox = pFormat->GetOtherTextBoxFormats();
340 if (!pTextBox)
341 return false;
343 if (nType == RES_DRAWFRMFMT)
345 if (pObject)
346 return pTextBox->GetTextBox(pObject);
347 if (auto pObj = pFormat->FindRealSdrObject())
348 return pTextBox->GetTextBox(pObj);
351 if (nType == RES_FLYFRMFMT)
353 return pTextBox->GetOwnerShape();
356 return false;
359 bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
361 if (!pObj)
362 return false;
364 uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
365 if (!xShape)
366 return false;
367 return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
370 sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
372 sal_Int32 nRet = 0;
373 for (const rtl::Reference<SdrObject>& p : *pPage)
375 assert(p);
376 if (p->IsTextBox())
377 continue;
378 ++nRet;
380 return nRet;
383 sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
385 sal_Int32 nRet = 0;
386 for (const sw::SpzFrameFormat* pFormat : *rDoc.GetSpzFrameFormats())
388 if (isTextBox(pFormat, RES_FLYFRMFMT))
389 ++nRet;
391 return nRet;
394 uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
396 if (nIndex < 0)
397 throw lang::IndexOutOfBoundsException();
399 sal_Int32 nCount = 0; // Current logical index.
400 for (const rtl::Reference<SdrObject>& p : *pPage)
402 assert(p);
403 if (p->IsTextBox())
404 continue;
405 if (nCount == nIndex)
406 return uno::Any(p->getUnoShape());
407 ++nCount;
410 throw lang::IndexOutOfBoundsException();
413 sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
415 if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
417 sal_Int32 nOrder = 0; // Current logical order.
418 for (const rtl::Reference<SdrObject>& p : *pPage)
420 assert(p);
421 if (p->IsTextBox())
422 continue;
423 if (p == pObject)
424 return nOrder;
425 ++nOrder;
429 SAL_WARN("sw.core", "SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
430 return pObject->GetOrdNum();
433 void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
435 SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
436 if (pShape)
437 rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
440 SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
441 sal_uInt16 nType, const SdrObject* pObject)
443 SolarMutexGuard aGuard;
444 if (!isTextBox(pFormat, nType, pObject))
445 return nullptr;
447 if (nType == RES_DRAWFRMFMT)
449 if (pObject)
450 return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
451 if (pFormat->FindRealSdrObject())
452 return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
453 return nullptr;
455 if (nType == RES_FLYFRMFMT)
457 return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
459 return nullptr;
462 SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
464 auto pShape = dynamic_cast<SwXShape*>(xShape.get());
465 if (!pShape)
466 return nullptr;
468 SwFrameFormat* pFormat = pShape->GetFrameFormat();
469 return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
470 SdrObject::getSdrObjectFromXShape(xShape));
473 uno::Reference<text::XTextFrame>
474 SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
476 if (xShape)
478 auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
479 if (pFrameFormat)
481 auto pSdrObj = pFrameFormat->FindSdrObject();
482 if (pSdrObj)
484 return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
488 return {};
491 template <typename T>
492 static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
494 if (SwFrameFormat* pFormat
495 = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
497 uno::Reference<T> const xInterface(
498 getXWeak(SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat).get()),
499 uno::UNO_QUERY);
500 rAny <<= xInterface;
504 uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType,
505 SdrObject* pObj)
507 uno::Any aRet;
509 if (rType == cppu::UnoType<css::text::XTextAppend>::get())
511 lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
513 else if (rType == cppu::UnoType<css::text::XText>::get())
515 lcl_queryInterface<text::XText>(pShape, aRet, pObj);
517 else if (rType == cppu::UnoType<css::text::XTextRange>::get())
519 lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
522 return aRet;
525 tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
527 tools::Rectangle aRet;
528 aRet.SetEmpty();
530 assert(pShape);
532 auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
533 if (pCustomShape)
535 // Need to temporarily release the lock acquired in
536 // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
537 // see EnhancedCustomShapeEngine::getTextBounds().
538 uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
539 uno::UNO_QUERY);
540 sal_Int16 nLocks = 0;
541 if (xLockable.is())
542 nLocks = xLockable->resetActionLocks();
543 pCustomShape->GetTextBounds(aRet);
544 if (nLocks)
545 xLockable->setActionLocks(nLocks);
547 else if (pShape)
549 // fallback - get *any* bound rect we can possibly get hold of
550 aRet = pShape->GetCurrentBoundRect();
553 if (pShape)
555 // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
556 Point aPoint(pShape->GetSnapRect().Center());
557 Size aSize(pShape->GetLogicRect().GetSize());
558 aPoint.AdjustX(-(aSize.Width() / 2));
559 aPoint.AdjustY(-(aSize.Height() / 2));
560 tools::Rectangle aLogicRect(aPoint, aSize);
561 aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
564 return aRet;
567 void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
568 const css::uno::Any& rValue, SdrObject* pObj)
570 // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
571 if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
573 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
574 if (!pFormat)
575 return;
577 auto xTextFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
578 uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();
580 // Select all paragraphs in the textframe
581 xCursor->gotoStart(false);
582 xCursor->gotoEnd(true);
583 uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);
585 // And simply map the property
586 const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
587 switch (eValue)
589 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
590 xFrameParaProps->setPropertyValue(
591 UNO_NAME_PARA_ADJUST,
592 uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
593 break;
594 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
595 xFrameParaProps->setPropertyValue(
596 UNO_NAME_PARA_ADJUST,
597 uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
598 break;
599 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
600 xFrameParaProps->setPropertyValue(
601 UNO_NAME_PARA_ADJUST,
602 uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
603 break;
604 default:
605 SAL_WARN("sw.core",
606 "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
607 << static_cast<sal_Int32>(eValue));
608 break;
610 return;
613 if (rPropertyName == u"CustomShapeGeometry")
615 // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
616 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
618 SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
619 if (pObject)
621 tools::Rectangle aRectangle(pObject->GetSnapRect());
622 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
623 uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
624 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
625 uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
628 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
629 if (!pFormat)
630 return;
632 // Older documents or documents in ODF strict do not have WritingMode, but have used the
633 // TextRotateAngle values -90 and -270 to emulate these text directions of frames.
634 // ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed?
635 comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
636 auto it = aCustomShapeGeometry.find(u"TextPreRotateAngle"_ustr);
637 if (it == aCustomShapeGeometry.end())
639 it = aCustomShapeGeometry.find(u"TextRotateAngle"_ustr);
642 if (it != aCustomShapeGeometry.end())
644 auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
645 if (nAngle == 0)
647 nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
650 sal_Int16 nDirection = 0;
651 switch (nAngle)
653 case -90:
654 nDirection = text::WritingMode2::TB_RL90;
655 break;
656 case -270:
657 nDirection = text::WritingMode2::BT_LR;
658 break;
659 default:
660 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
661 "CustomShapeGeometry:TextPreRotateAngle: "
662 << nAngle);
663 break;
666 if (nDirection)
668 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
672 else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
673 syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
674 else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
675 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
676 else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
677 syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
678 else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
679 syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
680 else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
681 syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
682 else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
683 syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
684 else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
686 text::WritingMode eMode;
687 sal_Int16 eMode2;
688 if (rValue >>= eMode)
689 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
690 else if (rValue >>= eMode2)
691 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
693 else if (rPropertyName == u"WritingMode")
695 sal_Int16 eMode2;
696 if (rValue >>= eMode2)
697 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
699 else if (rPropertyName == u"TextWordWrap")
701 // tdf#81567 shape word wrap to frame width type on shape update.
702 bool bTextWordwrap{};
703 if (rValue >>= bTextWordwrap)
705 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
706 uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
709 else
710 SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: "
711 << static_cast<OUString>(rPropertyName));
714 void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
715 css::uno::Any& rValue)
717 if (!pShape)
718 return;
720 nMemberID &= ~CONVERT_TWIPS;
722 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
723 if (!pFormat)
724 return;
726 if (nWID != RES_CHAIN)
727 return;
729 switch (nMemberID)
731 case MID_CHAIN_PREVNAME:
732 case MID_CHAIN_NEXTNAME:
734 const SwFormatChain& rChain = pFormat->GetChain();
735 rChain.QueryValue(rValue, nMemberID);
737 break;
738 case MID_CHAIN_NAME:
739 rValue <<= pFormat->GetName();
740 break;
741 default:
742 SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
743 << o3tl::narrowing<sal_uInt16>(nMemberID));
744 break;
748 css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
750 if (!pShape)
751 return {};
753 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
754 if (!pFormat)
755 return {};
757 rtl::Reference<SwXTextFrame> xPropertySet
758 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
760 return xPropertySet->getPropertyValue(rPropName);
763 void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
764 const css::uno::Any& rValue, SdrObject* pObj)
766 // No shape yet? Then nothing to do, initial properties are set by create().
767 if (!pShape)
768 return;
770 uno::Any aValue(rValue);
771 nMemberID &= ~CONVERT_TWIPS;
773 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
774 if (!pFormat)
775 return;
777 OUString aPropertyName;
778 bool bAdjustX = false;
779 bool bAdjustY = false;
780 bool bAdjustSize = false;
781 switch (nWID)
783 case RES_HORI_ORIENT:
784 switch (nMemberID)
786 case MID_HORIORIENT_ORIENT:
787 aPropertyName = UNO_NAME_HORI_ORIENT;
788 break;
789 case MID_HORIORIENT_RELATION:
790 if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
791 aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
792 else
793 return;
794 break;
795 case MID_HORIORIENT_POSITION:
796 aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
797 bAdjustX = true;
798 break;
799 default:
800 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
801 << o3tl::narrowing<sal_uInt16>(nMemberID)
802 << " (which-id: " << nWID << ")");
803 break;
805 break;
806 case RES_LR_SPACE:
808 switch (nMemberID)
810 case MID_L_MARGIN:
811 aPropertyName = UNO_NAME_LEFT_MARGIN;
812 break;
813 case MID_R_MARGIN:
814 aPropertyName = UNO_NAME_RIGHT_MARGIN;
815 break;
816 default:
817 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
818 << o3tl::narrowing<sal_uInt16>(nMemberID)
819 << " (which-id: " << nWID << ")");
820 break;
822 break;
824 case RES_VERT_ORIENT:
825 switch (nMemberID)
827 case MID_VERTORIENT_ORIENT:
828 aPropertyName = UNO_NAME_VERT_ORIENT;
829 break;
830 case MID_VERTORIENT_RELATION:
831 if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
832 aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
833 else
834 return;
835 break;
836 case MID_VERTORIENT_POSITION:
837 aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
838 bAdjustY = true;
839 break;
840 default:
841 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
842 << o3tl::narrowing<sal_uInt16>(nMemberID)
843 << " (which-id: " << nWID << ")");
844 break;
846 break;
847 case RES_FRM_SIZE:
848 switch (nMemberID)
850 case MID_FRMSIZE_WIDTH_TYPE:
851 aPropertyName = UNO_NAME_WIDTH_TYPE;
852 break;
853 case MID_FRMSIZE_IS_AUTO_HEIGHT:
854 aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
855 break;
856 case MID_FRMSIZE_REL_HEIGHT_RELATION:
857 aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
858 break;
859 case MID_FRMSIZE_REL_WIDTH_RELATION:
860 aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
861 break;
862 default:
863 aPropertyName = UNO_NAME_SIZE;
864 bAdjustSize = true;
865 break;
867 break;
868 case RES_ANCHOR:
869 switch (nMemberID)
871 case MID_ANCHOR_ANCHORTYPE:
873 changeAnchor(pShape, pObj);
874 return;
876 break;
877 default:
878 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
879 << o3tl::narrowing<sal_uInt16>(nMemberID)
880 << " (which-id: " << nWID << ")");
881 break;
883 break;
884 case FN_TEXT_RANGE:
886 uno::Reference<text::XTextRange> xRange;
887 rValue >>= xRange;
888 SwUnoInternalPaM aInternalPaM(*pFormat->GetDoc());
889 if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
891 SwFormatAnchor aAnchor(pFormat->GetAnchor());
892 aAnchor.SetAnchor(aInternalPaM.Start());
893 pFormat->SetFormatAttr(aAnchor);
896 break;
897 case RES_CHAIN:
898 switch (nMemberID)
900 case MID_CHAIN_PREVNAME:
901 aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
902 break;
903 case MID_CHAIN_NEXTNAME:
904 aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
905 break;
906 default:
907 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
908 << o3tl::narrowing<sal_uInt16>(nMemberID)
909 << " (which-id: " << nWID << ")");
910 break;
912 break;
913 case RES_TEXT_VERT_ADJUST:
914 aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
915 break;
916 case RES_BOX:
917 switch (nMemberID)
919 case LEFT_BORDER_DISTANCE:
920 aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
921 break;
922 case RIGHT_BORDER_DISTANCE:
923 aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
924 break;
925 case TOP_BORDER_DISTANCE:
926 aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
927 break;
928 case BOTTOM_BORDER_DISTANCE:
929 aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
930 break;
931 default:
932 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
933 << o3tl::narrowing<sal_uInt16>(nMemberID)
934 << " (which-id: " << nWID << ")");
935 break;
937 break;
938 case RES_OPAQUE:
939 aPropertyName = UNO_NAME_OPAQUE;
940 break;
941 case RES_FRAMEDIR:
942 aPropertyName = UNO_NAME_WRITING_MODE;
943 break;
944 case RES_WRAP_INFLUENCE_ON_OBJPOS:
945 switch (nMemberID)
947 case MID_ALLOW_OVERLAP:
948 aPropertyName = UNO_NAME_ALLOW_OVERLAP;
949 break;
950 default:
951 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
952 << o3tl::narrowing<sal_uInt16>(nMemberID)
953 << " (which-id: " << nWID << ")");
954 break;
956 break;
957 default:
958 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
959 << nWID << " (member-id: "
960 << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
961 break;
964 if (aPropertyName.isEmpty())
965 return;
967 // Position/size should be the text position/size, not the shape one as-is.
968 if (bAdjustX || bAdjustY || bAdjustSize)
970 changeAnchor(pShape, pObj);
971 tools::Rectangle aRect
972 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
973 if (!aRect.IsEmpty())
975 if (bAdjustX || bAdjustY)
977 sal_Int32 nValue;
978 if (aValue >>= nValue)
980 nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
981 aValue <<= nValue;
984 else if (bAdjustSize)
986 awt::Size aSize(convertTwipToMm100(aRect.getOpenWidth()),
987 convertTwipToMm100(aRect.getOpenHeight()));
988 aValue <<= aSize;
992 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
993 rtl::Reference<SwXTextFrame> const xPropertySet
994 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
995 xPropertySet->setPropertyValue(aPropertyName, aValue);
998 void SwTextBoxHelper::saveLinks(const sw::FrameFormats<sw::SpzFrameFormat*>& rFormats,
999 std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
1001 for (const auto pFormat : rFormats)
1003 if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
1004 rLinks[pFormat] = pTextBox;
1008 void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
1009 SavedLink& rSavedLinks)
1011 std::size_t i = 0;
1012 for (const auto& rIt : rOld)
1014 auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
1015 if (aTextBoxIt != rSavedLinks.end())
1017 std::size_t j = 0;
1018 for (const auto& rJt : rOld)
1020 if (rJt.GetFormat() == aTextBoxIt->second)
1021 rNew[i]->SetFormatAttr(rNew[j]->GetContent());
1022 ++j;
1025 ++i;
1029 text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
1031 text::TextContentAnchorType aAnchorType;
1032 switch (rAnchorID)
1034 case RndStdIds::FLY_AS_CHAR:
1035 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
1036 break;
1037 case RndStdIds::FLY_AT_CHAR:
1038 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
1039 break;
1040 case RndStdIds::FLY_AT_PARA:
1041 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
1042 break;
1043 case RndStdIds::FLY_AT_PAGE:
1044 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
1045 break;
1046 case RndStdIds::FLY_AT_FLY:
1047 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
1048 break;
1049 default:
1050 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
1051 SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
1052 break;
1054 return aAnchorType;
1057 void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet,
1058 SdrObject* pObj)
1060 SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
1061 if (!pFormat)
1062 return;
1064 const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
1065 const bool bLayoutInCell = rShape.GetFollowTextFlow().GetValue()
1066 && rShape.GetAnchor().GetAnchorNode()
1067 && rShape.GetAnchor().GetAnchorNode()->FindTableNode();
1068 SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
1070 SfxItemIter aIter(rSet);
1071 const SfxPoolItem* pItem = aIter.GetCurItem();
1075 switch (pItem->Which())
1077 case RES_VERT_ORIENT:
1079 // The new position can be with anchor changing so sync it!
1080 const text::TextContentAnchorType aNewAnchorType
1081 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1082 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
1083 pObj);
1084 if (bInlineAnchored || bLayoutInCell)
1085 return;
1086 SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));
1088 tools::Rectangle aRect
1089 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1090 if (!aRect.IsEmpty())
1091 aOrient.SetPos(aOrient.GetPos() + aRect.Top());
1093 if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
1094 && rShape.GetAnchor().GetPageNum() != 0)
1095 aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
1096 aTextBoxSet.Put(aOrient);
1098 // restore height (shrunk for extending beyond the page bottom - tdf#91260)
1099 SwFormatFrameSize aSize(pFormat->GetFrameSize());
1100 if (!aRect.IsEmpty())
1102 aSize.SetHeight(aRect.getOpenHeight());
1103 aTextBoxSet.Put(aSize);
1106 break;
1107 case RES_HORI_ORIENT:
1109 // The new position can be with anchor changing so sync it!
1110 const text::TextContentAnchorType aNewAnchorType
1111 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1112 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
1113 pObj);
1114 if (bInlineAnchored || bLayoutInCell)
1115 return;
1116 SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));
1118 tools::Rectangle aRect
1119 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1120 if (!aRect.IsEmpty())
1121 aOrient.SetPos(aOrient.GetPos() + aRect.Left());
1123 if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
1124 && rShape.GetAnchor().GetPageNum() != 0)
1125 aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
1126 aTextBoxSet.Put(aOrient);
1128 break;
1129 case RES_FRM_SIZE:
1131 // In case the shape got resized, then we need to adjust both
1132 // the position and the size of the textbox (e.g. larger
1133 // rounded edges of a rectangle -> need to push right/down the
1134 // textbox).
1135 SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
1136 SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
1137 SwFormatFrameSize aSize(pFormat->GetFrameSize());
1139 tools::Rectangle aRect
1140 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1141 if (!aRect.IsEmpty())
1143 if (!bInlineAnchored)
1145 aVertOrient.SetPos(
1146 (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
1147 + aRect.Top());
1148 aHoriOrient.SetPos(
1149 (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
1150 + aRect.Left());
1152 aTextBoxSet.Put(aVertOrient);
1153 aTextBoxSet.Put(aHoriOrient);
1156 aSize.SetWidth(aRect.getOpenWidth());
1157 aSize.SetHeight(aRect.getOpenHeight());
1158 aTextBoxSet.Put(aSize);
1161 break;
1162 case RES_ANCHOR:
1164 if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
1165 // the anchor have to be synced
1167 const text::TextContentAnchorType aNewAnchorType
1168 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1169 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
1170 uno::Any(aNewAnchorType), pObj);
1172 else
1174 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
1175 "shape different from the textframe!");
1178 break;
1179 default:
1180 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
1181 << pItem->Which());
1182 break;
1185 pItem = aIter.NextItem();
1186 } while (pItem && (0 != pItem->Which()));
1188 if (aTextBoxSet.Count())
1190 auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
1191 pFormat->SetFormatAttr(aTextBoxSet);
1193 DoTextBoxZOrderCorrection(&rShape, pObj);
1196 void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
1198 if (!pObj)
1199 return;
1200 uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
1201 if (!xShape)
1202 return;
1203 uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);
1205 auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
1206 if (!pParentFormat)
1207 return;
1209 // Sync the padding
1210 syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
1211 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
1212 syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
1213 xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
1214 syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
1215 xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
1216 syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
1217 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
1219 // Sync the text aligning
1220 syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
1221 xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
1222 syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
1223 xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
1225 // tdf137803: Sync autogrow:
1226 const bool bIsAutoGrow
1227 = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
1228 const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
1230 syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
1231 pObj);
1233 syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
1234 uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
1236 changeAnchor(pParentFormat, pObj);
1237 DoTextBoxZOrderCorrection(pParentFormat, pObj);
1240 bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
1242 if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1244 if (!isAnchorSyncNeeded(pShape, pFormat))
1246 doTextBoxPositioning(pShape, pObj);
1247 DoTextBoxZOrderCorrection(pShape, pObj);
1248 if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
1249 && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
1250 && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
1252 SwFormatVertOrient aTmp = pFormat->GetVertOrient();
1253 aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
1254 pFormat->SetFormatAttr(aTmp);
1257 return false;
1260 const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
1261 const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
1263 const auto pOldCnt = rOldAnch.GetContentAnchor();
1264 const auto pNewCnt = rNewAnch.GetContentAnchor();
1266 const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());
1270 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1271 ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
1272 rtl::Reference<SwXTextFrame> const xPropertySet
1273 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
1274 if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1275 && rNewAnch.GetPageNum())
1277 uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
1278 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
1279 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1280 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
1281 uno::Any(rNewAnch.GetPageNum()));
1283 else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
1285 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1287 assert(pNewCnt);
1288 uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
1289 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1290 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1291 uno::Any(text::RelOrientation::CHAR));
1292 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
1293 uno::Any(text::RelOrientation::PRINT_AREA));
1294 SwFormatAnchor aPos(pFormat->GetAnchor());
1295 aPos.SetAnchor(pNewCnt);
1296 pFormat->SetFormatAttr(aPos);
1298 else
1300 uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
1301 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1302 aShapeHorRelOrient);
1303 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1304 pFormat->SetFormatAttr(rNewAnch);
1307 else
1309 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1311 assert(pNewCnt);
1312 uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
1313 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1314 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1315 uno::Any(text::RelOrientation::CHAR));
1316 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
1317 uno::Any(text::RelOrientation::PRINT_AREA));
1318 SwFormatAnchor aPos(pFormat->GetAnchor());
1319 aPos.SetAnchor(pNewCnt);
1320 pFormat->SetFormatAttr(aPos);
1322 else
1324 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1325 aShapeHorRelOrient);
1326 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1327 && rNewAnch.GetPageNum() == 0)
1329 pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
1331 else
1332 pFormat->SetFormatAttr(pShape->GetAnchor());
1336 catch (uno::Exception& e)
1338 SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
1341 doTextBoxPositioning(pShape, pObj);
1342 DoTextBoxZOrderCorrection(pShape, pObj);
1343 return true;
1346 return false;
1349 bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
1351 // Set the position of the textboxes according to the position of its shape-pair
1352 const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
1353 if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1355 // Do not create undo entry for the positioning
1356 ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
1357 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1358 // Special treatment for AS_CHAR textboxes:
1359 if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1361 // Get the text area of the shape
1362 tools::Rectangle aRect
1363 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
1365 // Set the textbox position at the X-axis:
1366 SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
1367 if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
1368 aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
1370 // tdf#152142: For RTL, positioning is relative to the right
1371 if (pShape->GetLayoutDir() == SwFrameFormat::HORI_R2L)
1373 auto nRightSpace = pShape->GetLRSpace().ResolveRight({});
1375 const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
1376 DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS);
1377 if (bMSOLayout)
1379 aNewHOri.SetPos(-aRect.Right() + nRightSpace
1380 + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
1382 else
1384 aNewHOri.SetPos(aRect.Right() + nRightSpace
1385 + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
1388 else
1390 auto nLeftSpace = pShape->GetLRSpace().ResolveLeft({});
1391 aNewHOri.SetPos(aRect.Left() + nLeftSpace
1392 + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
1395 SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
1397 // Special handling of group textboxes
1398 if (bIsGroupObj)
1400 // There are the following cases:
1401 // case 1: The textbox should be in that position where the shape is.
1402 // case 2: The shape has negative offset so that have to be subtracted
1403 // case 3: The shape and its parent shape also has negative offset, so subtract
1404 aNewVOri.SetPos(
1405 ((pObj->GetRelativePos().getY()) > 0
1406 ? (pShape->GetVertOrient().GetPos() > 0
1407 ? pObj->GetRelativePos().getY()
1408 : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
1409 : (pShape->GetVertOrient().GetPos() > 0
1410 ? 0 // Is this can be a variation?
1411 : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
1412 + aRect.Top());
1414 else
1416 // Simple textboxes: vertical position equals to the vertical offset of the shape
1417 aNewVOri.SetPos(
1418 ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
1419 + aRect.Top());
1422 // Special cases when the shape is aligned to the line
1423 if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
1425 aNewVOri.SetVertOrient(text::VertOrientation::NONE);
1426 switch (pShape->GetVertOrient().GetVertOrient())
1428 // Top aligned shape
1429 case text::VertOrientation::TOP:
1430 case text::VertOrientation::CHAR_TOP:
1431 case text::VertOrientation::LINE_TOP:
1433 aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
1434 break;
1436 // Bottom aligned shape
1437 case text::VertOrientation::BOTTOM:
1438 case text::VertOrientation::CHAR_BOTTOM:
1439 case text::VertOrientation::LINE_BOTTOM:
1441 aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
1442 break;
1444 // Center aligned shape
1445 case text::VertOrientation::CENTER:
1446 case text::VertOrientation::CHAR_CENTER:
1447 case text::VertOrientation::LINE_CENTER:
1449 aNewVOri.SetPos(aNewVOri.GetPos()
1450 + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
1451 break;
1453 default:
1454 break;
1458 pFormat->SetFormatAttr(aNewHOri);
1459 pFormat->SetFormatAttr(aNewVOri);
1461 // Other cases when the shape has different anchor from AS_CHAR
1462 else
1464 // Text area of the shape
1465 tools::Rectangle aRect
1466 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
1468 // Set the same position as the (child) shape has
1469 SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
1470 if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
1471 aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
1473 aNewHOri.SetPos(
1474 (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
1475 + aRect.Left());
1476 SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
1477 aNewVOri.SetPos(
1478 (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
1479 + aRect.Top());
1481 // Get the distance of the child shape inside its parent
1482 const auto nInshapePos
1483 = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
1484 : Point();
1486 // Special case: the shape has relative position from the page
1487 if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1488 && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
1490 aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
1491 aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
1492 + aRect.Left());
1495 if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1496 && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
1498 aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
1499 aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
1500 + aRect.Top());
1503 // Other special case: shape is inside a table or floating table following the text flow
1504 if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetAnchorNode()
1505 && pShape->GetAnchor().GetAnchorNode()->FindTableNode())
1507 // WARNING: It is highly likely that everything here is simplistic and incomplete.
1509 // Microsoft allows WrapThrough shapes to be placed outside of the cell
1510 // despite having specified layoutInCell.
1511 // (Re-using existing, appropriately-named, compat flag to identify MSO formats.)
1512 const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
1513 DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
1515 // Table position
1516 Point nTableOffset;
1517 // Floating table
1518 if (auto pFly
1519 = pShape->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
1521 if (auto pFlyFormat = pFly->GetFlyFormat())
1523 nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
1524 nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
1527 else
1528 // Normal table
1530 auto pTableNode = pShape->GetAnchor().GetAnchorNode()->FindTableNode();
1531 if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
1533 nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
1534 nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
1538 // stay within the cell limits (since following text flow)
1539 // unless this is based on a Microsoft layout which has a through-wrap exception.
1540 bool bWrapThrough = false;
1541 getShapeWrapThrough(pShape, bWrapThrough);
1542 sal_Int32 nPos = aNewHOri.GetPos();
1543 if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
1544 nPos = 0;
1545 // Add the table positions to the textbox
1546 aNewHOri.SetPos(nPos + nTableOffset.getX());
1548 if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1549 || pShape->GetVertOrient().GetRelationOrient()
1550 == text::RelOrientation::PAGE_PRINT_AREA)
1552 nPos = aNewVOri.GetPos();
1553 if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
1554 nPos = 0;
1555 aNewVOri.SetPos(nPos + nTableOffset.getY());
1559 pFormat->SetFormatAttr(aNewHOri);
1560 pFormat->SetFormatAttr(aNewVOri);
1562 return true;
1565 return false;
1568 bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
1570 if (!pShape || !pObj)
1571 return false;
1573 if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1575 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1576 const auto aSize = getRelativeTextRectangle(pObj).GetSize();
1577 if (!aSize.IsEmpty())
1579 SwFormatFrameSize aFrameSize(pTextBox->GetFrameSize());
1580 aFrameSize.SetSize(aSize);
1581 return pTextBox->SetFormatAttr(aFrameSize);
1585 return false;
1588 bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
1590 // TODO: do this with group shape textboxes.
1591 SdrObject* pShpObj = nullptr;
1593 pShpObj = pShape->FindRealSdrObject();
1595 if (!pShpObj)
1597 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1598 "No Valid SdrObject for the shape!");
1599 return false;
1602 auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
1603 if (!pTextBox)
1604 return false;
1605 SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
1606 if (!pFrmObj)
1608 if (SwFlyFrameFormat* pFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pTextBox))
1610 // During loading there is no ready SdrObj for z-ordering, so create and cache it here
1611 pFrmObj = SwXTextFrame::GetOrCreateSdrObject(*pFlyFrameFormat);
1614 if (!pFrmObj)
1616 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1617 "No Valid SdrObject for the frame!");
1618 return false;
1620 // Get the draw model from the doc
1621 SwDrawModel* pDrawModel = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
1622 if (!pDrawModel)
1624 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1625 "No Valid Draw model for SdrObject for the shape!");
1626 return false;
1628 if (!pFrmObj->getParentSdrObjListFromSdrObject())
1630 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1631 "Frame object is not inserted into any parent");
1632 return false;
1634 // Not really sure this will work on all pages, but it seems it will.
1635 // If the shape is behind the frame, is good, but if there are some objects
1636 // between of them that is wrong so put the frame exactly one level higher
1637 // than the shape.
1638 pFrmObj->ensureSortedImmediatelyAfter(*pShpObj);
1639 return true; // Success
1642 void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
1643 SwFrameFormat* pFormat, SdrObject* pObj)
1645 if (auto pChildren = pObj->getChildrenOfSdrObject())
1647 for (const rtl::Reference<SdrObject>& pChildObj : *pChildren)
1648 synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildObj.get());
1650 else
1652 (*pFunc)(pFormat, pObj);
1656 std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
1657 SwFrameFormat* pFormat)
1659 std::vector<SwFrameFormat*> vRet;
1660 if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
1662 for (const rtl::Reference<SdrObject>& pObj : *pChildren)
1664 auto pChildTextBoxes = CollectTextBoxes(pObj.get(), pFormat);
1665 for (auto& rChildTextBox : pChildTextBoxes)
1666 vRet.push_back(rChildTextBox);
1669 else
1671 if (isTextBox(pFormat, RES_DRAWFRMFMT, pGroupObject))
1672 vRet.push_back(getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pGroupObject));
1674 return vRet;
1677 bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
1679 if (!pFirst)
1680 return false;
1682 if (!pSecond)
1683 return false;
1685 if (pFirst == pSecond)
1686 return false;
1688 if (!pFirst->GetOtherTextBoxFormats())
1689 return false;
1691 if (!pSecond->GetOtherTextBoxFormats())
1692 return false;
1694 if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
1695 return false;
1697 if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
1698 || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
1700 const auto& rShapeAnchor
1701 = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
1702 const auto& rFrameAnchor
1703 = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
1705 if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
1707 if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
1709 if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
1710 return true;
1712 return false;
1715 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1716 && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
1718 if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
1719 return false;
1720 else
1721 return true;
1724 return true;
1727 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
1728 && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
1730 if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
1732 if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
1733 return true;
1735 return false;
1738 return true;
1740 return false;
1743 bool SwTextBoxHelper::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat)
1745 SdrObject* pSdrObj = const_cast<SdrObject*>(rFrameFormat.FindRealSdrObject());
1746 if (!pSdrObj)
1747 return false;
1749 uno::Reference<beans::XPropertySet> xPropertySet(pSdrObj->getUnoShape(), uno::UNO_QUERY);
1750 if (!xPropertySet.is())
1751 return false;
1753 uno::Reference<beans::XPropertySetInfo> xPropSetInfo(xPropertySet->getPropertySetInfo());
1754 if (!xPropSetInfo.is() || !xPropSetInfo->hasPropertyByName(u"FrameInteropGrabBag"_ustr))
1755 return false;
1757 bool bRet = false;
1758 uno::Sequence<beans::PropertyValue> propList;
1759 xPropertySet->getPropertyValue(u"FrameInteropGrabBag"_ustr) >>= propList;
1760 auto pProp = std::find_if(
1761 std::cbegin(propList), std::cend(propList),
1762 [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
1763 if (pProp != std::cend(propList))
1764 pProp->Value >>= bRet;
1766 return bRet;
1769 SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
1771 assert(pOwnerShape);
1772 assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
1774 m_bIsCloningInProgress = false;
1775 m_bLock = false;
1777 m_pOwnerShapeFormat = pOwnerShape;
1778 if (!m_pTextBoxes.empty())
1779 m_pTextBoxes.clear();
1782 SwTextBoxNode::~SwTextBoxNode()
1784 if (!m_pTextBoxes.empty())
1786 SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
1787 assert(false);
1791 void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
1793 assert(pNewTextBox);
1794 assert(pNewTextBox->Which() == RES_FLYFRMFMT);
1796 assert(pDrawObject);
1798 SwTextBoxElement aElem;
1799 aElem.m_pDrawObject = pDrawObject;
1800 aElem.m_pTextBoxFormat = pNewTextBox;
1802 for (const auto& rE : m_pTextBoxes)
1804 if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
1806 SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
1807 return;
1811 auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
1812 if (pSwFlyDraw)
1814 pSwFlyDraw->SetTextBox(true);
1816 m_pTextBoxes.push_back(aElem);
1819 void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
1821 assert(pDrawObject);
1822 if (m_pTextBoxes.empty())
1823 return;
1825 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
1827 if (it->m_pDrawObject == pDrawObject)
1829 if (bDelFromDoc)
1831 it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1832 it->m_pTextBoxFormat);
1833 // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
1834 // then the ~SwFrameFormat() will call this method again to remove the entry.
1835 return;
1837 else
1839 it = m_pTextBoxes.erase(it);
1840 return;
1843 ++it;
1846 SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
1849 void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
1851 if (m_pTextBoxes.empty())
1852 return;
1854 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
1856 if (it->m_pTextBoxFormat == pTextBox)
1858 if (bDelFromDoc)
1860 it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1861 it->m_pTextBoxFormat);
1862 // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
1863 // then the ~SwFrameFormat() will call this method again to remove the entry.
1864 return;
1866 else
1868 it = m_pTextBoxes.erase(it);
1869 return;
1872 ++it;
1875 SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
1878 SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
1880 assert(pDrawObject);
1881 assert(m_pOwnerShapeFormat);
1883 if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
1885 if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
1887 SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
1891 if (m_bLock)
1892 return nullptr;
1894 if (!m_pTextBoxes.empty())
1896 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
1898 if (it->m_pDrawObject == pDrawObject)
1900 return it->m_pTextBoxFormat;
1903 SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
1906 return nullptr;
1909 void SwTextBoxNode::ClearAll()
1911 // If this called from ~SwDoc(), then only the address entries
1912 // have to be removed, the format will be deleted by the
1913 // the mpSpzFrameFormatTable->DeleteAndDestroyAll() in ~SwDoc()!
1914 if (m_pOwnerShapeFormat->GetDoc()->IsInDtor())
1916 m_pTextBoxes.clear();
1917 return;
1920 // For loop control
1921 sal_uInt16 nLoopCount = 0;
1923 // Reference not enough, copy needed.
1924 const size_t nTextBoxCount = m_pTextBoxes.size();
1926 // For loop has problems: When one entry deleted, the iterator has
1927 // to be refreshed according to the new situation. So using While() instead.
1928 while (!m_pTextBoxes.empty())
1930 // Delete the last textbox of the vector from the doc
1931 // (what will call deregister in ~SwFrameFormat()
1932 m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1933 m_pTextBoxes.back().m_pTextBoxFormat);
1935 // Check if we are looping
1936 if (nLoopCount > (nTextBoxCount + 1))
1938 SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
1939 break;
1941 else
1943 nLoopCount++;
1947 // Ensure the vector is empty.
1948 if (!m_pTextBoxes.empty())
1950 SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
1951 assert(false);
1955 bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
1957 std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
1959 std::map<SdrObject*, SwFrameFormat*> aRet;
1960 for (auto& rElem : m_pTextBoxes)
1962 aRet.emplace(rElem.m_pDrawObject, rElem.m_pTextBoxFormat);
1964 return aRet;
1967 void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
1968 bool bSetAttr, bool bMakeFrame) const
1970 if (!o_pTarget || !pDoc)
1971 return;
1973 if (o_pTarget->Which() != RES_DRAWFRMFMT)
1974 return;
1976 if (m_bIsCloningInProgress)
1977 return;
1979 m_bIsCloningInProgress = true;
1981 Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
1982 o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
1984 m_bIsCloningInProgress = false;
1986 for (auto& rElem : m_pTextBoxes)
1988 SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1989 SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1990 SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1991 SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1995 void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
1996 const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
1997 bool bMakeFrame) const
1999 if (!pSrcObj || !pDestObj)
2000 return;
2002 auto pSrcList = pSrcObj->getChildrenOfSdrObject();
2003 auto pDestList = pDestObj->getChildrenOfSdrObject();
2005 if (pSrcList && pDestList)
2007 if (pSrcList->GetObjCount() != pDestList->GetObjCount())
2009 SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
2010 return;
2013 for (auto itSrc = pSrcList->begin(), itDest = pDestList->begin(); itSrc != pSrcList->end();
2014 ++itSrc, ++itDest)
2016 Clone_Impl(pDoc, rNewAnc, o_pTarget, itSrc->get(), itDest->get(), bSetAttr, bMakeFrame);
2018 return;
2021 if (!pSrcList && !pDestList)
2023 if (auto pSrcFormat = GetTextBox(pSrcObj))
2025 SwFormatAnchor aNewAnchor(rNewAnc);
2026 if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
2028 aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
2030 if (!bMakeFrame)
2031 bMakeFrame = true;
2034 if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
2035 *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
2037 if (!o_pTarget->GetOtherTextBoxFormats())
2039 auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
2040 o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
2041 pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
2042 pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
2044 else
2046 o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
2047 pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
2049 o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
2055 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */