1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
18 #include <IDocumentLayoutAccess.hxx>
19 #include <IDocumentSettingAccess.hxx>
20 #include <IDocumentState.hxx>
22 #include <unocoll.hxx>
23 #include <unoframe.hxx>
24 #include <unodraw.hxx>
25 #include <unotextrange.hxx>
28 #include <unoprnms.hxx>
30 #include <fmtsrnd.hxx>
31 #include <fmtfollowtextflow.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>
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
)
70 assert(pShape
== ::FindFrameFormat(pObject
));
72 // If TextBox wasn't enabled previously
73 if (pShape
->GetOtherTextBoxFormats() && pShape
->GetOtherTextBoxFormats()->GetTextBox(pObject
))
76 // Store the current text content of the shape
77 OUString sCopyableText
;
83 uno::Reference
<text::XText
> xSrcCnt(pObject
->getWeakUnoShape().get(), uno::UNO_QUERY
);
84 auto xCur
= xSrcCnt
->createTextCursor();
85 xCur
->gotoStart(false);
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()),
96 uno::Reference
<text::XTextRange
> xAnchor
;
97 uno::Reference
<text::XTextContent
> xAnchorProvider(pObject
->getWeakUnoShape().get(),
99 assert(xAnchorProvider
.is());
100 if (xAnchorProvider
.is())
101 xAnchor
= xAnchorProvider
->getAnchor();
103 uno::Reference
<text::XTextContentAppend
> xTextContentAppend
;
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
);
118 // insertTextContentWithProperties would fail if xAnchor is in a different XText
119 assert(xAnchor
->getText() == xTextContentAppend
);
120 xTextContentAppend
->insertTextContentWithProperties(xTextFrame
, {}, xAnchor
);
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
);
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());
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());
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())
222 auto pSourceText
= DynCastSdrTextObj(pObject
);
223 uno::Reference
<text::XTextRange
> xDestText(xRealTextFrame
, uno::UNO_QUERY
);
225 xDestText
->setString(sCopyableText
);
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();
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
);
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());
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();
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
)
339 auto& pTextBox
= pFormat
->GetOtherTextBoxFormats();
343 if (nType
== RES_DRAWFRMFMT
)
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();
359 bool SwTextBoxHelper::hasTextFrame(const SdrObject
* pObj
)
364 uno::Reference
<drawing::XShape
> xShape(pObj
->getWeakUnoShape().get(), uno::UNO_QUERY
);
367 return SwTextBoxHelper::getOtherTextBoxFormat(xShape
);
370 sal_Int32
SwTextBoxHelper::getCount(SdrPage
const* pPage
)
373 for (const rtl::Reference
<SdrObject
>& p
: *pPage
)
383 sal_Int32
SwTextBoxHelper::getCount(const SwDoc
& rDoc
)
386 for (const sw::SpzFrameFormat
* pFormat
: *rDoc
.GetSpzFrameFormats())
388 if (isTextBox(pFormat
, RES_FLYFRMFMT
))
394 uno::Any
SwTextBoxHelper::getByIndex(SdrPage
const* pPage
, sal_Int32 nIndex
)
397 throw lang::IndexOutOfBoundsException();
399 sal_Int32 nCount
= 0; // Current logical index.
400 for (const rtl::Reference
<SdrObject
>& p
: *pPage
)
405 if (nCount
== nIndex
)
406 return uno::Any(p
->getUnoShape());
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
)
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
);
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
))
447 if (nType
== RES_DRAWFRMFMT
)
450 return pFormat
->GetOtherTextBoxFormats()->GetTextBox(pObject
);
451 if (pFormat
->FindRealSdrObject())
452 return pFormat
->GetOtherTextBoxFormats()->GetTextBox(pFormat
->FindRealSdrObject());
455 if (nType
== RES_FLYFRMFMT
)
457 return pFormat
->GetOtherTextBoxFormats()->GetOwnerShape();
462 SwFrameFormat
* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference
<drawing::XShape
> const& xShape
)
464 auto pShape
= dynamic_cast<SwXShape
*>(xShape
.get());
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
)
478 auto pFrameFormat
= SwTextBoxHelper::getOtherTextBoxFormat(xShape
);
481 auto pSdrObj
= pFrameFormat
->FindSdrObject();
484 return { pSdrObj
->getUnoShape(), uno::UNO_QUERY
};
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()),
504 uno::Any
SwTextBoxHelper::queryInterface(const SwFrameFormat
* pShape
, const uno::Type
& rType
,
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
);
525 tools::Rectangle
SwTextBoxHelper::getRelativeTextRectangle(SdrObject
* pShape
)
527 tools::Rectangle aRet
;
532 auto pCustomShape
= dynamic_cast<SdrObjCustomShape
*>(pShape
);
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(),
540 sal_Int16 nLocks
= 0;
542 nLocks
= xLockable
->resetActionLocks();
543 pCustomShape
->GetTextBounds(aRet
);
545 xLockable
->setActionLocks(nLocks
);
549 // fallback - get *any* bound rect we can possibly get hold of
550 aRet
= pShape
->GetCurrentBoundRect();
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());
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
);
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
>();
589 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER
:
590 xFrameParaProps
->setPropertyValue(
591 UNO_NAME_PARA_ADJUST
,
592 uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER
)); //3
594 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT
:
595 xFrameParaProps
->setPropertyValue(
596 UNO_NAME_PARA_ADJUST
,
597 uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT
)); //0
599 case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT
:
600 xFrameParaProps
->setPropertyValue(
601 UNO_NAME_PARA_ADJUST
,
602 uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT
)); //1
606 "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
607 << static_cast<sal_Int32
>(eValue
));
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();
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
);
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;
647 nAngle
= it
->second
.has
<double>() ? it
->second
.get
<double>() : 0;
650 sal_Int16 nDirection
= 0;
654 nDirection
= text::WritingMode2::TB_RL90
;
657 nDirection
= text::WritingMode2::BT_LR
;
660 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
661 "CustomShapeGeometry:TextPreRotateAngle: "
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
;
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")
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
);
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
)
720 nMemberID
&= ~CONVERT_TWIPS
;
722 SwFrameFormat
* pFormat
= getOtherTextBoxFormat(pShape
, RES_DRAWFRMFMT
);
726 if (nWID
!= RES_CHAIN
)
731 case MID_CHAIN_PREVNAME
:
732 case MID_CHAIN_NEXTNAME
:
734 const SwFormatChain
& rChain
= pFormat
->GetChain();
735 rChain
.QueryValue(rValue
, nMemberID
);
739 rValue
<<= pFormat
->GetName();
742 SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
743 << o3tl::narrowing
<sal_uInt16
>(nMemberID
));
748 css::uno::Any
SwTextBoxHelper::getProperty(SwFrameFormat
const* pShape
, const OUString
& rPropName
)
753 SwFrameFormat
* pFormat
= getOtherTextBoxFormat(pShape
, RES_DRAWFRMFMT
);
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().
770 uno::Any
aValue(rValue
);
771 nMemberID
&= ~CONVERT_TWIPS
;
773 SwFrameFormat
* pFormat
= getOtherTextBoxFormat(pShape
, RES_DRAWFRMFMT
, pObj
);
777 OUString aPropertyName
;
778 bool bAdjustX
= false;
779 bool bAdjustY
= false;
780 bool bAdjustSize
= false;
783 case RES_HORI_ORIENT
:
786 case MID_HORIORIENT_ORIENT
:
787 aPropertyName
= UNO_NAME_HORI_ORIENT
;
789 case MID_HORIORIENT_RELATION
:
790 if (pShape
->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR
)
791 aPropertyName
= UNO_NAME_HORI_ORIENT_RELATION
;
795 case MID_HORIORIENT_POSITION
:
796 aPropertyName
= UNO_NAME_HORI_ORIENT_POSITION
;
800 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
801 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
802 << " (which-id: " << nWID
<< ")");
811 aPropertyName
= UNO_NAME_LEFT_MARGIN
;
814 aPropertyName
= UNO_NAME_RIGHT_MARGIN
;
817 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
818 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
819 << " (which-id: " << nWID
<< ")");
824 case RES_VERT_ORIENT
:
827 case MID_VERTORIENT_ORIENT
:
828 aPropertyName
= UNO_NAME_VERT_ORIENT
;
830 case MID_VERTORIENT_RELATION
:
831 if (pShape
->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR
)
832 aPropertyName
= UNO_NAME_VERT_ORIENT_RELATION
;
836 case MID_VERTORIENT_POSITION
:
837 aPropertyName
= UNO_NAME_VERT_ORIENT_POSITION
;
841 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
842 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
843 << " (which-id: " << nWID
<< ")");
850 case MID_FRMSIZE_WIDTH_TYPE
:
851 aPropertyName
= UNO_NAME_WIDTH_TYPE
;
853 case MID_FRMSIZE_IS_AUTO_HEIGHT
:
854 aPropertyName
= UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT
;
856 case MID_FRMSIZE_REL_HEIGHT_RELATION
:
857 aPropertyName
= UNO_NAME_RELATIVE_HEIGHT_RELATION
;
859 case MID_FRMSIZE_REL_WIDTH_RELATION
:
860 aPropertyName
= UNO_NAME_RELATIVE_WIDTH_RELATION
;
863 aPropertyName
= UNO_NAME_SIZE
;
871 case MID_ANCHOR_ANCHORTYPE
:
873 changeAnchor(pShape
, pObj
);
878 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
879 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
880 << " (which-id: " << nWID
<< ")");
886 uno::Reference
<text::XTextRange
> 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
);
900 case MID_CHAIN_PREVNAME
:
901 aPropertyName
= UNO_NAME_CHAIN_PREV_NAME
;
903 case MID_CHAIN_NEXTNAME
:
904 aPropertyName
= UNO_NAME_CHAIN_NEXT_NAME
;
907 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
908 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
909 << " (which-id: " << nWID
<< ")");
913 case RES_TEXT_VERT_ADJUST
:
914 aPropertyName
= UNO_NAME_TEXT_VERT_ADJUST
;
919 case LEFT_BORDER_DISTANCE
:
920 aPropertyName
= UNO_NAME_LEFT_BORDER_DISTANCE
;
922 case RIGHT_BORDER_DISTANCE
:
923 aPropertyName
= UNO_NAME_RIGHT_BORDER_DISTANCE
;
925 case TOP_BORDER_DISTANCE
:
926 aPropertyName
= UNO_NAME_TOP_BORDER_DISTANCE
;
928 case BOTTOM_BORDER_DISTANCE
:
929 aPropertyName
= UNO_NAME_BOTTOM_BORDER_DISTANCE
;
932 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
933 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
934 << " (which-id: " << nWID
<< ")");
939 aPropertyName
= UNO_NAME_OPAQUE
;
942 aPropertyName
= UNO_NAME_WRITING_MODE
;
944 case RES_WRAP_INFLUENCE_ON_OBJPOS
:
947 case MID_ALLOW_OVERLAP
:
948 aPropertyName
= UNO_NAME_ALLOW_OVERLAP
;
951 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
952 << o3tl::narrowing
<sal_uInt16
>(nMemberID
)
953 << " (which-id: " << nWID
<< ")");
958 SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
959 << nWID
<< " (member-id: "
960 << o3tl::narrowing
<sal_uInt16
>(nMemberID
) << ")");
964 if (aPropertyName
.isEmpty())
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
)
978 if (aValue
>>= nValue
)
980 nValue
+= convertTwipToMm100(bAdjustX
? aRect
.Left() : aRect
.Top());
984 else if (bAdjustSize
)
986 awt::Size
aSize(convertTwipToMm100(aRect
.getOpenWidth()),
987 convertTwipToMm100(aRect
.getOpenHeight()));
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
)
1012 for (const auto& rIt
: rOld
)
1014 auto aTextBoxIt
= rSavedLinks
.find(rIt
.GetFormat());
1015 if (aTextBoxIt
!= rSavedLinks
.end())
1018 for (const auto& rJt
: rOld
)
1020 if (rJt
.GetFormat() == aTextBoxIt
->second
)
1021 rNew
[i
]->SetFormatAttr(rNew
[j
]->GetContent());
1029 text::TextContentAnchorType
SwTextBoxHelper::mapAnchorType(const RndStdIds
& rAnchorID
)
1031 text::TextContentAnchorType aAnchorType
;
1034 case RndStdIds::FLY_AS_CHAR
:
1035 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER
;
1037 case RndStdIds::FLY_AT_CHAR
:
1038 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER
;
1040 case RndStdIds::FLY_AT_PARA
:
1041 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH
;
1043 case RndStdIds::FLY_AT_PAGE
:
1044 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AT_PAGE
;
1046 case RndStdIds::FLY_AT_FLY
:
1047 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AT_FRAME
;
1050 aAnchorType
= text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH
;
1051 SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
1057 void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat
& rShape
, SfxItemSet
const& rSet
,
1060 SwFrameFormat
* pFormat
= getOtherTextBoxFormat(&rShape
, RES_DRAWFRMFMT
, pObj
);
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
),
1084 if (bInlineAnchored
|| bLayoutInCell
)
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
);
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
),
1114 if (bInlineAnchored
|| bLayoutInCell
)
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
);
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
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
)
1146 (pObj
? pObj
->GetRelativePos().getX() : aVertOrient
.GetPos())
1149 (pObj
? pObj
->GetRelativePos().getY() : aHoriOrient
.GetPos())
1152 aTextBoxSet
.Put(aVertOrient
);
1153 aTextBoxSet
.Put(aHoriOrient
);
1156 aSize
.SetWidth(aRect
.getOpenWidth());
1157 aSize
.SetHeight(aRect
.getOpenHeight());
1158 aTextBoxSet
.Put(aSize
);
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
);
1174 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
1175 "shape different from the textframe!");
1180 SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
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
)
1200 uno::Reference
<drawing::XShape
> xShape(pObj
->getUnoShape(), uno::UNO_QUERY
);
1203 uno::Reference
<beans::XPropertySet
> const xPropertySet(xShape
, uno::UNO_QUERY
);
1205 auto pParentFormat
= getOtherTextBoxFormat(getOtherTextBoxFormat(xShape
), RES_FLYFRMFMT
);
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
),
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
);
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
)
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
);
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
);
1309 if (rNewAnch
.GetAnchorId() == RndStdIds::FLY_AS_CHAR
)
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
);
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));
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
);
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
);
1379 aNewHOri
.SetPos(-aRect
.Right() + nRightSpace
1380 + (bIsGroupObj
? pObj
->GetRelativePos().getX() : 0));
1384 aNewHOri
.SetPos(aRect
.Right() + nRightSpace
1385 + (bIsGroupObj
? pObj
->GetRelativePos().getX() : 0));
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
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
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()))
1416 // Simple textboxes: vertical position equals to the vertical offset of the shape
1418 ((pShape
->GetVertOrient().GetPos()) > 0 ? pShape
->GetVertOrient().GetPos() : 0)
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());
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());
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));
1458 pFormat
->SetFormatAttr(aNewHOri
);
1459 pFormat
->SetFormatAttr(aNewVOri
);
1461 // Other cases when the shape has different anchor from AS_CHAR
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
);
1474 (bIsGroupObj
&& pObj
? pObj
->GetRelativePos().getX() : aNewHOri
.GetPos())
1476 SwFormatVertOrient
aNewVOri(pShape
->GetVertOrient());
1478 (bIsGroupObj
&& pObj
? pObj
->GetRelativePos().getY() : aNewVOri
.GetPos())
1481 // Get the distance of the child shape inside its parent
1482 const auto nInshapePos
1483 = pObj
? pObj
->GetRelativePos() - pShape
->FindRealSdrObject()->GetRelativePos()
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()
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()
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
);
1519 = pShape
->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
1521 if (auto pFlyFormat
= pFly
->GetFlyFormat())
1523 nTableOffset
.setX(pFlyFormat
->GetHoriOrient().GetPos());
1524 nTableOffset
.setY(pFlyFormat
->GetVertOrient().GetPos());
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
))
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
))
1555 aNewVOri
.SetPos(nPos
+ nTableOffset
.getY());
1559 pFormat
->SetFormatAttr(aNewHOri
);
1560 pFormat
->SetFormatAttr(aNewVOri
);
1568 bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat
* pShape
, SdrObject
* pObj
)
1570 if (!pShape
|| !pObj
)
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
);
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();
1597 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1598 "No Valid SdrObject for the shape!");
1602 auto pTextBox
= getOtherTextBoxFormat(pShape
, RES_DRAWFRMFMT
, pObj
);
1605 SdrObject
* pFrmObj
= pTextBox
->FindRealSdrObject();
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
);
1616 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1617 "No Valid SdrObject for the frame!");
1620 // Get the draw model from the doc
1621 SwDrawModel
* pDrawModel
= pShape
->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
1624 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1625 "No Valid Draw model for SdrObject for the shape!");
1628 if (!pFrmObj
->getParentSdrObjListFromSdrObject())
1630 SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
1631 "Frame object is not inserted into any parent");
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
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());
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
);
1671 if (isTextBox(pFormat
, RES_DRAWFRMFMT
, pGroupObject
))
1672 vRet
.push_back(getOtherTextBoxFormat(pFormat
, RES_DRAWFRMFMT
, pGroupObject
));
1677 bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat
* pFirst
, const SwFrameFormat
* pSecond
)
1685 if (pFirst
== pSecond
)
1688 if (!pFirst
->GetOtherTextBoxFormats())
1691 if (!pSecond
->GetOtherTextBoxFormats())
1694 if (pFirst
->GetOtherTextBoxFormats() != pSecond
->GetOtherTextBoxFormats())
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())
1715 if (rShapeAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PAGE
1716 && rFrameAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PAGE
)
1718 if (rShapeAnchor
.GetPageNum() == rFrameAnchor
.GetPageNum())
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())
1743 bool SwTextBoxHelper::TextBoxIsFramePr(const SwFrameFormat
& rFrameFormat
)
1745 SdrObject
* pSdrObj
= const_cast<SdrObject
*>(rFrameFormat
.FindRealSdrObject());
1749 uno::Reference
<beans::XPropertySet
> xPropertySet(pSdrObj
->getUnoShape(), uno::UNO_QUERY
);
1750 if (!xPropertySet
.is())
1753 uno::Reference
<beans::XPropertySetInfo
> xPropSetInfo(xPropertySet
->getPropertySetInfo());
1754 if (!xPropSetInfo
.is() || !xPropSetInfo
->hasPropertyByName(u
"FrameInteropGrabBag"_ustr
))
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
;
1769 SwTextBoxNode::SwTextBoxNode(SwFrameFormat
* pOwnerShape
)
1771 assert(pOwnerShape
);
1772 assert(pOwnerShape
->Which() == RES_DRAWFRMFMT
);
1774 m_bIsCloningInProgress
= 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!");
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!");
1811 auto pSwFlyDraw
= dynamic_cast<SwFlyDrawObj
*>(pDrawObject
);
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())
1825 for (auto it
= m_pTextBoxes
.begin(); it
!= m_pTextBoxes
.end();)
1827 if (it
->m_pDrawObject
== pDrawObject
)
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.
1839 it
= m_pTextBoxes
.erase(it
);
1846 SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
1849 void SwTextBoxNode::DelTextBox(const SwFrameFormat
* pTextBox
, bool bDelFromDoc
)
1851 if (m_pTextBoxes
.empty())
1854 for (auto it
= m_pTextBoxes
.begin(); it
!= m_pTextBoxes
.end();)
1856 if (it
->m_pTextBoxFormat
== pTextBox
)
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.
1868 it
= m_pTextBoxes
.erase(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!");
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!");
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();
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!");
1947 // Ensure the vector is empty.
1948 if (!m_pTextBoxes
.empty())
1950 SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
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
);
1967 void SwTextBoxNode::Clone(SwDoc
* pDoc
, const SwFormatAnchor
& rNewAnc
, SwFrameFormat
* o_pTarget
,
1968 bool bSetAttr
, bool bMakeFrame
) const
1970 if (!o_pTarget
|| !pDoc
)
1973 if (o_pTarget
->Which() != RES_DRAWFRMFMT
)
1976 if (m_bIsCloningInProgress
)
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
)
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!");
2013 for (auto itSrc
= pSrcList
->begin(), itDest
= pDestList
->begin(); itSrc
!= pSrcList
->end();
2016 Clone_Impl(pDoc
, rNewAnc
, o_pTarget
, itSrc
->get(), itDest
->get(), bSetAttr
, bMakeFrame
);
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
);
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
);
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: */