/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace
{ /// Covers sw/source/uibase/wrtsh/ fixes. class Test : public SwModelTestBase
{ public:
Test()
: SwModelTestBase(u"/sw/qa/uibase/wrtsh/data/"_ustr)
{
}
};
CPPUNIT_TEST_FIXTURE(Test, testInsertLineBreak)
{ // Given an empty document:
createSwDoc();
// When inserting a clearing break:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
std::optional<SwLineBreakClear> oClear = SwLineBreakClear::ALL;
pWrtShell->InsertLineBreak(oClear);
// Then make sure it's not just a plain linebreak:
uno::Reference<css::text::XTextRange> xTextPortion = getRun(getParagraph(1), 1); auto aPortionType = getProperty<OUString>(xTextPortion, u"TextPortionType"_ustr); // Without the accompanying fix in place, this test would have failed with: // - Expected: LineBreak // - Actual : Text // i.e. the line break lost its "clear" property.
CPPUNIT_ASSERT_EQUAL(u"LineBreak"_ustr, aPortionType); auto xLineBreak
= getProperty<uno::Reference<text::XTextContent>>(xTextPortion, u"LineBreak"_ustr); auto eClear = getProperty<sal_Int16>(xLineBreak, u"Clear"_ustr);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(SwLineBreakClear::ALL), eClear);
}
// When going to that content control in placeholder mode:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwNodeOffset nIndex = pWrtShell->GetCursor()->GetPointNode().GetIndex();
SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
pWrtShell->GotoContentControl(rFormatContentControl);
// Then make sure that the content control is selected (without the dummy character): // Without the accompanying fix in place, this test would have failed, the user had to manually // select the placeholder text.
sal_Int32 nStart = pWrtShell->GetCursor()->Start()->GetContentIndex();
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nStart);
sal_Int32 nEnd = pWrtShell->GetCursor()->End()->GetContentIndex();
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5), nEnd);
}
CPPUNIT_TEST_FIXTURE(Test, testTickCheckboxContentControl)
{ // Given a document with a checkbox (checked) content control:
createSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
// The default Liberation Serif doesn't have a checkmark glyph, avoid font fallback.
SwView& rView = pWrtShell->GetView();
SfxItemSet aSet(
SfxItemSet::makeFixedSfxItemSet<RES_CHRATR_BEGIN, RES_CHRATR_END>(rView.GetPool()));
SvxFontItem aFont(FAMILY_DONTKNOW, u"DejaVu Sans"_ustr, OUString(), PITCH_DONTKNOW,
RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
aSet.Put(aFont);
pWrtShell->SetAttrSet(aSet);
// When clicking on that content control:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
pWrtShell->GotoContentControl(rFormatContentControl);
// Then make sure that the checkbox is no longer checked: // Without the accompanying fix in place, this test would have failed: // - Expected: ☐ // - Actual : ☒ // i.e. the text node's text was "Ballot Box with X", not just "Ballot Box".
CPPUNIT_ASSERT_EQUAL(u"☐"_ustr, pTextNode->GetExpandText(pWrtShell->GetLayout()));
}
CPPUNIT_TEST_FIXTURE(Test, testInsertContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); // Without the accompanying fix in place, this test would have failed, nothing happened on // InsertContentControl().
CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL));
}
CPPUNIT_TEST_FIXTURE(Test, testInsertCheckboxContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
// The default Liberation Serif doesn't have a checkmark glyph, avoid font fallback.
SwView& rView = pWrtShell->GetView();
SfxItemSet aSet(
SfxItemSet::makeFixedSfxItemSet<RES_CHRATR_BEGIN, RES_CHRATR_END>(rView.GetPool()));
SvxFontItem aFont(FAMILY_DONTKNOW, u"DejaVu Sans"_ustr, OUString(), PITCH_DONTKNOW,
RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
aSet.Put(aFont);
pWrtShell->SetAttrSet(aSet);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); // Without the accompanying fix in place, this test would have failed, the inserted content // control wasn't a checkbox one.
CPPUNIT_ASSERT(pContentControl->GetCheckbox());
}
// When clicking on that content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
rFormatContentControl.GetContentControl()->SetSelectedListItem(0);
pWrtShell->GotoContentControl(rFormatContentControl);
// Then make sure that the document text is updated: // Without the accompanying fix in place, this test would have failed: // - Expected: red // - Actual : choose an item // i.e. the document text was unchanged instead of display text of the first list item.
CPPUNIT_ASSERT_EQUAL(u"red"_ustr, pTextNode->GetExpandText(pWrtShell->GetLayout()));
}
CPPUNIT_TEST_FIXTURE(Test, testInsertDropdownContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); // Without the accompanying fix in place, this test would have failed: // - Expected: 1 // - Actual : 0 // i.e. the inserted content control was a default (rich text) one, not a dropdown.
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pContentControl->GetListItems().size());
}
// Then make sure that the picture is replaced:
CPPUNIT_ASSERT(!rFormatContentControl.GetContentControl()->GetShowingPlaceHolder()); // Without the accompanying fix in place, this test would have failed, there was no special // handling for picture content control (how to interact with them), and the default handler // killed the image selection.
CPPUNIT_ASSERT(pWrtShell->IsFrameSelected());
}
CPPUNIT_TEST_FIXTURE(Test, testInsertPictureContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->InsertContentControl(SwContentControlType::PICTURE);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); // Without the accompanying fix in place, this test would have failed, there was no special // handling for picture content control, no placeholder fly content was inserted.
CPPUNIT_ASSERT(pContentControl->GetPicture());
CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(1, RES_TXTATR_FLYCNT));
}
CPPUNIT_TEST_FIXTURE(Test, testSelectDateContentControl)
{ // Given a document with a date content control:
createSwDoc();
uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
uno::Reference<text::XText> xText = xTextDocument->getText();
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
xText->insertString(xCursor, u"test"_ustr, /*bAbsorb=*/false);
xCursor->gotoStart(/*bExpand=*/false);
xCursor->gotoEnd(/*bExpand=*/true);
uno::Reference<text::XTextContent> xContentControl(
xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
xContentControlProps->setPropertyValue(u"Date"_ustr, uno::Any(true));
xContentControlProps->setPropertyValue(u"DateFormat"_ustr, uno::Any(u"YYYY-MM-DD"_ustr));
xContentControlProps->setPropertyValue(u"DateLanguage"_ustr, uno::Any(u"en-US"_ustr));
xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
// When clicking on that content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
rFormatContentControl.GetContentControl()->SetSelectedDate(44705);
pWrtShell->GotoContentControl(rFormatContentControl);
// Then make sure that the document text is updated: // Without the accompanying fix in place, this test would have failed with: // - Expected: 2022-05-24 // - Actual : test // i.e. the content control was not updated.
CPPUNIT_ASSERT_EQUAL(u"2022-05-24"_ustr, pTextNode->GetExpandText(pWrtShell->GetLayout()));
CPPUNIT_ASSERT_EQUAL(u"2022-05-24T00:00:00Z"_ustr,
rFormatContentControl.GetContentControl()->GetCurrentDate());
}
CPPUNIT_TEST_FIXTURE(Test, testInsertDateContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a date content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->InsertContentControl(SwContentControlType::DATE);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); // Without the accompanying fix in place, this test would have failed, there was no special // handling for date content control.
CPPUNIT_ASSERT(pContentControl->GetDate());
}
CPPUNIT_TEST_FIXTURE(Test, testInsertPlainTextContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a plain text content control:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->InsertContentControl(SwContentControlType::PLAIN_TEXT);
// Then make sure that the matching text attribute is added to the document model:
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode();
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); // Without the accompanying fix in place, this test would have failed, there was no special // handling for plain text content controls.
CPPUNIT_ASSERT(pContentControl->GetPlainText());
CPPUNIT_ASSERT(pContentControl->GetShowingPlaceHolder());
pWrtShell->GotoContentControl(rFormatContentControl);
CPPUNIT_ASSERT(pContentControl->GetShowingPlaceHolder());
pWrtShell->Insert(u"Foo"_ustr); // No longer showing placeholder text, as it has been changed
CPPUNIT_ASSERT(!pContentControl->GetShowingPlaceHolder());
}
CPPUNIT_TEST_FIXTURE(Test, testInsertComboBoxContentControl)
{ // Given an empty document:
createSwDoc();
// When inserting a combo box content control:
dispatchCommand(mxComponent, u".uno:InsertComboBoxContentControl"_ustr, {});
// Then make sure that the matching text attribute is added to the document model:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
SwTextNode* pTextNode = pWrtShell->GetCursor()->GetPointNode().GetTextNode(); // Without the accompanying fix in place, this test would have failed, no content control was // inserted.
SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
CPPUNIT_ASSERT(pAttr); auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
CPPUNIT_ASSERT(pContentControl->GetComboBox());
}
CPPUNIT_TEST_FIXTURE(Test, testSplitFlysAnchorJoin)
{ // Given a document with two paragraphs, each serving as an anchor of a split fly:
createSwDoc();
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->Insert(u"first para"_ustr);
pWrtShell->SplitNode();
pWrtShell->Insert(u"second para"_ustr);
pWrtShell->SttEndDoc(/*bStt=*/true);
InsertSplitFly(pWrtShell);
pWrtShell->SttEndDoc(/*bStt=*/false);
pWrtShell->SttPara();
InsertSplitFly(pWrtShell);
// When trying to delete at the end of the first para:
pWrtShell->SttEndDoc(/*bStt=*/true);
pWrtShell->EndPara();
pWrtShell->DelRight();
// Then make sure the join doesn't happen till a text node can only be an anchor for one split // fly:
pWrtShell->SttEndDoc(/*bStt=*/true);
SwCursor* pCursor = pWrtShell->GetCursor(); // Without the accompanying fix in place, this test would have failed with: // - Expected: first para // - Actual : first parasecond para // i.e. we did join the 2 anchors and for complex enough documents the layout never finished.
CPPUNIT_ASSERT_EQUAL(u"first para"_ustr, pCursor->GetPointNode().GetTextNode()->GetText());
pWrtShell->SttEndDoc(/*bStt=*/false);
CPPUNIT_ASSERT_EQUAL(u"second para"_ustr, pCursor->GetPointNode().GetTextNode()->GetText());
}
CPPUNIT_TEST_FIXTURE(Test, testBulletCharChangeOnIndent)
{ // Given an empty document:
createSwDoc();
// When adding 2 bullets, A is level 1, B is level 2:
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->BulletOn();
pWrtShell->Insert(u"A"_ustr);
pWrtShell->SplitNode(); // Increase indent: downgrade to level 2.
pWrtShell->NumUpDown(/*bDown=*/true);
pWrtShell->Insert(u"B"_ustr);
// Then make sure the bullet characters are different:
pWrtShell->Up(/*bSelect=*/false);
SwCursor* pCursor = pWrtShell->GetCursor();
sal_UCS4 nBullet1 = 0;
{
SwTextNode* pTextNode = pCursor->GetPointNode().GetTextNode();
SwNumRule* pNumRule = pTextNode->GetNumRule(); const SwNumFormat& rNumFormat = pNumRule->Get(pTextNode->GetActualListLevel());
nBullet1 = rNumFormat.GetBulletChar();
}
pWrtShell->Down(/*bSelect=*/false);
sal_UCS4 nBullet2 = 0;
{
SwTextNode* pTextNode = pCursor->GetPointNode().GetTextNode();
SwNumRule* pNumRule = pTextNode->GetNumRule(); const SwNumFormat& rNumFormat = pNumRule->Get(pTextNode->GetActualListLevel());
nBullet2 = rNumFormat.GetBulletChar();
} // Without the accompanying fix in place, this test would have failed, while nBullet1 should be // • and nBullet2 should be ◦.
CPPUNIT_ASSERT(nBullet1 != nBullet2);
}
CPPUNIT_TEST_FIXTURE(Test, testRemoveIndent)
{ // Given a document with an empty, bulleted paragraph at the document end:
createSwDoc("remove-indent.docx");
SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
pWrtShell->SttEndDoc(/*bStt=*/false); // Press backspace once to make it not numbered: bool bOnlyBackspaceKey = true;
pWrtShell->NumOrNoNum(!bOnlyBackspaceKey);
// When pressing backspace again to try to decrease its indent to change from left margin to // first line margin:
pWrtShell->TryRemoveIndent();
// Then make sure we actually decrease the indent:
SwPaM* pCursor = pWrtShell->GetCursor();
SwTextNode* pTextNode = pCursor->GetPointNode().GetTextNode();
SwTwips nLeftMargin = pTextNode->GetSwAttrSet().GetTextLeftMargin().GetTextLeft().m_dValue; // Without the accompanying fix in place, this test would have failed with: // - Expected: 1135 // - Actual : 1418 // i.e. there was no decrease of the left text margin on pressing backspace.
CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(1135), nLeftMargin);
}
}