android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / textboxhelper.cxx
blob1bb09d55b753730927c977bf83a7f2d115e62f01
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 <fmtcntnt.hxx>
12 #include <fmtanchr.hxx>
13 #include <fmtcnct.hxx>
14 #include <fmtornt.hxx>
15 #include <fmtfsize.hxx>
16 #include <doc.hxx>
17 #include <IDocumentLayoutAccess.hxx>
18 #include <IDocumentState.hxx>
19 #include <docsh.hxx>
20 #include <unocoll.hxx>
21 #include <unoframe.hxx>
22 #include <unodraw.hxx>
23 #include <unotextrange.hxx>
24 #include <cmdid.h>
25 #include <unomid.h>
26 #include <unoprnms.hxx>
27 #include <mvsave.hxx>
28 #include <fmtsrnd.hxx>
29 #include <fmtfollowtextflow.hxx>
30 #include <frmfmt.hxx>
31 #include <frameformats.hxx>
32 #include <dflyobj.hxx>
33 #include <swtable.hxx>
35 #include <editeng/unoprnms.hxx>
36 #include <editeng/memberids.h>
37 #include <svx/svdoashp.hxx>
38 #include <svx/svdpage.hxx>
39 #include <svl/itemiter.hxx>
40 #include <comphelper/sequenceashashmap.hxx>
41 #include <sal/log.hxx>
42 #include <tools/UnitConversion.hxx>
43 #include <svx/swframetypes.hxx>
44 #include <drawdoc.hxx>
45 #include <IDocumentUndoRedo.hxx>
46 #include <IDocumentDrawModelAccess.hxx>
47 #include <frmatr.hxx>
49 #include <com/sun/star/document/XActionLockable.hpp>
50 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
51 #include <com/sun/star/text/SizeType.hpp>
52 #include <com/sun/star/text/WrapTextMode.hpp>
53 #include <com/sun/star/text/XTextDocument.hpp>
54 #include <com/sun/star/text/XTextFrame.hpp>
55 #include <com/sun/star/table/BorderLine2.hpp>
56 #include <com/sun/star/text/WritingMode.hpp>
57 #include <com/sun/star/text/WritingMode2.hpp>
58 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
59 #include <com/sun/star/style/ParagraphAdjust.hpp>
61 using namespace com::sun::star;
63 void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
65 assert(pShape);
66 assert(pObject);
68 // If TextBox wasn't enabled previously
69 if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
70 return;
72 // Store the current text content of the shape
73 OUString sCopyableText;
75 if (bCopyText)
77 if (pObject)
79 uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
80 auto xCur = xSrcCnt->createTextCursor();
81 xCur->gotoStart(false);
82 xCur->gotoEnd(true);
83 sCopyableText = xCur->getText()->getString();
87 // Create the associated TextFrame and insert it into the document.
88 uno::Reference<text::XTextContent> xTextFrame(
89 SwXServiceProvider::MakeInstance(SwServiceType::TypeTextFrame, *pShape->GetDoc()),
90 uno::UNO_QUERY);
91 uno::Reference<text::XTextDocument> xTextDocument(
92 pShape->GetDoc()->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
93 uno::Reference<text::XTextContentAppend> xTextContentAppend(xTextDocument->getText(),
94 uno::UNO_QUERY);
95 xTextContentAppend->appendTextContent(xTextFrame, uno::Sequence<beans::PropertyValue>());
97 // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
98 uno::Reference<text::XTextFrame> xRealTextFrame(xTextFrame, uno::UNO_QUERY);
99 auto pTextFrame = dynamic_cast<SwXTextFrame*>(xRealTextFrame.get());
100 assert(nullptr != pTextFrame);
101 SwFrameFormat* pFormat = pTextFrame->GetFrameFormat();
103 assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
104 assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
106 if (!pShape->GetOtherTextBoxFormats())
108 auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
109 pTextBox->AddTextBox(pObject, pFormat);
110 pShape->SetOtherTextBoxFormats(pTextBox);
111 pFormat->SetOtherTextBoxFormats(pTextBox);
113 else
115 auto& pTextBox = pShape->GetOtherTextBoxFormats();
116 pTextBox->AddTextBox(pObject, pFormat);
117 pFormat->SetOtherTextBoxFormats(pTextBox);
119 // Initialize properties.
120 uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
121 uno::Any aEmptyBorder{ table::BorderLine2() };
122 xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
123 xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
124 xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
125 xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
127 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
129 xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
131 xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
133 uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
134 assert(!xNamed->getName().isEmpty());
135 (void)xNamed;
137 // Link its text range to the original shape.
138 uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW);
139 SwUnoInternalPaM aInternalPaM(*pShape->GetDoc());
140 if (sw::XTextRangeToSwPaM(aInternalPaM, xTextBox))
142 SwAttrSet aSet(pShape->GetAttrSet());
143 SwFormatContent aContent(aInternalPaM.GetPointNode().StartOfSectionNode());
144 aSet.Put(aContent);
145 pShape->SetFormatAttr(aSet);
148 DoTextBoxZOrderCorrection(pShape, pObject);
150 // Also initialize the properties, which are not constant, but inherited from the shape's ones.
151 uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
152 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);
154 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
155 syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
156 xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
157 syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
158 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
159 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
160 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
161 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
162 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
163 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
164 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
165 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
166 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
167 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
168 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
169 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
170 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
171 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
172 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
173 syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
174 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
175 text::WritingMode eMode;
176 if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
177 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);
179 changeAnchor(pShape, pObject);
180 syncTextBoxSize(pShape, pObject);
182 // Check if the shape had text before and move it to the new textframe
183 if (!bCopyText || sCopyableText.isEmpty())
184 return;
186 if (pObject)
188 auto pSourceText = DynCastSdrTextObj(pObject);
189 uno::Reference<text::XTextRange> xDestText(xRealTextFrame, uno::UNO_QUERY);
191 xDestText->setString(sCopyableText);
193 if (pSourceText)
194 pSourceText->SetText(OUString());
196 pShape->GetDoc()->getIDocumentState().SetModified();
200 void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
201 uno::Reference<text::XTextFrame> xNew)
203 // Do not set invalid data
204 assert(pShapeFormat && pObj && xNew);
205 // Firstly find the format of the new textbox.
206 SwFrameFormat* pFormat = nullptr;
207 if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
208 pFormat = pTextFrame->GetFrameFormat();
209 if (!pFormat)
210 return;
212 // If there is a format, check if the shape already has a textbox assigned to.
213 if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
215 // If it has a texbox, destroy it.
216 if (pTextBoxNode->GetTextBox(pObj))
217 pTextBoxNode->DelTextBox(pObj, true);
218 // And set the new one.
219 pTextBoxNode->AddTextBox(pObj, pFormat);
220 pFormat->SetOtherTextBoxFormats(pTextBoxNode);
222 else
224 // If the shape do not have a texbox node and textbox,
225 // create that for the shape.
226 auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
227 pTextBox->AddTextBox(pObj, pFormat);
228 pShapeFormat->SetOtherTextBoxFormats(pTextBox);
229 pFormat->SetOtherTextBoxFormats(pTextBox);
231 // Initialize its properties
232 uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
233 uno::Any aEmptyBorder{ table::BorderLine2() };
234 xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
235 xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
236 xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
237 xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
238 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
239 xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
240 xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
241 // Add a new name to it
242 uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
243 assert(!xNamed->getName().isEmpty());
244 (void)xNamed;
245 // And sync. properties.
246 uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
247 syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObj);
248 uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
249 syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
250 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj);
251 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
252 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj);
253 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
254 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
255 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
256 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj);
257 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
258 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
259 syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
260 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
261 syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
262 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
263 syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
264 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
265 drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
266 if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
267 ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
268 != beans::PropertyState::PropertyState_DEFAULT_VALUE)
270 aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
271 .get<drawing::TextVerticalAdjust>();
273 xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::Any(aVertAdj));
274 text::WritingMode eMode;
275 if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
276 syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
278 // Do sync for the new textframe.
279 synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
280 synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
282 updateTextBoxMargin(pObj);
285 void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
287 // If a TextBox was enabled previously
288 auto& pTextBox = pShape->GetOtherTextBoxFormats();
289 if (pTextBox)
291 // Unlink the TextBox's text range from the original shape.
292 // Delete the associated TextFrame.
293 pTextBox->DelTextBox(pObject, true);
297 bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
298 const SdrObject* pObject)
300 DBG_TESTSOLARMUTEX();
301 assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
302 if (!pFormat || pFormat->Which() != nType)
303 return false;
305 auto& pTextBox = pFormat->GetOtherTextBoxFormats();
306 if (!pTextBox)
307 return false;
309 if (nType == RES_DRAWFRMFMT)
311 if (pObject)
312 return pTextBox->GetTextBox(pObject);
313 if (auto pObj = pFormat->FindRealSdrObject())
314 return pTextBox->GetTextBox(pObj);
317 if (nType == RES_FLYFRMFMT)
319 return pTextBox->GetOwnerShape();
322 return false;
325 bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
327 if (!pObj)
328 return false;
330 uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
331 if (!xShape)
332 return false;
333 return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
336 sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
338 sal_Int32 nRet = 0;
339 for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
341 SdrObject* p = pPage->GetObj(i);
342 if (p && p->IsTextBox())
343 continue;
344 ++nRet;
346 return nRet;
349 sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
351 sal_Int32 nRet = 0;
352 for (const sw::SpzFrameFormat* pFormat : *rDoc.GetSpzFrameFormats())
354 if (isTextBox(pFormat, RES_FLYFRMFMT))
355 ++nRet;
357 return nRet;
360 uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
362 if (nIndex < 0)
363 throw lang::IndexOutOfBoundsException();
365 SdrObject* pRet = nullptr;
366 sal_Int32 nCount = 0; // Current logical index.
367 for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
369 SdrObject* p = pPage->GetObj(i);
370 if (p && p->IsTextBox())
371 continue;
372 if (nCount == nIndex)
374 pRet = p;
375 break;
377 ++nCount;
380 if (!pRet)
381 throw lang::IndexOutOfBoundsException();
383 return uno::Any(uno::Reference<drawing::XShape>(pRet->getUnoShape(), uno::UNO_QUERY));
386 sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
388 if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
390 sal_Int32 nOrder = 0; // Current logical order.
391 for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
393 SdrObject* p = pPage->GetObj(i);
394 if (p && p->IsTextBox())
395 continue;
396 if (p == pObject)
397 return nOrder;
398 ++nOrder;
402 SAL_WARN("sw.core", "SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
403 return pObject->GetOrdNum();
406 void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
408 SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
409 if (pShape)
410 rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
413 SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
414 sal_uInt16 nType, const SdrObject* pObject)
416 SolarMutexGuard aGuard;
417 if (!isTextBox(pFormat, nType, pObject))
418 return nullptr;
420 if (nType == RES_DRAWFRMFMT)
422 if (pObject)
423 return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
424 if (pFormat->FindRealSdrObject())
425 return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
426 return nullptr;
428 if (nType == RES_FLYFRMFMT)
430 return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
432 return nullptr;
435 SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
437 auto pShape = dynamic_cast<SwXShape*>(xShape.get());
438 if (!pShape)
439 return nullptr;
441 SwFrameFormat* pFormat = pShape->GetFrameFormat();
442 return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
443 SdrObject::getSdrObjectFromXShape(xShape));
446 uno::Reference<text::XTextFrame>
447 SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
449 if (xShape)
451 auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
452 if (pFrameFormat)
454 auto pSdrObj = pFrameFormat->FindSdrObject();
455 if (pSdrObj)
457 return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
461 return {};
464 template <typename T>
465 static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
467 if (SwFrameFormat* pFormat
468 = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
470 uno::Reference<T> const xInterface(
471 static_cast<cppu::OWeakObject*>(
472 SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat).get()),
473 uno::UNO_QUERY);
474 rAny <<= xInterface;
478 uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType,
479 SdrObject* pObj)
481 uno::Any aRet;
483 if (rType == cppu::UnoType<css::text::XTextAppend>::get())
485 lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
487 else if (rType == cppu::UnoType<css::text::XText>::get())
489 lcl_queryInterface<text::XText>(pShape, aRet, pObj);
491 else if (rType == cppu::UnoType<css::text::XTextRange>::get())
493 lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
496 return aRet;
499 tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
501 tools::Rectangle aRet;
502 aRet.SetEmpty();
504 assert(pShape);
506 auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
507 if (pCustomShape)
509 // Need to temporarily release the lock acquired in
510 // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
511 // see EnhancedCustomShapeEngine::getTextBounds().
512 uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
513 uno::UNO_QUERY);
514 sal_Int16 nLocks = 0;
515 if (xLockable.is())
516 nLocks = xLockable->resetActionLocks();
517 pCustomShape->GetTextBounds(aRet);
518 if (nLocks)
519 xLockable->setActionLocks(nLocks);
521 else if (pShape)
523 // fallback - get *any* bound rect we can possibly get hold of
524 aRet = pShape->GetCurrentBoundRect();
527 if (pShape)
529 // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
530 Point aPoint(pShape->GetSnapRect().Center());
531 Size aSize(pShape->GetLogicRect().GetSize());
532 aPoint.AdjustX(-(aSize.Width() / 2));
533 aPoint.AdjustY(-(aSize.Height() / 2));
534 tools::Rectangle aLogicRect(aPoint, aSize);
535 aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
538 return aRet;
541 void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
542 const css::uno::Any& rValue, SdrObject* pObj)
544 // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
545 if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
547 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
548 if (!pFormat)
549 return;
551 auto xTextFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
552 uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();
554 // Select all paragraphs in the textframe
555 xCursor->gotoStart(false);
556 xCursor->gotoEnd(true);
557 uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);
559 // And simply map the property
560 const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
561 switch (eValue)
563 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
564 xFrameParaProps->setPropertyValue(
565 UNO_NAME_PARA_ADJUST,
566 uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
567 break;
568 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
569 xFrameParaProps->setPropertyValue(
570 UNO_NAME_PARA_ADJUST,
571 uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
572 break;
573 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
574 xFrameParaProps->setPropertyValue(
575 UNO_NAME_PARA_ADJUST,
576 uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
577 break;
578 default:
579 SAL_WARN("sw.core",
580 "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
581 << static_cast<sal_Int32>(eValue));
582 break;
584 return;
587 if (rPropertyName == u"CustomShapeGeometry")
589 // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
590 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
592 SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
593 if (pObject)
595 tools::Rectangle aRectangle(pObject->GetSnapRect());
596 syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
597 uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
598 syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
599 uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
602 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
603 if (!pFormat)
604 return;
606 // Older documents or documents in ODF strict do not have WritingMode, but have used the
607 // TextRotateAngle values -90 and -270 to emulate these text directions of frames.
608 // ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed?
609 comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
610 auto it = aCustomShapeGeometry.find("TextPreRotateAngle");
611 if (it == aCustomShapeGeometry.end())
613 it = aCustomShapeGeometry.find("TextRotateAngle");
616 if (it != aCustomShapeGeometry.end())
618 auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
619 if (nAngle == 0)
621 nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
624 sal_Int16 nDirection = 0;
625 switch (nAngle)
627 case -90:
628 nDirection = text::WritingMode2::TB_RL90;
629 break;
630 case -270:
631 nDirection = text::WritingMode2::BT_LR;
632 break;
633 default:
634 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
635 "CustomShapeGeometry:TextPreRotateAngle: "
636 << nAngle);
637 break;
640 if (nDirection)
642 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
646 else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
647 syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
648 else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
649 syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
650 else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
651 syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
652 else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
653 syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
654 else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
655 syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
656 else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
657 syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
658 else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
660 text::WritingMode eMode;
661 sal_Int16 eMode2;
662 if (rValue >>= eMode)
663 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
664 else if (rValue >>= eMode2)
665 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
667 else if (rPropertyName == u"WritingMode")
669 sal_Int16 eMode2;
670 if (rValue >>= eMode2)
671 syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
673 else
674 SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: "
675 << static_cast<OUString>(rPropertyName));
678 void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
679 css::uno::Any& rValue)
681 if (!pShape)
682 return;
684 nMemberID &= ~CONVERT_TWIPS;
686 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
687 if (!pFormat)
688 return;
690 if (nWID != RES_CHAIN)
691 return;
693 switch (nMemberID)
695 case MID_CHAIN_PREVNAME:
696 case MID_CHAIN_NEXTNAME:
698 const SwFormatChain& rChain = pFormat->GetChain();
699 rChain.QueryValue(rValue, nMemberID);
701 break;
702 case MID_CHAIN_NAME:
703 rValue <<= pFormat->GetName();
704 break;
705 default:
706 SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
707 << o3tl::narrowing<sal_uInt16>(nMemberID));
708 break;
712 css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
714 if (!pShape)
715 return {};
717 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
718 if (!pFormat)
719 return {};
721 rtl::Reference<SwXTextFrame> xPropertySet
722 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
724 return xPropertySet->getPropertyValue(rPropName);
727 void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
728 const css::uno::Any& rValue, SdrObject* pObj)
730 // No shape yet? Then nothing to do, initial properties are set by create().
731 if (!pShape)
732 return;
734 uno::Any aValue(rValue);
735 nMemberID &= ~CONVERT_TWIPS;
737 SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
738 if (!pFormat)
739 return;
741 OUString aPropertyName;
742 bool bAdjustX = false;
743 bool bAdjustY = false;
744 bool bAdjustSize = false;
745 switch (nWID)
747 case RES_HORI_ORIENT:
748 switch (nMemberID)
750 case MID_HORIORIENT_ORIENT:
751 aPropertyName = UNO_NAME_HORI_ORIENT;
752 break;
753 case MID_HORIORIENT_RELATION:
754 if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
755 aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
756 else
757 return;
758 break;
759 case MID_HORIORIENT_POSITION:
760 aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
761 bAdjustX = true;
762 break;
763 default:
764 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
765 << o3tl::narrowing<sal_uInt16>(nMemberID)
766 << " (which-id: " << nWID << ")");
767 break;
769 break;
770 case RES_LR_SPACE:
772 switch (nMemberID)
774 case MID_L_MARGIN:
775 aPropertyName = UNO_NAME_LEFT_MARGIN;
776 break;
777 case MID_R_MARGIN:
778 aPropertyName = UNO_NAME_RIGHT_MARGIN;
779 break;
780 default:
781 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
782 << o3tl::narrowing<sal_uInt16>(nMemberID)
783 << " (which-id: " << nWID << ")");
784 break;
786 break;
788 case RES_VERT_ORIENT:
789 switch (nMemberID)
791 case MID_VERTORIENT_ORIENT:
792 aPropertyName = UNO_NAME_VERT_ORIENT;
793 break;
794 case MID_VERTORIENT_RELATION:
795 if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
796 aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
797 else
798 return;
799 break;
800 case MID_VERTORIENT_POSITION:
801 aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
802 bAdjustY = true;
803 break;
804 default:
805 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
806 << o3tl::narrowing<sal_uInt16>(nMemberID)
807 << " (which-id: " << nWID << ")");
808 break;
810 break;
811 case RES_FRM_SIZE:
812 switch (nMemberID)
814 case MID_FRMSIZE_WIDTH_TYPE:
815 aPropertyName = UNO_NAME_WIDTH_TYPE;
816 break;
817 case MID_FRMSIZE_IS_AUTO_HEIGHT:
818 aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
819 break;
820 case MID_FRMSIZE_REL_HEIGHT_RELATION:
821 aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
822 break;
823 case MID_FRMSIZE_REL_WIDTH_RELATION:
824 aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
825 break;
826 default:
827 aPropertyName = UNO_NAME_SIZE;
828 bAdjustSize = true;
829 break;
831 break;
832 case RES_ANCHOR:
833 switch (nMemberID)
835 case MID_ANCHOR_ANCHORTYPE:
837 changeAnchor(pShape, pObj);
838 return;
840 break;
841 default:
842 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
843 << o3tl::narrowing<sal_uInt16>(nMemberID)
844 << " (which-id: " << nWID << ")");
845 break;
847 break;
848 case FN_TEXT_RANGE:
850 uno::Reference<text::XTextRange> xRange;
851 rValue >>= xRange;
852 SwUnoInternalPaM aInternalPaM(*pFormat->GetDoc());
853 if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
855 SwFormatAnchor aAnchor(pFormat->GetAnchor());
856 aAnchor.SetAnchor(aInternalPaM.Start());
857 pFormat->SetFormatAttr(aAnchor);
860 break;
861 case RES_CHAIN:
862 switch (nMemberID)
864 case MID_CHAIN_PREVNAME:
865 aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
866 break;
867 case MID_CHAIN_NEXTNAME:
868 aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
869 break;
870 default:
871 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
872 << o3tl::narrowing<sal_uInt16>(nMemberID)
873 << " (which-id: " << nWID << ")");
874 break;
876 break;
877 case RES_TEXT_VERT_ADJUST:
878 aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
879 break;
880 case RES_BOX:
881 switch (nMemberID)
883 case LEFT_BORDER_DISTANCE:
884 aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
885 break;
886 case RIGHT_BORDER_DISTANCE:
887 aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
888 break;
889 case TOP_BORDER_DISTANCE:
890 aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
891 break;
892 case BOTTOM_BORDER_DISTANCE:
893 aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
894 break;
895 default:
896 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
897 << o3tl::narrowing<sal_uInt16>(nMemberID)
898 << " (which-id: " << nWID << ")");
899 break;
901 break;
902 case RES_OPAQUE:
903 aPropertyName = UNO_NAME_OPAQUE;
904 break;
905 case RES_FRAMEDIR:
906 aPropertyName = UNO_NAME_WRITING_MODE;
907 break;
908 case RES_WRAP_INFLUENCE_ON_OBJPOS:
909 switch (nMemberID)
911 case MID_ALLOW_OVERLAP:
912 aPropertyName = UNO_NAME_ALLOW_OVERLAP;
913 break;
914 default:
915 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
916 << o3tl::narrowing<sal_uInt16>(nMemberID)
917 << " (which-id: " << nWID << ")");
918 break;
920 break;
921 default:
922 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
923 << nWID << " (member-id: "
924 << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
925 break;
928 if (aPropertyName.isEmpty())
929 return;
931 // Position/size should be the text position/size, not the shape one as-is.
932 if (bAdjustX || bAdjustY || bAdjustSize)
934 changeAnchor(pShape, pObj);
935 tools::Rectangle aRect
936 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
937 if (!aRect.IsEmpty())
939 if (bAdjustX || bAdjustY)
941 sal_Int32 nValue;
942 if (aValue >>= nValue)
944 nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
945 aValue <<= nValue;
948 else if (bAdjustSize)
950 awt::Size aSize(convertTwipToMm100(aRect.getOpenWidth()),
951 convertTwipToMm100(aRect.getOpenHeight()));
952 aValue <<= aSize;
956 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
957 rtl::Reference<SwXTextFrame> const xPropertySet
958 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
959 xPropertySet->setPropertyValue(aPropertyName, aValue);
962 void SwTextBoxHelper::saveLinks(const sw::FrameFormats<sw::SpzFrameFormat*>& rFormats,
963 std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
965 for (const auto pFormat : rFormats)
967 if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
968 rLinks[pFormat] = pTextBox;
972 void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
973 SavedLink& rSavedLinks)
975 std::size_t i = 0;
976 for (const auto& rIt : rOld)
978 auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
979 if (aTextBoxIt != rSavedLinks.end())
981 std::size_t j = 0;
982 for (const auto& rJt : rOld)
984 if (rJt.GetFormat() == aTextBoxIt->second)
985 rNew[i]->SetFormatAttr(rNew[j]->GetContent());
986 ++j;
989 ++i;
993 text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
995 text::TextContentAnchorType aAnchorType;
996 switch (rAnchorID)
998 case RndStdIds::FLY_AS_CHAR:
999 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
1000 break;
1001 case RndStdIds::FLY_AT_CHAR:
1002 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
1003 break;
1004 case RndStdIds::FLY_AT_PARA:
1005 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
1006 break;
1007 case RndStdIds::FLY_AT_PAGE:
1008 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
1009 break;
1010 case RndStdIds::FLY_AT_FLY:
1011 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
1012 break;
1013 default:
1014 aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
1015 SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
1016 break;
1018 return aAnchorType;
1021 void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet,
1022 SdrObject* pObj)
1024 SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
1025 if (!pFormat)
1026 return;
1028 const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
1029 const bool bLayoutInCell = rShape.GetFollowTextFlow().GetValue()
1030 && rShape.GetAnchor().GetAnchorNode()
1031 && rShape.GetAnchor().GetAnchorNode()->FindTableNode();
1032 SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
1034 SfxItemIter aIter(rSet);
1035 const SfxPoolItem* pItem = aIter.GetCurItem();
1039 switch (pItem->Which())
1041 case RES_VERT_ORIENT:
1043 // The new position can be with anchor changing so sync it!
1044 const text::TextContentAnchorType aNewAnchorType
1045 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1046 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
1047 pObj);
1048 if (bInlineAnchored || bLayoutInCell)
1049 return;
1050 SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));
1052 tools::Rectangle aRect
1053 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1054 if (!aRect.IsEmpty())
1055 aOrient.SetPos(aOrient.GetPos() + aRect.Top());
1057 if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
1058 && rShape.GetAnchor().GetPageNum() != 0)
1059 aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
1060 aTextBoxSet.Put(aOrient);
1062 // restore height (shrunk for extending beyond the page bottom - tdf#91260)
1063 SwFormatFrameSize aSize(pFormat->GetFrameSize());
1064 if (!aRect.IsEmpty())
1066 aSize.SetHeight(aRect.getOpenHeight());
1067 aTextBoxSet.Put(aSize);
1070 break;
1071 case RES_HORI_ORIENT:
1073 // The new position can be with anchor changing so sync it!
1074 const text::TextContentAnchorType aNewAnchorType
1075 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1076 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
1077 pObj);
1078 if (bInlineAnchored || bLayoutInCell)
1079 return;
1080 SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));
1082 tools::Rectangle aRect
1083 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1084 if (!aRect.IsEmpty())
1085 aOrient.SetPos(aOrient.GetPos() + aRect.Left());
1087 if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
1088 && rShape.GetAnchor().GetPageNum() != 0)
1089 aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
1090 aTextBoxSet.Put(aOrient);
1092 break;
1093 case RES_FRM_SIZE:
1095 // In case the shape got resized, then we need to adjust both
1096 // the position and the size of the textbox (e.g. larger
1097 // rounded edges of a rectangle -> need to push right/down the
1098 // textbox).
1099 SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
1100 SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
1101 SwFormatFrameSize aSize(pFormat->GetFrameSize());
1103 tools::Rectangle aRect
1104 = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
1105 if (!aRect.IsEmpty())
1107 if (!bInlineAnchored)
1109 aVertOrient.SetPos(
1110 (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
1111 + aRect.Top());
1112 aHoriOrient.SetPos(
1113 (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
1114 + aRect.Left());
1116 aTextBoxSet.Put(aVertOrient);
1117 aTextBoxSet.Put(aHoriOrient);
1120 aSize.SetWidth(aRect.getOpenWidth());
1121 aSize.SetHeight(aRect.getOpenHeight());
1122 aTextBoxSet.Put(aSize);
1125 break;
1126 case RES_ANCHOR:
1128 if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
1129 // the anchor have to be synced
1131 const text::TextContentAnchorType aNewAnchorType
1132 = mapAnchorType(rShape.GetAnchor().GetAnchorId());
1133 syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
1134 uno::Any(aNewAnchorType), pObj);
1136 else
1138 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
1139 "shape different from the textframe!");
1142 break;
1143 default:
1144 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
1145 << pItem->Which());
1146 break;
1149 pItem = aIter.NextItem();
1150 } while (pItem && (0 != pItem->Which()));
1152 if (aTextBoxSet.Count())
1154 auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
1155 pFormat->SetFormatAttr(aTextBoxSet);
1157 DoTextBoxZOrderCorrection(&rShape, pObj);
1160 void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
1162 if (!pObj)
1163 return;
1164 uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
1165 if (!xShape)
1166 return;
1167 uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);
1169 auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
1170 if (!pParentFormat)
1171 return;
1173 // Sync the padding
1174 syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
1175 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
1176 syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
1177 xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
1178 syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
1179 xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
1180 syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
1181 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
1183 // Sync the text aligning
1184 syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
1185 xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
1186 syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
1187 xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
1189 // tdf137803: Sync autogrow:
1190 const bool bIsAutoGrow
1191 = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
1192 const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
1194 syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
1195 pObj);
1197 syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
1198 uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
1200 changeAnchor(pParentFormat, pObj);
1201 DoTextBoxZOrderCorrection(pParentFormat, pObj);
1204 bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
1206 if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1208 if (!isAnchorSyncNeeded(pShape, pFormat))
1210 doTextBoxPositioning(pShape, pObj);
1211 DoTextBoxZOrderCorrection(pShape, pObj);
1212 if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
1213 && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
1214 && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
1216 SwFormatVertOrient aTmp = pFormat->GetVertOrient();
1217 aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
1218 pFormat->SetFormatAttr(aTmp);
1221 return false;
1224 const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
1225 const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
1227 const auto pOldCnt = rOldAnch.GetContentAnchor();
1228 const auto pNewCnt = rNewAnch.GetContentAnchor();
1230 const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());
1234 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1235 ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
1236 rtl::Reference<SwXTextFrame> const xPropertySet
1237 = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
1238 if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1239 && rNewAnch.GetPageNum())
1241 uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
1242 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
1243 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1244 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
1245 uno::Any(rNewAnch.GetPageNum()));
1247 else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
1249 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1251 assert(pNewCnt);
1252 uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
1253 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1254 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1255 uno::Any(text::RelOrientation::CHAR));
1256 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
1257 uno::Any(text::RelOrientation::PRINT_AREA));
1258 SwFormatAnchor aPos(pFormat->GetAnchor());
1259 aPos.SetAnchor(pNewCnt);
1260 pFormat->SetFormatAttr(aPos);
1262 else
1264 uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
1265 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1266 aShapeHorRelOrient);
1267 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1268 pFormat->SetFormatAttr(rNewAnch);
1271 else
1273 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1275 assert(pNewCnt);
1276 uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
1277 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
1278 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1279 uno::Any(text::RelOrientation::CHAR));
1280 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
1281 uno::Any(text::RelOrientation::PRINT_AREA));
1282 SwFormatAnchor aPos(pFormat->GetAnchor());
1283 aPos.SetAnchor(pNewCnt);
1284 pFormat->SetFormatAttr(aPos);
1286 else
1288 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
1289 aShapeHorRelOrient);
1290 if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1291 && rNewAnch.GetPageNum() == 0)
1293 pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
1295 else
1296 pFormat->SetFormatAttr(pShape->GetAnchor());
1300 catch (uno::Exception& e)
1302 SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
1305 doTextBoxPositioning(pShape, pObj);
1306 DoTextBoxZOrderCorrection(pShape, pObj);
1307 return true;
1310 return false;
1313 bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
1315 // Set the position of the textboxes according to the position of its shape-pair
1316 const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
1317 if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1319 // Do not create undo entry for the positioning
1320 ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
1321 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1322 // Special treatment for AS_CHAR textboxes:
1323 if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1325 // Get the text area of the shape
1326 tools::Rectangle aRect
1327 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
1329 // Get the left spacing of the text area of the shape
1330 auto nLeftSpace = pShape->GetLRSpace().GetLeft();
1332 // Set the textbox position at the X-axis:
1333 SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
1334 if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
1335 aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
1336 aNewHOri.SetPos(aRect.Left() + nLeftSpace
1337 + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
1338 SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
1340 // Special handling of group textboxes
1341 if (bIsGroupObj)
1343 // There are the following cases:
1344 // case 1: The textbox should be in that position where the shape is.
1345 // case 2: The shape has negative offset so that have to be subtracted
1346 // case 3: The shape and its parent shape also has negative offset, so subtract
1347 aNewVOri.SetPos(
1348 ((pObj->GetRelativePos().getY()) > 0
1349 ? (pShape->GetVertOrient().GetPos() > 0
1350 ? pObj->GetRelativePos().getY()
1351 : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
1352 : (pShape->GetVertOrient().GetPos() > 0
1353 ? 0 // Is this can be a variation?
1354 : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
1355 + aRect.Top());
1357 else
1359 // Simple textboxes: vertical position equals to the vertical offset of the shape
1360 aNewVOri.SetPos(
1361 ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
1362 + aRect.Top());
1365 // Special cases when the shape is aligned to the line
1366 if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
1368 aNewVOri.SetVertOrient(text::VertOrientation::NONE);
1369 switch (pShape->GetVertOrient().GetVertOrient())
1371 // Top aligned shape
1372 case text::VertOrientation::TOP:
1373 case text::VertOrientation::CHAR_TOP:
1374 case text::VertOrientation::LINE_TOP:
1376 aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
1377 break;
1379 // Bottom aligned shape
1380 case text::VertOrientation::BOTTOM:
1381 case text::VertOrientation::CHAR_BOTTOM:
1382 case text::VertOrientation::LINE_BOTTOM:
1384 aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
1385 break;
1387 // Center aligned shape
1388 case text::VertOrientation::CENTER:
1389 case text::VertOrientation::CHAR_CENTER:
1390 case text::VertOrientation::LINE_CENTER:
1392 aNewVOri.SetPos(aNewVOri.GetPos()
1393 + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
1394 break;
1396 default:
1397 break;
1401 pFormat->SetFormatAttr(aNewHOri);
1402 pFormat->SetFormatAttr(aNewVOri);
1404 // Other cases when the shape has different anchor from AS_CHAR
1405 else
1407 // Text area of the shape
1408 tools::Rectangle aRect
1409 = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
1411 // X Offset of the shape spacing
1412 auto nLeftSpace = pShape->GetLRSpace().GetLeft();
1414 // Set the same position as the (child) shape has
1415 SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
1416 if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
1417 aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
1419 aNewHOri.SetPos(
1420 (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
1421 + aRect.Left());
1422 SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
1423 aNewVOri.SetPos(
1424 (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
1425 + aRect.Top());
1427 // Get the distance of the child shape inside its parent
1428 const auto& nInshapePos
1429 = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
1430 : Point();
1432 // Special case: the shape has relative position from the page
1433 if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1434 && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
1436 aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
1437 aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
1438 + aRect.Left());
1441 if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1442 && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
1444 aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
1445 aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
1446 + aRect.Top());
1449 // Other special case: shape is inside a table or floating table following the text flow
1450 if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetAnchorNode()
1451 && pShape->GetAnchor().GetAnchorNode()->FindTableNode())
1453 // Table position
1454 Point nTableOffset;
1455 // Floating table
1456 if (auto pFly
1457 = pShape->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
1459 if (auto pFlyFormat = pFly->GetFlyFormat())
1461 nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
1462 nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
1465 else
1466 // Normal table
1468 auto pTableNode = pShape->GetAnchor().GetAnchorNode()->FindTableNode();
1469 if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
1471 nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
1472 nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
1476 // Add the table positions to the textbox.
1477 aNewHOri.SetPos(aNewHOri.GetPos() + nTableOffset.getX() + nLeftSpace);
1478 if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
1479 || pShape->GetVertOrient().GetRelationOrient()
1480 == text::RelOrientation::PAGE_PRINT_AREA)
1481 aNewVOri.SetPos(aNewVOri.GetPos() + nTableOffset.getY());
1484 pFormat->SetFormatAttr(aNewHOri);
1485 pFormat->SetFormatAttr(aNewVOri);
1487 return true;
1490 return false;
1493 bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
1495 if (!pShape || !pObj)
1496 return false;
1498 if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
1500 auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
1501 const auto& rSize = getRelativeTextRectangle(pObj).GetSize();
1502 if (!rSize.IsEmpty())
1504 SwFormatFrameSize aSize(pTextBox->GetFrameSize());
1505 aSize.SetSize(rSize);
1506 return pTextBox->SetFormatAttr(aSize);
1510 return false;
1513 bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
1515 // TODO: do this with group shape textboxes.
1516 SdrObject* pShpObj = nullptr;
1518 pShpObj = pShape->FindRealSdrObject();
1520 if (pShpObj)
1522 auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
1523 if (!pTextBox)
1524 return false;
1525 SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
1526 if (!pFrmObj)
1528 // During loading there is no ready SdrObj for z-ordering, so create and cache it here
1529 pFrmObj
1530 = SwXTextFrame::GetOrCreateSdrObject(*dynamic_cast<SwFlyFrameFormat*>(pTextBox));
1532 if (pFrmObj)
1534 // Get the draw model from the doc
1535 SwDrawModel* pDrawModel
1536 = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
1537 if (pDrawModel)
1539 // Not really sure this will work on all pages, but it seems it will.
1540 auto pPage = pDrawModel->GetPage(0);
1541 // Recalc all Z-orders
1542 pPage->RecalcObjOrdNums();
1543 // Here is a counter avoiding running to in infinity:
1544 sal_uInt16 nIterator = 0;
1545 // If the shape is behind the frame, is good, but if there are some objects
1546 // between of them that is wrong so put the frame exactly one level higher
1547 // than the shape.
1548 if (pFrmObj->GetOrdNum() > pShpObj->GetOrdNum())
1549 pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pShpObj->GetOrdNum() + 1);
1550 else
1551 // Else, if the frame is behind the shape, bring to the front of it.
1552 while (pFrmObj->GetOrdNum() <= pShpObj->GetOrdNum())
1554 pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pFrmObj->GetOrdNum() + 1);
1555 // If there is any problem with the indexes, do not run over the infinity
1556 if (pPage->GetObjCount() == pFrmObj->GetOrdNum())
1557 break;
1558 ++nIterator;
1559 if (nIterator > 300)
1560 break; // Do not run to infinity
1562 pPage->RecalcObjOrdNums();
1563 return true; // Success
1565 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1566 "No Valid Draw model for SdrObject for the shape!");
1568 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1569 "No Valid SdrObject for the frame!");
1571 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1572 "No Valid SdrObject for the shape!");
1574 return false;
1577 void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
1578 SwFrameFormat* pFormat, SdrObject* pObj)
1580 if (auto pChildren = pObj->getChildrenOfSdrObject())
1582 for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
1583 synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildren->GetObj(i));
1585 else
1587 (*pFunc)(pFormat, pObj);
1591 std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
1592 SwFrameFormat* pFormat)
1594 std::vector<SwFrameFormat*> vRet;
1595 if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
1597 for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
1599 auto pChildTextBoxes = CollectTextBoxes(pChildren->GetObj(i), pFormat);
1600 for (auto& rChildTextBox : pChildTextBoxes)
1601 vRet.push_back(rChildTextBox);
1604 else
1606 if (isTextBox(pFormat, RES_DRAWFRMFMT, pGroupObject))
1607 vRet.push_back(getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pGroupObject));
1609 return vRet;
1612 bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
1614 if (!pFirst)
1615 return false;
1617 if (!pSecond)
1618 return false;
1620 if (pFirst == pSecond)
1621 return false;
1623 if (!pFirst->GetOtherTextBoxFormats())
1624 return false;
1626 if (!pSecond->GetOtherTextBoxFormats())
1627 return false;
1629 if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
1630 return false;
1632 if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
1633 || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
1635 const auto& rShapeAnchor
1636 = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
1637 const auto& rFrameAnchor
1638 = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
1640 if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
1642 if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
1644 if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
1645 return true;
1647 return false;
1650 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1651 && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
1653 if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
1654 return false;
1655 else
1656 return true;
1659 return true;
1662 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
1663 && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
1665 if (rShapeAnchor.GetAnchorNode() && rFrameAnchor.GetAnchorNode())
1667 if (*rShapeAnchor.GetContentAnchor() != *rFrameAnchor.GetContentAnchor())
1668 return true;
1670 return false;
1673 return true;
1675 return false;
1678 SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
1680 assert(pOwnerShape);
1681 assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
1683 m_bIsCloningInProgress = false;
1684 m_bLock = false;
1686 m_pOwnerShapeFormat = pOwnerShape;
1687 if (!m_pTextBoxes.empty())
1688 m_pTextBoxes.clear();
1691 SwTextBoxNode::~SwTextBoxNode()
1693 if (m_pTextBoxes.size() != 0)
1695 SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
1696 assert(false);
1700 void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
1702 assert(pNewTextBox);
1703 assert(pNewTextBox->Which() == RES_FLYFRMFMT);
1705 assert(pDrawObject);
1707 SwTextBoxElement aElem;
1708 aElem.m_pDrawObject = pDrawObject;
1709 aElem.m_pTextBoxFormat = pNewTextBox;
1711 for (const auto& rE : m_pTextBoxes)
1713 if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
1715 SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
1716 return;
1720 auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
1721 if (pSwFlyDraw)
1723 pSwFlyDraw->SetTextBox(true);
1725 m_pTextBoxes.push_back(aElem);
1728 void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
1730 assert(pDrawObject);
1731 if (m_pTextBoxes.empty())
1732 return;
1734 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
1736 if (it->m_pDrawObject == pDrawObject)
1738 if (bDelFromDoc)
1740 it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1741 it->m_pTextBoxFormat);
1742 // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
1743 // then the ~SwFrameFormat() will call this method again to remove the entry.
1744 return;
1746 else
1748 it = m_pTextBoxes.erase(it);
1749 return;
1752 ++it;
1755 SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
1758 void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
1760 if (m_pTextBoxes.empty())
1761 return;
1763 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
1765 if (it->m_pTextBoxFormat == pTextBox)
1767 if (bDelFromDoc)
1769 it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1770 it->m_pTextBoxFormat);
1771 // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
1772 // then the ~SwFrameFormat() will call this method again to remove the entry.
1773 return;
1775 else
1777 it = m_pTextBoxes.erase(it);
1778 return;
1781 ++it;
1784 SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
1787 SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
1789 assert(pDrawObject);
1790 assert(m_pOwnerShapeFormat);
1792 if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
1794 if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
1796 SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
1800 if (m_bLock)
1801 return nullptr;
1803 if (!m_pTextBoxes.empty())
1805 for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
1807 if (it->m_pDrawObject == pDrawObject)
1809 return it->m_pTextBoxFormat;
1812 SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
1815 return nullptr;
1818 void SwTextBoxNode::ClearAll()
1820 // If this called from ~SwDoc(), then only the address entries
1821 // have to be removed, the format will be deleted by the
1822 // the mpSpzFrameFormatTable->DeleteAndDestroyAll() in ~SwDoc()!
1823 if (m_pOwnerShapeFormat->GetDoc()->IsInDtor())
1825 m_pTextBoxes.clear();
1826 return;
1829 // For loop control
1830 sal_uInt16 nLoopCount = 0;
1832 // Reference not enough, copy needed.
1833 const size_t nTextBoxCount = m_pTextBoxes.size();
1835 // For loop has problems: When one entry deleted, the iterator has
1836 // to be refreshed according to the new situation. So using While() instead.
1837 while (!m_pTextBoxes.empty())
1839 // Delete the last textbox of the vector from the doc
1840 // (what will call deregister in ~SwFrameFormat()
1841 m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
1842 m_pTextBoxes.back().m_pTextBoxFormat);
1844 // Check if we are looping
1845 if (nLoopCount > (nTextBoxCount + 1))
1847 SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
1848 break;
1850 else
1852 nLoopCount++;
1856 // Ensure the vector is empty.
1857 if (!m_pTextBoxes.empty())
1859 SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
1860 assert(false);
1864 bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
1866 std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
1868 std::map<SdrObject*, SwFrameFormat*> aRet;
1869 for (auto& rElem : m_pTextBoxes)
1871 aRet.emplace(rElem.m_pDrawObject, rElem.m_pTextBoxFormat);
1873 return aRet;
1876 void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
1877 bool bSetAttr, bool bMakeFrame) const
1879 if (!o_pTarget || !pDoc)
1880 return;
1882 if (o_pTarget->Which() != RES_DRAWFRMFMT)
1883 return;
1885 if (m_bIsCloningInProgress)
1886 return;
1888 m_bIsCloningInProgress = true;
1890 Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
1891 o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
1893 m_bIsCloningInProgress = false;
1895 for (auto& rElem : m_pTextBoxes)
1897 SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1898 SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1899 SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1900 SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
1904 void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
1905 const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
1906 bool bMakeFrame) const
1908 if (!pSrcObj || !pDestObj)
1909 return;
1911 auto pSrcList = pSrcObj->getChildrenOfSdrObject();
1912 auto pDestList = pDestObj->getChildrenOfSdrObject();
1914 if (pSrcList && pDestList)
1916 if (pSrcList->GetObjCount() != pDestList->GetObjCount())
1918 SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
1919 return;
1922 for (size_t i = 0; i < pSrcList->GetObjCount(); ++i)
1924 Clone_Impl(pDoc, rNewAnc, o_pTarget, pSrcList->GetObj(i), pDestList->GetObj(i),
1925 bSetAttr, bMakeFrame);
1927 return;
1930 if (!pSrcList && !pDestList)
1932 if (auto pSrcFormat = GetTextBox(pSrcObj))
1934 SwFormatAnchor aNewAnchor(rNewAnc);
1935 if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1937 aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
1939 if (!bMakeFrame)
1940 bMakeFrame = true;
1943 if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
1944 *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
1946 if (!o_pTarget->GetOtherTextBoxFormats())
1948 auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
1949 o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
1950 pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
1951 pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
1953 else
1955 o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
1956 pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
1958 o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
1964 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */