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 <swmodeltestbase.hxx>
12 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
13 #include <comphelper/lok.hxx>
14 #include <comphelper/propertyvalue.hxx>
15 #include <com/sun/star/text/XTextTable.hpp>
16 #include <sfx2/viewsh.hxx>
17 #include <vcl/gdimtf.hxx>
18 #include <vcl/scheduler.hxx>
19 #include <sfx2/lokhelper.hxx>
20 #include <test/lokcallback.hxx>
21 #include <editeng/escapementitem.hxx>
23 #include <IDocumentStatistics.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <fmtanchr.hxx>
26 #include <frameformats.hxx>
28 #include <unotxdoc.hxx>
30 #include <formatcontentcontrol.hxx>
33 #include <txatbase.hxx>
35 #include <textcontentcontrol.hxx>
36 #include <swdtflvr.hxx>
37 #include <txtrfmrk.hxx>
39 #include <formatflysplit.hxx>
41 #include <rootfrm.hxx>
42 #include <pagefrm.hxx>
45 /// Covers sw/source/core/txtnode/ fixes.
46 class SwCoreTxtnodeTest
: public SwModelTestBase
50 : SwModelTestBase(u
"/sw/qa/core/txtnode/data/"_ustr
)
55 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testBtlrCellChinese
)
57 // Load a document with a table cell, with btlr cell direction. The cell has text which is
58 // classified as vertical, i.e. the glyph has the same direction in both the lrtb ("Latin") and
59 // tbrl ("Chinese") directions. Make sure that Chinese text is handled the same way in the btlr
60 // case as it's handled in the Latin case.
61 createSwDoc("btlr-cell-chinese.doc");
62 SwDocShell
* pShell
= getSwDocShell();
63 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
64 MetafileXmlDump dumper
;
65 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
66 assertXPath(pXmlDoc
, "//font[1]", "orientation", u
"900");
67 // Without the accompanying fix in place, this test would have failed with:
70 // i.e. the glyph was rotated further, so it was upside down.
71 assertXPath(pXmlDoc
, "//font[1]", "vertical", u
"false");
74 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testSpecialInsertAfterMergedCells
)
76 // Load a document with a table with bottom right cells merged vertically.
77 // SpecialInsert with alt-Enter must work here, too.
78 createSwDoc("special-insert-after-merged-cells.fodt");
79 SwDoc
* pDoc
= getSwDoc();
80 SwNodeOffset
const nNodes(pDoc
->GetNodes().Count());
81 SwDocShell
* pShell
= getSwDocShell();
82 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
83 // go to the merged cell
84 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
86 // When pressing alt-Enter on the keyboard:
87 SwEditWin
& rEditWin
= pWrtShell
->GetView().GetEditWin();
88 vcl::KeyCode
aKeyCode(KEY_RETURN
, KEY_MOD2
);
89 KeyEvent
aKeyEvent(' ', aKeyCode
);
90 rEditWin
.KeyInput(aKeyEvent
);
92 // Without the accompanying fix in place, this test would have failed with:
93 // - Expected: nNodes + 1
95 // i.e. new empty paragraph wasn't inserted under the table
96 CPPUNIT_ASSERT_EQUAL(nNodes
+ 1, pDoc
->GetNodes().Count());
99 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testTextBoxCopyAnchor
)
101 createSwDoc("textbox-copy-anchor.docx");
102 SwDocShell
* pShell
= getSwDocShell();
103 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
106 pWrtShell
->Copy(aClipboard
);
107 pWrtShell
->SttEndDoc(/*bStart=*/false);
108 pWrtShell
->Paste(aClipboard
);
110 const auto& rFormats
= *pShell
->GetDoc()->GetSpzFrameFormats();
111 // Without the accompanying fix in place, this test would have failed with:
114 // i.e. 2 fly frames were copied twice.
115 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rFormats
.size());
117 SwPosition aDrawAnchor1
= *rFormats
[0]->GetAnchor().GetContentAnchor();
118 SwPosition aFlyAnchor1
= *rFormats
[1]->GetAnchor().GetContentAnchor();
119 CPPUNIT_ASSERT_EQUAL(aFlyAnchor1
.GetNodeIndex(), aDrawAnchor1
.GetNodeIndex());
120 SwPosition aDrawAnchor2
= *rFormats
[2]->GetAnchor().GetContentAnchor();
121 SwPosition aFlyAnchor2
= *rFormats
[3]->GetAnchor().GetContentAnchor();
122 // This also failed, aFlyAnchor2 was wrong, as it got out of sync with aDrawAnchor2.
123 CPPUNIT_ASSERT_EQUAL(aFlyAnchor2
.GetNodeIndex(), aDrawAnchor2
.GetNodeIndex());
126 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testTextBoxNodeSplit
)
128 createSwDoc("textbox-node-split.docx");
129 SwDocShell
* pShell
= getSwDocShell();
130 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
131 pWrtShell
->SttEndDoc(/*bStart=*/false);
132 // Without the accompanying fix in place, this would have crashed in
133 // SwFlyAtContentFrame::SwClientNotify().
134 pWrtShell
->SplitNode();
141 int m_nInvalidations
= 0;
143 static void callback(int nType
, const char* pPayload
, void* pData
);
144 void callbackImpl(int nType
, const char* pPayload
);
147 void ViewCallback::callback(int nType
, const char* pPayload
, void* pData
)
149 static_cast<ViewCallback
*>(pData
)->callbackImpl(nType
, pPayload
);
152 void ViewCallback::callbackImpl(int nType
, const char* /*pPayload*/)
156 case LOK_CALLBACK_INVALIDATE_TILES
:
165 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testTitleFieldInvalidate
)
167 // Set up LOK to track invalidations.
168 comphelper::LibreOfficeKit::setActive(true);
170 // Given a document with a title field:
171 createSwDoc("title-field-invalidate.fodt");
172 getSwTextDoc()->initializeForTiledRendering({});
173 SwDocShell
* pShell
= getSwDocShell();
174 SwDoc
* pDoc
= pShell
->GetDoc();
175 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
176 pWrtShell
->SttEndDoc(/*bStt=*/false);
177 ViewCallback aCallback
;
178 TestLokCallbackWrapper
aCallbackWrapper(&ViewCallback::callback
, &aCallback
);
179 pWrtShell
->GetSfxViewShell()->setLibreOfficeKitViewCallback(&aCallbackWrapper
);
180 aCallbackWrapper
.setLOKViewId(SfxLokHelper::getView(pWrtShell
->GetSfxViewShell()));
181 Scheduler::ProcessEventsToIdle();
182 aCallback
.m_nInvalidations
= 0;
184 // When typing to the document:
185 pWrtShell
->Insert(u
"x"_ustr
);
186 pWrtShell
->GetSfxViewShell()->flushPendingLOKInvalidateTiles();
188 // Then make sure that only the text frame at the cursor is invalidated:
189 pDoc
->getIDocumentStatistics().GetUpdatedDocStat(/*bCompleteAsync=*/true, /*bFields=*/false);
190 // Without the accompanying fix in place, this test would have failed with:
193 // i.e. the footer was also invalidated on each keypress.
194 CPPUNIT_ASSERT_EQUAL(1, aCallback
.m_nInvalidations
);
197 pWrtShell
->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr);
198 mxComponent
->dispose();
200 comphelper::LibreOfficeKit::setActive(false);
203 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testFlyAnchorUndo
)
205 // Given a document with a fly frame, anchored after the last char of the document:
206 createSwDoc("fly-anchor-undo.odt");
207 SwDocShell
* pShell
= getSwDocShell();
208 SwDoc
* pDoc
= pShell
->GetDoc();
209 const auto& rSpz
= *pDoc
->GetSpzFrameFormats();
210 sal_Int32 nExpected
= rSpz
[0]->GetAnchor().GetAnchorContentOffset();
212 // When deleting that last character and undoing it:
213 SwWrtShell
* pWrtShell
= pShell
->GetWrtShell();
214 pWrtShell
->SttEndDoc(/*bStt=*/false);
215 pWrtShell
->DelLeft();
218 // Then make sure the anchor position after the undo is the same as the original:
219 sal_Int32 nActual
= rSpz
[0]->GetAnchor().GetAnchorContentOffset();
220 // Without the accompanying fix in place, this test would have failed with:
223 // i.e. the anchor position was left unchanged by the undo.
224 CPPUNIT_ASSERT_EQUAL(nExpected
, nActual
);
227 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testSplitNodeSuperscriptCopy
)
229 // Given a document with superscript text at the end of a paragraph:
231 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
232 pWrtShell
->Insert(u
"1st"_ustr
);
233 pWrtShell
->Left(SwCursorSkipMode::Chars
, /*bSelect=*/true, 2, /*bBasicCall=*/false);
234 SfxItemSetFixed
<RES_CHRATR_ESCAPEMENT
, RES_CHRATR_ESCAPEMENT
> aSet(pWrtShell
->GetAttrPool());
235 SvxEscapementItem
aItem(SvxEscapement::Superscript
, RES_CHRATR_ESCAPEMENT
);
237 pWrtShell
->SetAttrSet(aSet
);
239 // When hitting enter at the end of the paragraph:
240 pWrtShell
->SttEndDoc(/*bStt=*/false);
241 pWrtShell
->SplitNode(/*bAutoFormat=*/true);
243 // Then make sure that the superscript formatting doesn't appear on the next paragraph:
244 aSet
.ClearItem(RES_CHRATR_ESCAPEMENT
);
245 pWrtShell
->GetCurAttr(aSet
);
246 // Without the accompanying fix in place, this test would have failed, the unexpected
247 // superscript appeared in the next paragraph.
248 CPPUNIT_ASSERT(!aSet
.HasItem(RES_CHRATR_ESCAPEMENT
));
251 /* FIXME: behavior change reverted due to regression;
252 * see sw/source/core/txtnode/atrref.cxx
253 *CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testDontExpandRefmark)
255 * // Given a document with a refmark:
258 * uno::Sequence<css::beans::PropertyValue> aArgs = {
259 * comphelper::makePropertyValue("TypeName", uno::Any(OUString("SetRef"))),
260 * comphelper::makePropertyValue(
261 * "Name", uno::Any(OUString("ZOTERO_ITEM CSL_CITATION {} RNDpyJknp173F"))),
262 * comphelper::makePropertyValue("Content", uno::Any(OUString("foo"))),
264 * dispatchCommand(mxComponent, ".uno:InsertField", aArgs);
266 * SwDoc* pDoc = getSwDoc();
267 * SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
268 * SwPosition& rCursor = *pWrtShell->GetCursor()->GetPoint();
269 * SwTextNode* pTextNode = rCursor.GetNode().GetTextNode();
270 * std::vector<SwTextAttr*> aAttrs
271 * = pTextNode->GetTextAttrsAt(rCursor.GetContentIndex(), RES_TXTATR_REFMARK);
273 * auto& rRefmark = const_cast<SwFormatRefMark&>(aAttrs[0]->GetRefMark());
274 * auto pTextRefMark = const_cast<SwTextRefMark*>(rRefmark.GetTextRefMark());
276 * // When typing after the refmark...
277 * pWrtShell->SttEndDoc(true);
278 * pWrtShell->Right(SwCursorSkipMode::Chars, false, 3, false);
279 * pWrtShell->Insert(" bar");
281 * // and skipping back to insert a comma after the refmark
282 * pWrtShell->Left(SwCursorSkipMode::Chars, false, 4, false);
283 * pWrtShell->Insert(",");
285 * // Without the accompanying fix in place, this test would have failed with:
288 * // i.e. the reference mark expanded
289 * CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(*pTextRefMark->End()));
293 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testInsertDropDownContentControlTwice
)
295 // Given an already selected dropdown content control:
297 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
298 pWrtShell
->InsertContentControl(SwContentControlType::DROP_DOWN_LIST
);
300 // When trying to insert an inner one, make sure that we don't crash:
301 pWrtShell
->InsertContentControl(SwContentControlType::DROP_DOWN_LIST
);
304 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testCheckboxContentControlKeyboard
)
306 // Given an already selected checkbox content control:
308 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
309 pWrtShell
->InsertContentControl(SwContentControlType::CHECKBOX
);
310 SwEditWin
& rEditWin
= pWrtShell
->GetView().GetEditWin();
312 // When pressing space on the keyboard:
313 KeyEvent
aKeyEvent(' ', KEY_SPACE
);
314 rEditWin
.KeyInput(aKeyEvent
);
316 // Then make sure the state is toggled:
317 SwTextNode
* pTextNode
= pWrtShell
->GetCursor()->GetPointNode().GetTextNode();
318 SwTextAttr
* pAttr
= pTextNode
->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL
);
319 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
320 auto& rFormatContentControl
321 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
322 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
323 // Without the accompanying fix in place, this test would have failed, because the state
324 // remained unchanged.
325 CPPUNIT_ASSERT(pContentControl
->GetChecked());
328 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testDropdownContentControlKeyboard
)
330 // Given an already selected dropdown content control:
332 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
333 pWrtShell
->InsertContentControl(SwContentControlType::DROP_DOWN_LIST
);
335 // When checking if alt-down should open a popup:
336 SwTextContentControl
* pTextContentControl
= pWrtShell
->CursorInsideContentControl();
337 auto& rFormatContentControl
338 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
339 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
340 vcl::KeyCode
aKeyCode(KEY_DOWN
, KEY_MOD2
);
341 bool bShouldOpen
= pContentControl
->ShouldOpenPopup(aKeyCode
);
343 // Then make sure that the answer is yes for dropdowns:
344 // Without the accompanying fix in place, this test would have failed, the dropdown popup was
346 CPPUNIT_ASSERT(bShouldOpen
);
349 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testPictureContentControlKeyboard
)
351 // Given an already selected picture content control:
353 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
354 pWrtShell
->InsertContentControl(SwContentControlType::PICTURE
);
355 pWrtShell
->GotoObj(/*bNext=*/true, GotoObjFlags::Any
);
357 // When checking if enter should trigger the file picker:
358 const SwFrameFormat
* pFlyFormat
= pWrtShell
->GetFlyFrameFormat();
359 const SwFormatAnchor
& rFormatAnchor
= pFlyFormat
->GetAnchor();
360 SwNode
* pAnchorNode
= rFormatAnchor
.GetAnchorNode();
361 SwTextNode
* pTextNode
= pAnchorNode
->GetTextNode();
363 = pTextNode
->GetTextAttrAt(rFormatAnchor
.GetAnchorContentOffset(),
364 RES_TXTATR_CONTENTCONTROL
, ::sw::GetTextAttrMode::Parent
);
365 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
366 auto& rFormatContentControl
367 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
368 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
369 bool bIsInteracting
= pContentControl
->IsInteractingCharacter('\r');
371 // Then make sure that the answer is yes for pictures:
372 // Without the accompanying fix in place, this test would have failed, the picture replacement
373 // file-picker was mouse-only.
374 CPPUNIT_ASSERT(bIsInteracting
);
377 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testDateContentControlKeyboard
)
379 // Given an already selected date content control:
381 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
382 pWrtShell
->InsertContentControl(SwContentControlType::DATE
);
384 // When checking if alt-down should open a popup:
385 SwTextContentControl
* pTextContentControl
= pWrtShell
->CursorInsideContentControl();
386 auto& rFormatContentControl
387 = static_cast<SwFormatContentControl
&>(pTextContentControl
->GetAttr());
388 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
389 vcl::KeyCode
aKeyCode(KEY_DOWN
, KEY_MOD2
);
390 bool bShouldOpen
= pContentControl
->ShouldOpenPopup(aKeyCode
);
392 // Then make sure that the answer is yes for date:
393 // Without the accompanying fix in place, this test would have failed, the date popup was
395 CPPUNIT_ASSERT(bShouldOpen
);
398 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testContentControlCopy
)
400 // Given a document with a content control:
402 SwDoc
* pDoc
= getSwDoc();
403 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
404 pWrtShell
->InsertContentControl(SwContentControlType::CHECKBOX
);
406 // When copying that content control:
408 rtl::Reference
<SwTransferable
> xTransfer
= new SwTransferable(*pWrtShell
);
410 // Kill the selection, go to the end of the document:
411 pWrtShell
->EndOfSection();
412 TransferableDataHelper
aHelper(xTransfer
);
413 SwTransferable::Paste(*pWrtShell
, aHelper
);
415 // Then make sure that the copy is also a checkbox:
416 SwContentControlManager
& rManager
= pDoc
->GetContentControlManager();
417 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rManager
.GetCount());
418 const SwFormatContentControl
& rFormat1
= rManager
.Get(0)->GetContentControl();
419 CPPUNIT_ASSERT_EQUAL(SwContentControlType::CHECKBOX
, rFormat1
.GetContentControl()->GetType());
420 const SwFormatContentControl
& rFormat2
= rManager
.Get(1)->GetContentControl();
421 // Without the accompanying fix in place, this test would have failed with:
422 // - Expected: 1 (CHECKBOX)
423 // - Actual : 0 (RICH_TEXT)
424 // i.e. properties were not copied from the source to the destination content control.
425 CPPUNIT_ASSERT_EQUAL(SwContentControlType::CHECKBOX
, rFormat2
.GetContentControl()->GetType());
428 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testTdf157287
)
430 createSwDoc("tdf157287.odt");
431 uno::Reference
<text::XTextFieldsSupplier
> xTextFieldsSupplier(mxComponent
, uno::UNO_QUERY
);
432 auto xFieldsAccess(xTextFieldsSupplier
->getTextFields());
433 uno::Reference
<container::XEnumeration
> xFields(xFieldsAccess
->createEnumeration());
434 uno::Reference
<text::XTextField
> xField(xFields
->nextElement(), uno::UNO_QUERY
);
436 CPPUNIT_ASSERT_EQUAL(u
"30"_ustr
, xField
->getPresentation(false));
438 uno::Reference
<text::XTextTablesSupplier
> xTextTablesSupplier(mxComponent
, uno::UNO_QUERY
);
439 uno::Reference
<container::XIndexAccess
> xIndexAccess(xTextTablesSupplier
->getTextTables(),
441 uno::Reference
<text::XTextTable
> xTextTable(xIndexAccess
->getByIndex(0), uno::UNO_QUERY
);
443 uno::Reference
<text::XTextRange
> xCellA1(xTextTable
->getCellByName(u
"B1"_ustr
), uno::UNO_QUERY
);
444 xCellA1
->setString(u
"100"_ustr
);
446 dispatchCommand(mxComponent
, u
".uno:UpdateFields"_ustr
, {});
448 // Without the fix in place, this test would have failed with
451 CPPUNIT_ASSERT_EQUAL(u
"120"_ustr
, xField
->getPresentation(false));
454 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testFlySplitFootnote
)
456 // Given a document with a split fly (to host a table):
458 SwDoc
* pDoc
= getSwDoc();
459 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
460 SwFlyFrameAttrMgr
aMgr(true, pWrtShell
, Frmmgr_Type::TEXT
, nullptr);
461 RndStdIds eAnchor
= RndStdIds::FLY_AT_PARA
;
462 pWrtShell
->StartAllAction();
463 aMgr
.InsertFlyFrame(eAnchor
, aMgr
.GetPos(), aMgr
.GetSize());
464 pWrtShell
->EndAllAction();
465 pWrtShell
->StartAllAction();
466 sw::FrameFormats
<sw::SpzFrameFormat
*>& rFlys
= *pDoc
->GetSpzFrameFormats();
467 sw::SpzFrameFormat
* pFly
= rFlys
[0];
468 SwAttrSet
aSet(pFly
->GetAttrSet());
469 aSet
.Put(SwFormatFlySplit(true));
470 pDoc
->SetAttr(aSet
, *pFly
);
471 pWrtShell
->EndAllAction();
472 pWrtShell
->UnSelectFrame();
473 pWrtShell
->LeaveSelFrameMode();
474 pWrtShell
->GetView().AttrChangedNotify(nullptr);
475 pWrtShell
->MoveSection(GoCurrSection
, fnSectionEnd
);
477 // When inserting a footnote:
478 pWrtShell
->InsertFootnote(OUString());
480 // Then make sure the footnote gets inserted to the doc model.
481 // Without the accompanying fix in place, this test would have failed, insert code refused to
482 // have footnotes in all fly frames.
483 CPPUNIT_ASSERT(!pDoc
->GetFootnoteIdxs().empty());
486 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testSplitFlyAnchorSplit
)
488 // Given a document with a 2 pages long floating table:
489 createSwDoc("floattable-anchor-split.docx");
491 // When splitting the "AB" anchor text into "A" (remains as anchor text) and "B" (new text node
493 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
494 pWrtShell
->SttEndDoc(/*bStt=*/false);
495 pWrtShell
->Left(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
496 // Without the accompanying fix in place, this test would have failed with a layout loop.
497 pWrtShell
->SplitNode();
499 // Then make sure the resulting layout is what we want:
500 SwDoc
* pDoc
= getSwDoc();
501 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
502 auto pPage1
= pLayout
->Lower()->DynCastPageFrame();
503 CPPUNIT_ASSERT(pPage1
);
504 // Page 1 has the master fly:
505 CPPUNIT_ASSERT(pPage1
->GetSortedObjs());
506 auto pPage2
= pPage1
->GetNext()->DynCastPageFrame();
507 CPPUNIT_ASSERT(pPage2
);
508 // Page 2 has the follow fly:
509 CPPUNIT_ASSERT(pPage2
->GetSortedObjs());
510 // Anchor text is now just "A":
511 auto pText1
= pPage2
->FindFirstBodyContent()->DynCastTextFrame();
512 CPPUNIT_ASSERT_EQUAL(u
"A"_ustr
, pText1
->GetText());
513 // New text frame is just "B":
514 auto pText2
= pText1
->GetNext()->DynCastTextFrame();
515 CPPUNIT_ASSERT_EQUAL(u
"B"_ustr
, pText2
->GetText());
517 // Also test that the new follow anchor text frame still has a fly portion, otherwise the anchor
518 // text and the floating table would overlap:
519 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
520 OUString aPortionType
= getXPath(
521 pXmlDoc
, "//page[2]/body/txt[1]/SwParaPortion/SwLineLayout[1]/child::*[1]", "type");
522 // Without the accompanying fix in place, this test would have failed with:
523 // - Expected: PortionType::Fly
524 // - Actual : PortionType::Para
525 // i.e. the fly portion was missing, text overlapped.
526 CPPUNIT_ASSERT_EQUAL(u
"PortionType::Fly"_ustr
, aPortionType
);
529 CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest
, testPlainContentControlCopy
)
531 // Given a document with a plain text content control, all text selected and copied to the
533 createSwDoc("plain-content-control-copy.docx");
534 SwDocShell
* pDocShell
= getSwDocShell();
535 SwWrtShell
* pWrtShell
= pDocShell
->GetWrtShell();
538 rtl::Reference
<SwTransferable
> xTransfer
= new SwTransferable(*pWrtShell
);
542 // When closing that document, then make sure we don't crash on shutdown:
543 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
544 uno::Reference
<util::XCloseable
> xFrame(xModel
->getCurrentController()->getFrame(),
546 // Without the accompanying fix in place, this resulted in an assertion failure, a char style
547 // still had clients by the time it was deleted.
548 xFrame
->close(false);
552 CPPUNIT_PLUGIN_IMPLEMENT();
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */