/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { char const DATA_DIRECTORY[] = "/sw/qa/extras/uiwriter/data2/"; char const FLOATING_TABLE_DATA_DIRECTORY[] = "/sw/qa/extras/uiwriter/data/floating_table/"; } // namespace /// Second set of tests asserting the behavior of Writer user interface shells. class SwUiWriterTest2 : public SwModelTestBase { public: virtual std::unique_ptr preTest(const char* filename) override { m_aSavedSettings = Application::GetSettings(); if (OString(filename).indexOf("LocaleArabic") != -1) { std::unique_ptr pResetter( new Resetter([this]() { Application::SetSettings(this->m_aSavedSettings); })); AllSettings aSettings(m_aSavedSettings); aSettings.SetLanguageTag(LanguageTag("ar")); Application::SetSettings(aSettings); return pResetter; } return nullptr; } protected: AllSettings m_aSavedSettings; SwDoc* createDoc(const char* pName = nullptr); }; SwDoc* SwUiWriterTest2::createDoc(const char* pName) { if (!pName) loadURL("private:factory/swriter", nullptr); else load(DATA_DIRECTORY, pName); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); return pTextDoc->GetDocShell()->GetDoc(); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf47471_paraStyleBackground) { SwDoc* pDoc = createDoc("tdf47471_paraStyleBackground.odt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT_EQUAL(OUString("00Background"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(sal_Int32(14729933), getProperty(getParagraph(2), "FillColor")); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->EndPara(/*bSelect=*/true); dispatchCommand(mxComponent, ".uno:ResetAttributes", {}); // the background color should revert to the color for 00Background style CPPUNIT_ASSERT_EQUAL(sal_Int32(14605542), getProperty(getParagraph(2), "FillColor")); // the paragraph style should not be reset CPPUNIT_ASSERT_EQUAL(OUString("00Background"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("00Background"), getProperty(getParagraph(3), "ParaStyleName")); // Save it and load it back. reload("writer8", "tdf47471_paraStyleBackgroundRT.odt"); CPPUNIT_ASSERT_EQUAL(sal_Int32(14605542), getProperty(getParagraph(2), "FillColor")); // on round-trip, the paragraph style name was lost CPPUNIT_ASSERT_EQUAL(OUString("00Background"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("00Background"), getProperty(getParagraph(3), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf101534) { // Copy the first paragraph of the document. load(DATA_DIRECTORY, "tdf101534.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->EndPara(/*bSelect=*/true); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Copy(); // Go to the second paragraph, assert that we have margins as direct // formatting. pWrtShell->Down(/*bSelect=*/false); SfxItemSet aSet(pWrtShell->GetAttrPool(), svl::Items{}); pWrtShell->GetCurAttr(aSet); CPPUNIT_ASSERT(aSet.HasItem(RES_LR_SPACE)); // Make sure that direct formatting is preserved during paste. pWrtShell->EndPara(/*bSelect=*/false); TransferableDataHelper aHelper(pTransfer.get()); SwTransferable::Paste(*pWrtShell, aHelper); aSet.ClearItem(); pWrtShell->GetCurAttr(aSet); // This failed, direct formatting was lost. CPPUNIT_ASSERT(aSet.HasItem(RES_LR_SPACE)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testRedlineMoveInsertInDelete) { loadURL("private:factory/swriter", nullptr); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); SwWrtShell* const pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Insert(" foo"); pWrtShell->SttEndDoc(true); pWrtShell->InsertFootnote(""); CPPUNIT_ASSERT(pWrtShell->IsCursorInFootnote()); RedlineFlags const mode(pWrtShell->GetRedlineFlags() | RedlineFlags::On); CPPUNIT_ASSERT(mode & (RedlineFlags::ShowDelete | RedlineFlags::ShowInsert)); pWrtShell->SetRedlineFlags(mode); // insert redline pWrtShell->Insert("bar"); // first delete redline, logically containing the insert redline // (note: Word apparently allows similar things...) pWrtShell->SttEndDoc(true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); pWrtShell->Delete(); // the footnote // second delete redline, following the first one pWrtShell->EndOfSection(false); pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 3, /*bBasicCall=*/false); pWrtShell->Delete(); // "foo" // hiding used to copy the 2nd delete redline "foo", but not delete it pWrtShell->SetRedlineFlags(mode & ~RedlineFlags::ShowDelete); // hide CPPUNIT_ASSERT_EQUAL( OUString(" "), pWrtShell->GetCursor()->GetPoint()->nNode.GetNode().GetTextNode()->GetText()); pWrtShell->SetRedlineFlags(mode); // show again CPPUNIT_ASSERT_EQUAL( OUString(u"\u0001 foo"), pWrtShell->GetCursor()->GetPoint()->nNode.GetNode().GetTextNode()->GetText()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testRedlineInHiddenSection) { loadURL("private:factory/swriter", nullptr); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); SwWrtShell* const pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->SplitNode(); pWrtShell->Insert("foo"); pWrtShell->SplitNode(); pWrtShell->Insert("bar"); pWrtShell->SplitNode(); pWrtShell->Insert("baz"); RedlineFlags const mode(pWrtShell->GetRedlineFlags() | RedlineFlags::On); CPPUNIT_ASSERT(mode & (RedlineFlags::ShowDelete | RedlineFlags::ShowInsert)); pWrtShell->SetRedlineFlags(mode); // delete paragraph "bar" pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false); pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 8, /*bBasicCall=*/false); pWrtShell->Delete(); pWrtShell->StartOfSection(); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); pWrtShell->EndOfSection(true); SwSectionData section(SectionType::Content, pWrtShell->GetUniqueSectionName()); section.SetHidden(true); SwSection const* pSection = pWrtShell->InsertSection(section, nullptr); SwSectionNode const* pNode = pSection->GetFormat()->GetSectionNode(); CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 1]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 2]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 3]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT(pNode->GetNodes()[pNode->GetIndex() + 4]->IsEndNode()); pWrtShell->SetRedlineFlags(mode & ~RedlineFlags::ShowDelete); // hide CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 1]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT(pNode->GetNodes()[pNode->GetIndex() + 2]->IsEndNode()); pWrtShell->SetRedlineFlags(mode); // show again CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 1]->GetTextNode()->getLayoutFrame(nullptr)); // there was a frame created here CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 2]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT( !pNode->GetNodes()[pNode->GetIndex() + 3]->GetTextNode()->getLayoutFrame(nullptr)); CPPUNIT_ASSERT(pNode->GetNodes()[pNode->GetIndex() + 4]->IsEndNode()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf54819) { load(DATA_DIRECTORY, "tdf54819.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); //turn on red-lining and hide changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE("redlines shouldn't be visible", !IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // remove first paragraph with paragraph break SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // remaining paragraph keeps its original style CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(1), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf54819_keep_numbering_with_Undo) { load(DATA_DIRECTORY, "tdf54819b.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); OUString sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); //turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE("redlines shouldn't be visible", !IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // remove heading with paragraph break SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // solved problem: changing paragraph style after deletion CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); sNumName = getProperty(getParagraph(2), "NumberingStyleName"); // solved problem: lost numbering CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // accept deletion, remaining (now second) paragraph: still bulleted list item IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); sNumName = getProperty(getParagraph(2), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // solved problem: Undo with the workaround sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); rUndoManager.Undo(); // heading, manual test is correct // TODO: it works well, but the test fails... // SwWrtShell* const pWrtShell2 = pTextDoc->GetDocShell()->GetWrtShell(); // CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), // getProperty(getParagraph(2), "ParaStyleName")); // CPPUNIT_ASSERT_EQUAL(OUString("Outline"), // getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119571_keep_numbering_with_Undo) { // as the previous test, but with partial paragraph deletion: // all deleted paragraphs get the formatting of the first (the partially deleted) one load(DATA_DIRECTORY, "tdf54819b.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); OUString sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // third paragraph: normal text without numbering CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(4), "ParaStyleName")); sNumName = getProperty(getParagraph(4), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Bad numbering", sNumName.isEmpty()); //turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE("redlines shouldn't be visible", !IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // remove only end part of the heading and the next numbered paragraph with paragraph break SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 6, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 2, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // solved problem: changing paragraph style after deletion CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); // solved problem: apply numbering CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // accept deletion IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); // Joined paragraph 2 and paragraph 4: Fusce...nunc. CPPUNIT_ASSERT(getParagraph(2)->getString().startsWith("Fusce")); CPPUNIT_ASSERT(getParagraph(2)->getString().endsWith("nunc.")); // Remaining (now second) paragraph: it is still heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // solved problem: Undo with the workaround sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); rUndoManager.Undo(); // heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // third paragraph: normal text without numbering CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(4), "ParaStyleName")); sNumName = getProperty(getParagraph(4), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Bad numbering", sNumName.isEmpty()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119571_keep_numbering_with_Reject) { // as the previous test, but with partial paragraph deletion: // all deleted paragraphs get the formatting of the first (the partially deleted) one load(DATA_DIRECTORY, "tdf54819b.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); OUString sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // third paragraph: normal text without numbering CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(4), "ParaStyleName")); sNumName = getProperty(getParagraph(4), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Bad numbering", sNumName.isEmpty()); //turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE("redlines shouldn't be visible", !IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // remove only end part of the heading and the next numbered paragraph with paragraph break SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 6, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 2, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // solved problem: changing paragraph style after deletion CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); // solved problem: apply numbering CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // reject deletion IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(false); // heading CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Outline"), getProperty(getParagraph(2), "NumberingStyleName")); // next paragraph: bulleted list item CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(3), "ParaStyleName")); sNumName = getProperty(getParagraph(3), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Missing numbering style", !sNumName.isEmpty()); CPPUNIT_ASSERT_MESSAGE("Not a bulleted list item", sNumName != "Outline"); // third paragraph: normal text without numbering CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(4), "ParaStyleName")); sNumName = getProperty(getParagraph(4), "NumberingStyleName"); CPPUNIT_ASSERT_MESSAGE("Bad numbering", sNumName.isEmpty()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf109376_redline) { SwDoc* pDoc = createDoc(); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); // need 2 paragraphs to get to the bMoveNds case pWrtShell->Insert("foo"); pWrtShell->SplitNode(); pWrtShell->Insert("bar"); pWrtShell->SplitNode(); pWrtShell->StartOfSection(false); // add AT_PARA fly at 1st to be deleted node SwFormatAnchor anchor(RndStdIds::FLY_AT_PARA); anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint()); SfxItemSet flySet(pDoc->GetAttrPool(), svl::Items{}); flySet.Put(anchor); SwFormatFrameSize size(SwFrameSize::Minimum, 1000, 1000); flySet.Put(size); // set a size, else we get 1 char per line... SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true); CPPUNIT_ASSERT(pFly != nullptr); pWrtShell->SttEndDoc(false); SwInsertTableOptions tableOpt(SwInsertTableFlags::DefaultBorder, 0); const SwTable& rTable = pWrtShell->InsertTable(tableOpt, 1, 1); pWrtShell->StartOfSection(false); SwPaM pam(*pWrtShell->GetCursor()->GetPoint()); pam.SetMark(); pam.GetPoint()->nNode = *rTable.GetTableNode(); pam.GetPoint()->nContent.Assign(nullptr, 0); pam.Exchange(); // same selection direction as in doc compare... IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); rIDRA.AppendRedline(new SwRangeRedline(RedlineType::Delete, pam), true); // this used to assert/crash with m_pAnchoredFlys mismatch because the // fly was not deleted but its anchor was moved to the SwTableNode rIDRA.AcceptAllRedline(true); CPPUNIT_ASSERT_EQUAL(size_t(0), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); CPPUNIT_ASSERT_EQUAL(size_t(1), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); rUndoManager.Redo(); CPPUNIT_ASSERT_EQUAL(size_t(0), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); rUndoManager.Undo(); CPPUNIT_ASSERT_EQUAL(size_t(1), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf109376) { SwDoc* pDoc = createDoc(); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); // need 2 paragraphs to get to the bMoveNds case pWrtShell->Insert("foo"); pWrtShell->SplitNode(); pWrtShell->Insert("bar"); pWrtShell->SplitNode(); pWrtShell->StartOfSection(false); // add AT_PARA fly at 1st to be deleted node SwFormatAnchor anchor(RndStdIds::FLY_AT_PARA); anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint()); SfxItemSet flySet(pDoc->GetAttrPool(), svl::Items{}); flySet.Put(anchor); SwFormatFrameSize size(SwFrameSize::Minimum, 1000, 1000); flySet.Put(size); // set a size, else we get 1 char per line... SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true); CPPUNIT_ASSERT(pFly != nullptr); pWrtShell->SttEndDoc(false); SwInsertTableOptions tableOpt(SwInsertTableFlags::DefaultBorder, 0); const SwTable& rTable = pWrtShell->InsertTable(tableOpt, 1, 1); pWrtShell->StartOfSection(false); SwPaM pam(*pWrtShell->GetCursor()->GetPoint()); pam.SetMark(); pam.GetPoint()->nNode = *rTable.GetTableNode(); pam.GetPoint()->nContent.Assign(nullptr, 0); pam.Exchange(); // same selection direction as in doc compare... // this used to assert/crash with m_pAnchoredFlys mismatch because the // fly was not deleted but its anchor was moved to the SwTableNode pDoc->getIDocumentContentOperations().DeleteRange(pam); CPPUNIT_ASSERT_EQUAL(size_t(0), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); CPPUNIT_ASSERT_EQUAL(size_t(1), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); rUndoManager.Redo(); CPPUNIT_ASSERT_EQUAL(size_t(0), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); rUndoManager.Undo(); CPPUNIT_ASSERT_EQUAL(size_t(1), pWrtShell->GetFlyCount(FLYCNTTYPE_FRM)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf64242_optimizeTable) { SwDoc* pDoc = createDoc("tdf64242_optimizeTable.odt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xModel(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); uno::Reference xTextTable(xTables->getByIndex(0), uno::UNO_QUERY); uno::Reference xTableRows = xTextTable->getRows(); double origWidth = getProperty(xTextTable, "Width"); sal_Int32 nToleranceW = origWidth * .01; CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Table Width", double(17013), origWidth, nToleranceW); pWrtShell->SelTable(); //select the whole table dispatchCommand(mxComponent, ".uno:SetOptimalColumnWidth", {}); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Table Width: optimize", origWidth, getProperty(xTextTable, "Width"), nToleranceW); dispatchCommand(mxComponent, ".uno:SetMinimalColumnWidth", {}); CPPUNIT_ASSERT_MESSAGE("Table Width: minimized", (origWidth - nToleranceW) > getProperty(xTextTable, "Width")); double origRowHeight = getProperty(xTableRows->getByIndex(2), "Height"); sal_Int32 nToleranceH = origRowHeight * .01; CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Row Height", double(3441), origRowHeight, nToleranceH); dispatchCommand(mxComponent, ".uno:SetOptimalRowHeight", {}); double optimalRowHeight = getProperty(xTableRows->getByIndex(2), "Height"); CPPUNIT_ASSERT_MESSAGE("Row Height: optimized", (origRowHeight - nToleranceH) > optimalRowHeight); dispatchCommand(mxComponent, ".uno:SetMinimalRowHeight", {}); double minimalRowHeight = getProperty(xTableRows->getByIndex(2), "Height"); CPPUNIT_ASSERT_MESSAGE("Row Height: minimized", (optimalRowHeight - nToleranceH) > minimalRowHeight); CPPUNIT_ASSERT_EQUAL_MESSAGE("Row set to auto-height", double(0), minimalRowHeight); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf126784_distributeSelectedColumns) { SwDoc* pDoc = createDoc("tdf126784_distributeSelectedColumns.odt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xModel(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); uno::Reference xTextTable(xTables->getByIndex(0), uno::UNO_QUERY); uno::Reference xTableRows = xTextTable->getRows(); auto aSeq = getProperty>(xTableRows->getByIndex(0), "TableColumnSeparators"); sal_Int16 nOrigCol2Pos = aSeq[0].Position; sal_Int16 nOrigCol3Pos = aSeq[1].Position; //Select column 1 and 2 pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); dispatchCommand(mxComponent, ".uno:DistributeColumns", {}); aSeq = getProperty>(xTableRows->getByIndex(0), "TableColumnSeparators"); CPPUNIT_ASSERT_MESSAGE("Second column should shrink", nOrigCol2Pos < aSeq[0].Position); CPPUNIT_ASSERT_EQUAL_MESSAGE("Last column shouldn't change", nOrigCol3Pos, aSeq[1].Position); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf108687_tabstop) { SwDoc* pDoc = createDoc("tdf108687_tabstop.odt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); sal_Int32 nStartIndex = pWrtShell->GetCursor()->GetNode().GetIndex(); CPPUNIT_ASSERT_EQUAL(sal_Int32(9), nStartIndex); // Now pressing 'tab' should jump to the radio buttons. SwXTextDocument* pXTextDocument = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pXTextDocument); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB); Scheduler::ProcessEventsToIdle(); //sal_Int32 nEndIndex = pWrtShell->GetCursor()->GetNode().GetIndex(); //CPPUNIT_ASSERT_EQUAL(sal_Int32(11), nEndIndex); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119571) { load(DATA_DIRECTORY, "tdf54819.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); //turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // join paragraphs by removing the end of the first one with paragraph break SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // second paragraph changes its style in "Show changes" mode CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119019) { // check handling of overlapping redlines load(DATA_DIRECTORY, "tdf119019.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus."), getParagraph(2)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(2), 1)->getString()); // second paragraph has got a tracked paragraph formatting at this point CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 1), "RedlineType")); // delete last word of the second paragraph to remove tracked paragraph formatting // of this paragraph to track and show word deletion correctly. SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/false); pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 7, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // check tracked text deletion CPPUNIT_ASSERT_EQUAL(OUString("tellus."), getRun(getParagraph(2), 3)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(2), 2)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 2), "RedlineType")); // make sure that the tracked paragraph formatting is removed CPPUNIT_ASSERT(!hasProperty(getRun(getParagraph(2), 1), "RedlineType")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119824) { // check handling of overlapping redlines with Redo SwDoc* pDoc = createDoc("tdf119019.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Pellentesque habitant morbi tristique senectus " "et netus et malesuada fames ac turpis egestas. " "Proin pharetra nonummy pede. Mauris et orci."), getParagraph(3)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 1)->getString()); // third paragraph has got a tracked paragraph formatting at this point CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 1), "RedlineType")); // and a tracked text deletion at the beginning of the paragraph CPPUNIT_ASSERT_EQUAL(OUString("Pellentesque habitant morbi tristique senectus "), getRun(getParagraph(3), 3)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 2)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 2), "RedlineType")); // delete last word of the third paragraph to remove tracked paragraph formatting // of this paragraph to track and show word deletion correctly. SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/false); pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 5, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // check tracking of the new text deletion CPPUNIT_ASSERT_EQUAL(OUString("orci."), getRun(getParagraph(3), 7)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 6)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 6), "RedlineType")); // make sure that the tracked paragraph formatting is removed (tracked deletion is in the second run) CPPUNIT_ASSERT_EQUAL(OUString("Pellentesque habitant morbi tristique senectus "), getRun(getParagraph(3), 2)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 1)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 1), "RedlineType")); // tdf#119824 check redo sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); rUndoManager.Undo(); rUndoManager.Redo(); rUndoManager.Redo(); // check again the first tracked text deletion (we lost this before the redo fix) CPPUNIT_ASSERT_EQUAL(OUString("Pellentesque habitant morbi tristique senectus "), getRun(getParagraph(3), 2)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 1)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 1), "RedlineType")); // check redo of the new tracked text deletion CPPUNIT_ASSERT_EQUAL(OUString("orci."), getRun(getParagraph(3), 7)->getString()); CPPUNIT_ASSERT_EQUAL(OUString(""), getRun(getParagraph(3), 6)->getString()); CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(3), 6), "RedlineType")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf105413) { load(DATA_DIRECTORY, "tdf105413.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // all paragraphs have got Standard paragraph style for (int i = 1; i < 4; ++i) { CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(i), "ParaStyleName")); } // turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // Set Heading 1 paragraph style in the 3th paragraph. // Because of the tracked deleted region between them, // this sets also the same style in the first paragraph automatically // to keep the changed paragraph style at hiding tracked changes or saving the document SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/false); uno::Sequence aPropertyValues = comphelper::InitPropertySequence({ { "Style", uno::makeAny(OUString("Heading 1")) }, { "FamilyName", uno::makeAny(OUString("ParagraphStyles")) }, }); dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(3), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); // first paragraph gets the same heading style CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testUnfloatButtonSmallTable) { // The floating table in the test document is too small, so we don't provide an unfloat button load(FLOATING_TABLE_DATA_DIRECTORY, "small_floating_table.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); const SwSortedObjs* pAnchored = pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetDrawObjs(); CPPUNIT_ASSERT(pAnchored); CPPUNIT_ASSERT_EQUAL(static_cast(1), pAnchored->size()); SwAnchoredObject* pAnchoredObj = (*pAnchored)[0]; SwFlyFrame* pFlyFrame = dynamic_cast(pAnchoredObj); CPPUNIT_ASSERT(pFlyFrame); CPPUNIT_ASSERT(!pFlyFrame->IsShowUnfloatButton(pWrtShell)); SdrObject* pObj = pFlyFrame->GetFormat()->FindRealSdrObject(); CPPUNIT_ASSERT(pObj); pWrtShell->SelectObj(Point(), 0, pObj); CPPUNIT_ASSERT(!pFlyFrame->IsShowUnfloatButton(pWrtShell)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testUnfloatButton) { // Different use cases where unfloat button should be visible const std::vector aTestFiles = { "unfloatable_floating_table.odt", // Typical use case of multipage floating table "unfloatable_floating_table.docx", // Need to test the DOCX import whether we detect the floating table correctly "unfloatable_floating_table.doc", // Also the DOC import "unfloatable_small_floating_table.docx" // Atypical use case, when the table is small, but because of it's position is it broken to two pages }; for (const OUString& aTestFile : aTestFiles) { OString sTestFileName = OUStringToOString(aTestFile, RTL_TEXTENCODING_UTF8); OString sFailureMessage = OStringLiteral("Failure in the test file: ") + sTestFileName; load(FLOATING_TABLE_DATA_DIRECTORY, sTestFileName.getStr()); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pWrtShell); const SwSortedObjs* pAnchored; if (sTestFileName == "unfloatable_small_floating_table.docx") pAnchored = pWrtShell->GetLayout() ->GetLower() ->GetLower() ->GetLower() ->GetNext() ->GetDrawObjs(); else pAnchored = pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetDrawObjs(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pAnchored); CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailureMessage.getStr(), static_cast(1), pAnchored->size()); SwAnchoredObject* pAnchoredObj = (*pAnchored)[0]; // The unfloat button is not visible until it gets selected SwFlyFrame* pFlyFrame = dynamic_cast(pAnchoredObj); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pFlyFrame); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), !pFlyFrame->IsShowUnfloatButton(pWrtShell)); SdrObject* pObj = pFlyFrame->GetFormat()->FindRealSdrObject(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pObj); pWrtShell->SelectObj(Point(), 0, pObj); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pFlyFrame->IsShowUnfloatButton(pWrtShell)); } } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testUnfloatButtonReadOnlyMode) { // In read only mode we don't show the unfloat button even if we have a multipage floating table load(FLOATING_TABLE_DATA_DIRECTORY, "unfloatable_floating_table.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); pWrtShell->SetReadonlyOption(true); const SwSortedObjs* pAnchored = pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetDrawObjs(); CPPUNIT_ASSERT(pAnchored); CPPUNIT_ASSERT_EQUAL(static_cast(1), pAnchored->size()); SwAnchoredObject* pAnchoredObj = (*pAnchored)[0]; SwFlyFrame* pFlyFrame = dynamic_cast(pAnchoredObj); CPPUNIT_ASSERT(pFlyFrame); CPPUNIT_ASSERT(!pFlyFrame->IsShowUnfloatButton(pWrtShell)); SdrObject* pObj = pFlyFrame->GetFormat()->FindRealSdrObject(); CPPUNIT_ASSERT(pObj); pWrtShell->SelectObj(Point(), 0, pObj); CPPUNIT_ASSERT(!pFlyFrame->IsShowUnfloatButton(pWrtShell)); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testUnfloating) { // Test unfloating with tables imported from different file formats const std::vector aTestFiles = { "unfloatable_floating_table.odt", "unfloatable_floating_table.docx", "unfloatable_floating_table.doc", }; for (const OUString& aTestFile : aTestFiles) { OString sTestFileName = OUStringToOString(aTestFile, RTL_TEXTENCODING_UTF8); OString sFailureMessage = OStringLiteral("Failure in the test file: ") + sTestFileName; // Test what happens when pushing the unfloat button load(FLOATING_TABLE_DATA_DIRECTORY, "unfloatable_floating_table.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pWrtShell); SwFlyFrame* pFlyFrame; // Before unfloating we have only one page with a fly frame { CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailureMessage.getStr(), SwFrameType::Page, pWrtShell->GetLayout()->GetLower()->GetType()); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), !pWrtShell->GetLayout()->GetLower()->GetNext()); CPPUNIT_ASSERT_EQUAL_MESSAGE( sFailureMessage.getStr(), SwFrameType::Txt, pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetType()); const SwSortedObjs* pAnchored = pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetDrawObjs(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pAnchored); CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailureMessage.getStr(), static_cast(1), pAnchored->size()); SwAnchoredObject* pAnchoredObj = (*pAnchored)[0]; pFlyFrame = dynamic_cast(pAnchoredObj); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pFlyFrame); } // Select the floating table SdrObject* pObj = pFlyFrame->GetFormat()->FindRealSdrObject(); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pObj); pWrtShell->SelectObj(Point(), 0, pObj); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pFlyFrame->IsShowUnfloatButton(pWrtShell)); // Push the unfloat button pFlyFrame->ActiveUnfloatButton(pWrtShell); Scheduler::ProcessEventsToIdle(); // After unfloating we have two pages with one table frame on each page CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), pWrtShell->GetLayout()->GetLower()->GetNext()); CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailureMessage.getStr(), SwFrameType::Page, pWrtShell->GetLayout()->GetLower()->GetNext()->GetType()); CPPUNIT_ASSERT_MESSAGE(sFailureMessage.getStr(), !pWrtShell->GetLayout()->GetLower()->GetNext()->GetNext()); CPPUNIT_ASSERT_EQUAL_MESSAGE( sFailureMessage.getStr(), SwFrameType::Tab, pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower()->GetType()); CPPUNIT_ASSERT_EQUAL_MESSAGE( sFailureMessage.getStr(), SwFrameType::Tab, pWrtShell->GetLayout()->GetLower()->GetNext()->GetLower()->GetLower()->GetType()); } } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testRTLparaStyle_LocaleArabic) { // New documents, created in RTL locales, were not round-tripping the paragraph style as RTL. // Set the locale to "ar" for this test - see preTest() at the top of this file. std::unique_ptr const pChanges(preTest("LocaleArabic")); createDoc(); // new, empty doc - everything defaults to RTL with Arabic locale // Save it and load it back. reload("Office Open XML Text", "tdf116404_paraStyleFrameDir.docx"); uno::Reference xPageStyle( getStyles("ParagraphStyles")->getByName("Default Style"), uno::UNO_QUERY_THROW); // Test the text Direction value for the -none- based paragraph styles CPPUNIT_ASSERT_EQUAL_MESSAGE("RTL Writing Mode", sal_Int32(1), getProperty(xPageStyle, "WritingMode")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122893) { load(DATA_DIRECTORY, "tdf105413.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // all paragraphs are left-aligned with preset single line spacing for (int i = 1; i < 4; ++i) { CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(i), "ParaAdjust")); dispatchCommand(mxComponent, ".uno:SpacePara1", {}); } // turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // Set center-aligned paragraph with preset double line spacing in the 3th paragraph. // Because of the tracked deleted region between them, // this sets also the same formatting in the first paragraph automatically // to keep the changed paragraph formatting at hiding tracked changes or saving the document SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/false); dispatchCommand(mxComponent, ".uno:CenterPara", {}); dispatchCommand(mxComponent, ".uno:SpacePara2", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty(getParagraph(3), "ParaAdjust")); // center-aligned CPPUNIT_ASSERT_EQUAL(sal_Int16(200), getProperty(getParagraph(3), "ParaLineSpacing") .Height); // double line spacing CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(2), "ParaAdjust")); // left-aligned CPPUNIT_ASSERT_EQUAL(sal_Int16(100), getProperty(getParagraph(2), "ParaLineSpacing") .Height); // single line spacing // first paragraph is also center-aligned with double line spacing CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty(getParagraph(1), "ParaAdjust")); CPPUNIT_ASSERT_EQUAL( sal_Int16(200), getProperty(getParagraph(1), "ParaLineSpacing").Height); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122901) { load(DATA_DIRECTORY, "tdf105413.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // all paragraphs with zero borders for (int i = 1; i < 4; ++i) { CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(i), "ParaTopMargin")); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(i), "ParaBottomMargin")); } // turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // Increase paragraph borders in the 3th paragraph, similar to the default icon of the UI // "Increase Paragraph Spacing". Because of the tracked deleted region between them, // this sets also the same formatting in the first paragraph automatically // to keep the changed paragraph formatting at hiding tracked changes or saving the document SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->EndPara(/*bSelect=*/false); dispatchCommand(mxComponent, ".uno:ParaspaceIncrease", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(101), getProperty(getParagraph(3), "ParaTopMargin")); CPPUNIT_ASSERT_EQUAL(sal_Int32(101), getProperty(getParagraph(3), "ParaBottomMargin")); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(2), "ParaTopMargin")); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(2), "ParaBottomMargin")); // first paragraph is also center-aligned with double line spacing CPPUNIT_ASSERT_EQUAL(sal_Int32(101), getProperty(getParagraph(1), "ParaTopMargin")); CPPUNIT_ASSERT_EQUAL(sal_Int32(101), getProperty(getParagraph(1), "ParaBottomMargin")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122942) { load(DATA_DIRECTORY, "tdf122942.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); // Do the moral equivalent of mouse button down, move and up. // Start creating a custom shape that overlaps with the rounded rectangle // already present in the document. Point aStartPos(8000, 3000); pWrtShell->BeginCreate(static_cast(OBJ_CUSTOMSHAPE), aStartPos); // Set its size. Point aMovePos(10000, 5000); pWrtShell->MoveCreate(aMovePos); // Finish creation. pWrtShell->EndCreate(SdrCreateCmd::ForceEnd); // Make sure that the shape is inserted. SwDoc* pDoc = pWrtShell->GetDoc(); const SwFrameFormats& rFormats = *pDoc->GetSpzFrameFormats(); CPPUNIT_ASSERT_EQUAL(static_cast(2), rFormats.size()); reload("writer8", "tdf122942.odt"); pTextDoc = dynamic_cast(mxComponent.get()); pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pDoc = pWrtShell->GetDoc(); const SwFrameFormats& rFormats2 = *pDoc->GetSpzFrameFormats(); CPPUNIT_ASSERT_EQUAL(static_cast(2), rFormats2.size()); // Make sure the top of the inserted shape does not move outside the existing shape, even after // reload. SdrObject* pObject1 = rFormats2[0]->FindSdrObject(); CPPUNIT_ASSERT(pObject1); const tools::Rectangle& rOutRect1 = pObject1->GetLastBoundRect(); SdrObject* pObject2 = rFormats2[1]->FindSdrObject(); CPPUNIT_ASSERT(pObject2); const tools::Rectangle& rOutRect2 = pObject2->GetLastBoundRect(); CPPUNIT_ASSERT(rOutRect2.Top() > rOutRect1.Top() && rOutRect2.Top() < rOutRect1.Bottom()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf52391) { load(DATA_DIRECTORY, "tdf52391.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {}); const uno::Reference xRun = getRun(getParagraph(1), 1); // this was "Portion1", because the tracked background color of Portion1 was // accepted for "Reject All". Now rejection clears formatting of the text // in format-only changes, concatenating the text portions in the first paragraph. CPPUNIT_ASSERT_EQUAL(OUString("Portion1Portion2"), xRun->getString()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf126206) { load(DATA_DIRECTORY, "tdf126206.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // normal text (it was bold) auto xText = getParagraph(1)->getText(); CPPUNIT_ASSERT(xText.is()); { auto xCursor(xText->createTextCursorByRange(getRun(getParagraph(1), 4))); CPPUNIT_ASSERT(xCursor.is()); CPPUNIT_ASSERT_EQUAL(OUString("ipsum"), xCursor->getString()); CPPUNIT_ASSERT_EQUAL(awt::FontWeight::NORMAL, getProperty(xCursor, "CharWeight")); } // reject tracked changes dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {}); // bold text again xText = getParagraph(1)->getText(); CPPUNIT_ASSERT(xText.is()); { auto xCursor(xText->createTextCursorByRange(getRun(getParagraph(1), 3))); CPPUNIT_ASSERT(xCursor.is()); CPPUNIT_ASSERT_EQUAL(OUString("ipsum"), xCursor->getString()); CPPUNIT_ASSERT_EQUAL(awt::FontWeight::BOLD, getProperty(xCursor, "CharWeight")); } } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf101873) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); SwDocShell* pDocShell = pDoc->GetDocShell(); CPPUNIT_ASSERT(pDocShell); SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); // Insert some content. pWrtShell->Insert("something"); // Search for something which does not exist, twice. uno::Sequence aFirst(comphelper::InitPropertySequence({ { "SearchItem.SearchString", uno::makeAny(OUString("fig")) }, { "SearchItem.Backward", uno::makeAny(false) }, })); dispatchCommand(mxComponent, ".uno:ExecuteSearch", aFirst); dispatchCommand(mxComponent, ".uno:ExecuteSearch", aFirst); uno::Sequence aSecond(comphelper::InitPropertySequence({ { "SearchItem.SearchString", uno::makeAny(OUString("something")) }, { "SearchItem.Backward", uno::makeAny(false) }, })); dispatchCommand(mxComponent, ".uno:ExecuteSearch", aSecond); // Without the accompanying fix in place, this test would have failed with "Expected: something; // Actual:", i.e. searching for "something" failed, even if it was inserted above. SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false); CPPUNIT_ASSERT_EQUAL(OUString("something"), pShellCursor->GetText()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTableWidth) { load(DATA_DIRECTORY, "frame_size_export.docx"); uno::Reference xStorable(mxComponent, uno::UNO_QUERY); utl::MediaDescriptor aMediaDescriptor; aMediaDescriptor["FilterName"] <<= OUString("Office Open XML Text"); xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); // after exporting: table width was overwritten in the doc model uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xTables(xTablesSupplier->getTextTables(), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(sal_Int16(100), getProperty(xTables->getByIndex(0), "RelativeWidth")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTextFormFieldInsertion) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a text form field dispatchCommand(mxComponent, ".uno:TextFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMTEXT), pFieldmark->GetFieldname()); // The text form field has the placeholder text in it uno::Reference xPara = getParagraph(1); sal_Unicode vEnSpaces[5] = { 8194, 8194, 8194, 8194, 8194 }; CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), xPara->getString()); // Undo insertion dispatchCommand(mxComponent, ".uno:Undo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); xPara.set(getParagraph(1)); CPPUNIT_ASSERT(xPara->getString().isEmpty()); // Redo insertion dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); xPara.set(getParagraph(1)); CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), xPara->getString()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testCheckboxFormFieldInsertion) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a checkbox form field dispatchCommand(mxComponent, ".uno:CheckBoxFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMCHECKBOX), pFieldmark->GetFieldname()); // The checkbox is not checked by default ::sw::mark::ICheckboxFieldmark* pCheckBox = dynamic_cast<::sw::mark::ICheckboxFieldmark*>(pFieldmark); CPPUNIT_ASSERT(pCheckBox); CPPUNIT_ASSERT(!pCheckBox->IsChecked()); // Undo insertion dispatchCommand(mxComponent, ".uno:Undo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Redo insertion dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMCHECKBOX), pFieldmark->GetFieldname()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDropDownFormFieldInsertion) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a drop-down form field dispatchCommand(mxComponent, ".uno:DropDownFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDROPDOWN), pFieldmark->GetFieldname()); // Check drop down field's parameters. By default these params are not set const sw::mark::IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters(); auto pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY); CPPUNIT_ASSERT(bool(pListEntries == pParameters->end())); auto pResult = pParameters->find(ODF_FORMDROPDOWN_RESULT); CPPUNIT_ASSERT(bool(pResult == pParameters->end())); // Undo insertion dispatchCommand(mxComponent, ".uno:Undo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Redo insertion dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDROPDOWN), pFieldmark->GetFieldname()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testMixedFormFieldInsertion) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert fields dispatchCommand(mxComponent, ".uno:TextFormField", {}); dispatchCommand(mxComponent, ".uno:CheckBoxFormField", {}); dispatchCommand(mxComponent, ".uno:DropDownFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMarkAccess->getAllMarksCount()); // Undo insertion dispatchCommand(mxComponent, ".uno:Undo", {}); dispatchCommand(mxComponent, ".uno:Undo", {}); dispatchCommand(mxComponent, ".uno:Undo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Redo insertion dispatchCommand(mxComponent, ".uno:Redo", {}); dispatchCommand(mxComponent, ".uno:Redo", {}); dispatchCommand(mxComponent, ".uno:Redo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(3), pMarkAccess->getAllMarksCount()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf124261) { #if !defined(_WIN32) // Make sure that pressing a key in a btlr cell frame causes an immediate, correct repaint. SwDoc* pDoc = createDoc("tdf124261.docx"); SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); SwFrame* pPageFrame = pLayout->GetLower(); CPPUNIT_ASSERT(pPageFrame->IsPageFrame()); SwFrame* pBodyFrame = pPageFrame->GetLower(); CPPUNIT_ASSERT(pBodyFrame->IsBodyFrame()); SwFrame* pTabFrame = pBodyFrame->GetLower(); CPPUNIT_ASSERT(pTabFrame->IsTabFrame()); SwFrame* pRowFrame = pTabFrame->GetLower(); CPPUNIT_ASSERT(pRowFrame->IsRowFrame()); SwFrame* pCellFrame = pRowFrame->GetLower(); CPPUNIT_ASSERT(pCellFrame->IsCellFrame()); SwFrame* pFrame = pCellFrame->GetLower(); CPPUNIT_ASSERT(pFrame->IsTextFrame()); // Make sure that the text frame's area and the paint rectangle match. // Without the accompanying fix in place, this test would have failed with 'Expected: 1721; // Actual: 1547', i.e. an area other than the text frame was invalidated for a single-line // paragraph. SwTextFrame* pTextFrame = static_cast(pFrame); SwRect aRect = pTextFrame->GetPaintSwRect(); CPPUNIT_ASSERT_EQUAL(pTextFrame->getFrameArea().Top(), aRect.Top()); #endif } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDocxAttributeTableExport) { createDoc("floating-table-position.docx"); // get the table frame, set new values and dismiss the references { uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xDrawPage = xDrawPageSupplier->getDrawPage(); uno::Reference xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); // change the properties // 8133 -> 8000 xShape->setPropertyValue("VertOrientPosition", uno::makeAny(static_cast(8000))); // 5964 -> 5000 xShape->setPropertyValue("HoriOrientPosition", uno::makeAny(static_cast(5000))); // 0 (frame) -> 8 (page print area) xShape->setPropertyValue("VertOrientRelation", uno::makeAny(static_cast(8))); // 8 (page print area) -> 0 (frame) xShape->setPropertyValue("HoriOrientRelation", uno::makeAny(static_cast(0))); } // save it to docx reload("Office Open XML Text", "floating-table-position.docx"); uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xDrawPage = xDrawPageSupplier->getDrawPage(); uno::Reference xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); // test the new values sal_Int32 nValue = getProperty(xShape, "VertOrientPosition"); CPPUNIT_ASSERT(sal_Int32(7999) <= nValue && nValue <= sal_Int32(8001)); nValue = getProperty(xShape, "HoriOrientPosition"); CPPUNIT_ASSERT(sal_Int32(4999) <= nValue && nValue <= sal_Int32(5001)); CPPUNIT_ASSERT_EQUAL(sal_Int16(8), getProperty(xShape, "VertOrientRelation")); CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty(xShape, "HoriOrientRelation")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf118699_redline_numbering) { load(DATA_DIRECTORY, "tdf118699.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); uno::Reference xProps(getParagraph(2), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_MESSAGE("first paragraph after the first deletion: erroneous numbering", !xProps->getPropertyValue("NumberingRules").hasValue()); CPPUNIT_ASSERT_MESSAGE( "first paragraph after the second deletion: missing numbering", getProperty>(getParagraph(5), "NumberingRules") .is()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125881_redline_list_level) { load(DATA_DIRECTORY, "tdf125881.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); uno::Reference xProps(getParagraph(8), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_MESSAGE("deleted paragraph: erroneous numbering", !xProps->getPropertyValue("NumberingRules").hasValue()); // deleted paragraph gets the numbering of the next paragraph uno::Reference xProps2(getParagraph(9), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_MESSAGE("first paragraph after the first deletion: missing numbering", xProps2->getPropertyValue("NumberingRules").hasValue()); // check numbering level at deletion (1 instead of 0) CPPUNIT_ASSERT_EQUAL(sal_Int16(1), getProperty(getParagraph(9), "NumberingLevel")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125916_redline_restart_numbering) { load(DATA_DIRECTORY, "tdf125916.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); // check unnecessary numbering uno::Reference xProps(getParagraph(3), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_MESSAGE("first paragraph after the first deletion: erroneous numbering", !xProps->getPropertyValue("NumberingRules").hasValue()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125310) { load(DATA_DIRECTORY, "tdf125310.fodt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(1, getPages()); // turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); // paragraph join SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); pWrtShell->EndPara(/*bSelect=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false); rtl::Reference pTransfer = new SwTransferable(*pWrtShell); pTransfer->Cut(); // copied paragraph style CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); // without copying the page break CPPUNIT_ASSERT_EQUAL(1, getPages()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125310b) { SwDoc* pDoc = createDoc("tdf125310b.fodt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(3), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(2, getPages()); // turn on red-lining and show changes pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); // remove second paragraph with the page break pWrtShell->Down(/*bSelect=*/false); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Up(/*bSelect=*/true); pWrtShell->DelLeft(); IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); // losing the page break, as without redlining CPPUNIT_ASSERT_EQUAL(1, getPages()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf120336) { load(DATA_DIRECTORY, "tdf120336.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); // turn on red-lining and show changes SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); pDoc->getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowDelete | RedlineFlags::ShowInsert); CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); CPPUNIT_ASSERT_MESSAGE( "redlines should be visible", IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags())); CPPUNIT_ASSERT_EQUAL(2, getPages()); IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess()); rIDRA.AcceptAllRedline(true); // keep page break, as without redlining CPPUNIT_ASSERT_EQUAL(2, getPages()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf106843) { load(DATA_DIRECTORY, "tdf106843.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); // try to turn off red-lining dispatchCommand(mxComponent, ".uno:TrackChanges", {}); // but the protection doesn't allow it CPPUNIT_ASSERT_MESSAGE("redlining should be on", pDoc->getIDocumentRedlineAccess().IsRedlineOn()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testImageComment) { // Load a document with an as-char image in it. SwDoc* pDoc = createDoc("image-comment.odt"); SwView* pView = pDoc->GetDocShell()->GetView(); // Test document has "beforeafter", remove the content before the image. SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); pWrtShell->SttEndDoc(/*bStart=*/true); pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, 6, /*bBasicCall=*/false); pWrtShell->Delete(); // Select the image. pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Insert a comment while the image is selected. pView->GetViewFrame()->GetDispatcher()->Execute(FN_POSTIT, SfxCallMode::SYNCHRON); // Verify that the comment is around the image. // Without the accompanying fix in place, this test would have failed, as FN_POSTIT was disabled // in the frame shell. // Then this test would have failed, as in case the as-char anchored image was at the start of // the paragraph, the comment of the image covered the character after the image, not the image. uno::Reference xPara = getParagraph(1); CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), getProperty(getRun(xPara, 1), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("Frame"), getProperty(getRun(xPara, 2), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"), getProperty(getRun(xPara, 3), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("Text"), getProperty(getRun(xPara, 4), "TextPortionType")); // Insert content to the comment, and select the image again. SfxStringItem aItem(FN_INSERT_STRING, "x"); pView->GetViewFrame()->GetDispatcher()->ExecuteList(FN_INSERT_STRING, SfxCallMode::SYNCHRON, { &aItem }); pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); #if !defined(MACOSX) // Calc the left edge of the as-char frame. SwRootFrame* pLayout = pWrtShell->GetLayout(); SwFrame* pPage = pLayout->GetLower(); SwFrame* pBody = pPage->GetLower(); SwFrame* pTextFrame = pBody->GetLower(); CPPUNIT_ASSERT(pTextFrame->GetDrawObjs()); const SwSortedObjs& rAnchored = *pTextFrame->GetDrawObjs(); CPPUNIT_ASSERT_GREATER(static_cast(0), rAnchored.size()); SwAnchoredObject* pObject = rAnchored[0]; long nFrameLeft = pObject->GetObjRect().Left(); long nFrameTop = pObject->GetObjRect().Top(); // Make sure that the anchor points to the bottom left corner of the image. // Without the accompanying fix in place, this test would have failed with: // - Expected less or equal than: 1418 // - Actual: 2442 // The anchor pointed to the bottom right corner, so as-char and at-char was inconsistent. Scheduler::ProcessEventsToIdle(); SwPostItMgr* pPostItMgr = pView->GetPostItMgr(); for (const auto& pItem : *pPostItMgr) { const SwRect& rAnchor = pItem->pPostIt->GetAnchorRect(); CPPUNIT_ASSERT_EQUAL(nFrameLeft, rAnchor.Left()); } // Test the comment anchor we expose via the LOK API. // Without the accompanying fix in place, this test would have failed with: // - Expected: 1418, 1418, 0, 0 // - Actual : 1418, 1418, 1024, 1024 // I.e. the anchor position had a non-empty size, which meant different rendering via tiled // rendering and on the desktop. SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); OUString aPostits = pTextDoc->getPostIts(); std::stringstream aStream(aPostits.toUtf8().getStr()); boost::property_tree::ptree aTree; boost::property_tree::read_json(aStream, aTree); for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("comments")) { const boost::property_tree::ptree& rComment = rValue.second; OString aAnchorPos(rComment.get("anchorPos").c_str()); OString aExpected = OString::number(nFrameLeft) + ", " + OString::number(nFrameTop) + ", 0, 0"; CPPUNIT_ASSERT_EQUAL(aExpected, aAnchorPos); } #endif // Now delete the image. pView->GetViewFrame()->GetDispatcher()->Execute(SID_DELETE, SfxCallMode::SYNCHRON); // Without the accompanying fix in place, this test would have failed with 'Expected: 0; Actual: // 1', i.e. the comment of the image was not deleted when the image was deleted. CPPUNIT_ASSERT_EQUAL(static_cast(0), pDoc->getIDocumentMarkAccess()->getAnnotationMarksCount()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testImageCommentAtChar) { // Load a document with an at-char image in it. SwDoc* pDoc = createDoc("image-comment-at-char.odt"); SwView* pView = pDoc->GetDocShell()->GetView(); // Select the image. pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Insert a comment while the image is selected. pView->GetViewFrame()->GetDispatcher()->Execute(FN_POSTIT, SfxCallMode::SYNCHRON); // Verify that the comment is around the image. // Without the accompanying fix in place, this test would have failed, as the comment was // anchored at the end of the paragraph, it was not around the image. uno::Reference xPara = getParagraph(1); CPPUNIT_ASSERT_EQUAL(OUString("Text"), getProperty(getRun(xPara, 1), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("Annotation"), getProperty(getRun(xPara, 2), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("Frame"), getProperty(getRun(xPara, 3), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"), getProperty(getRun(xPara, 4), "TextPortionType")); CPPUNIT_ASSERT_EQUAL(OUString("Text"), getProperty(getRun(xPara, 5), "TextPortionType")); // Insert content to the comment, and select the image again. SfxStringItem aItem(FN_INSERT_STRING, "x"); pView->GetViewFrame()->GetDispatcher()->ExecuteList(FN_INSERT_STRING, SfxCallMode::SYNCHRON, { &aItem }); pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Now delete the image. pView->GetViewFrame()->GetDispatcher()->Execute(SID_DELETE, SfxCallMode::SYNCHRON); // Without the accompanying fix in place, this test would have failed with 'Expected: 0; Actual: // 1', i.e. the comment of the image was not deleted when the image was deleted. CPPUNIT_ASSERT_EQUAL(static_cast(0), pDoc->getIDocumentMarkAccess()->getAnnotationMarksCount()); // Undo the deletion and move the image down, so the anchor changes. pView->GetViewFrame()->GetDispatcher()->Execute(SID_UNDO, SfxCallMode::SYNCHRON); CPPUNIT_ASSERT_EQUAL(static_cast(1), pDoc->getIDocumentMarkAccess()->getAnnotationMarksCount()); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); Point aNewAnchor = pWrtShell->GetFlyRect().TopLeft(); aNewAnchor.Move(0, 600); pWrtShell->SetFlyPos(aNewAnchor); // Get the image anchor doc model position. SwFlyFrame* pFly = pWrtShell->GetCurrFlyFrame(false); CPPUNIT_ASSERT(pFly); SwFrameFormat& rFlyFormat = pFly->GetFrameFormat(); const SwPosition* pImageAnchor = rFlyFormat.GetAnchor().GetContentAnchor(); CPPUNIT_ASSERT(pImageAnchor); // Get the annotation mark doc model start. auto it = pDoc->getIDocumentMarkAccess()->getAnnotationMarksBegin(); CPPUNIT_ASSERT(it != pDoc->getIDocumentMarkAccess()->getAnnotationMarksEnd()); const sw::mark::IMark* pMark = *it; const SwPosition& rAnnotationMarkStart = pMark->GetMarkPos(); // Without the accompanying fix in place, this test would have failed with: // - Expected: SwPosition (node 14, offset 15) // - Actual : SwPosition (node 12, offset 3) // This means moving the image anchor did not move the comment anchor / annotation mark, so the // image and its comment got out of sync. CPPUNIT_ASSERT_EQUAL(*pImageAnchor, rAnnotationMarkStart); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf120338) { load(DATA_DIRECTORY, "tdf120338.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty(getParagraph(2), "ParaAdjust")); // right CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty(getParagraph(3), "ParaAdjust")); // right CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(4), "ParaAdjust")); // left CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getProperty(getParagraph(5), "ParaAdjust")); // right CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty(getParagraph(7), "NumberingStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("WWNum2"), getProperty(getParagraph(8), "NumberingStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"), getProperty(getParagraph(10), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"), getProperty(getParagraph(11), "ParaStyleName")); // reject tracked paragraph adjustments dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(2), "ParaAdjust")); // left CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty(getParagraph(3), "ParaAdjust")); // center CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty(getParagraph(4), "ParaAdjust")); // center CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty(getParagraph(5), "ParaAdjust")); // left // tdf#126245 revert numbering changes CPPUNIT_ASSERT_EQUAL(OUString("WWNum2"), getProperty(getParagraph(7), "NumberingStyleName")); CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty(getParagraph(8), "NumberingStyleName")); // tdf#126243 revert paragraph styles CPPUNIT_ASSERT_EQUAL(OUString("Standard"), getProperty(getParagraph(10), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 3"), getProperty(getParagraph(11), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf120338_multiple_paragraph_join) { load(DATA_DIRECTORY, "redline-para-join.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(3), "ParaStyleName")); // reject tracked paragraph styles dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {}); CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), getProperty(getParagraph(1), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"), getProperty(getParagraph(2), "ParaStyleName")); CPPUNIT_ASSERT_EQUAL(OUString("Heading 3"), getProperty(getParagraph(3), "ParaStyleName")); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testShapePageMove) { // Load a document with 2 pages, shape on the first page. SwDoc* pDoc = createDoc("shape-page-move.odt"); SwView* pView = pDoc->GetDocShell()->GetView(); // Make sure that the 2nd page is below the 1st one. pView->SetViewLayout(/*nColumns=*/1, /*bBookMode=*/false); calcLayout(); // Select the shape. pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Make sure SwTextShell is replaced with SwDrawShell right now, not after 120 ms, as set in the // SwView ctor. pView->StopShellTimer(); // Move the shape down to the 2nd page. SfxInt32Item aXItem(SID_ATTR_TRANSFORM_POS_X, 4000); SfxInt32Item aYItem(SID_ATTR_TRANSFORM_POS_Y, 12000); pView->GetViewFrame()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM, SfxCallMode::SYNCHRON, { &aXItem, &aYItem }); // Check if the shape anchor was moved to the 2nd page as well. SwFrameFormats* pShapeFormats = pDoc->GetSpzFrameFormats(); CPPUNIT_ASSERT(!pShapeFormats->empty()); auto it = pShapeFormats->begin(); SwFrameFormat* pShapeFormat = *it; const SwPosition* pAnchor = pShapeFormat->GetAnchor().GetContentAnchor(); CPPUNIT_ASSERT(pAnchor); // Find out the node index of the 1st para on the 2nd page. SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); SwFrame* pFirstPage = pLayout->Lower(); SwFrame* pSecondPage = pFirstPage->GetNext(); CPPUNIT_ASSERT(pSecondPage->IsLayoutFrame()); SwFrame* pBodyFrame = static_cast(pSecondPage)->GetLower(); CPPUNIT_ASSERT(pBodyFrame->IsLayoutFrame()); SwFrame* pTextFrame = static_cast(pBodyFrame)->GetLower(); CPPUNIT_ASSERT(pTextFrame->IsTextFrame()); sal_uLong nNodeIndex = static_cast(pTextFrame)->GetTextNodeFirst()->GetIndex(); // Without the accompanying fix in place, this test would have failed with "Expected: 13; // Actual: 12", i.e. the shape was anchored to the last paragraph of the 1st page, not to a // paragraph on the 2nd page. CPPUNIT_ASSERT_EQUAL(nNodeIndex, pAnchor->nNode.GetIndex()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDateFormFieldInsertion) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a date form field dispatchCommand(mxComponent, ".uno:DatePickerFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDATE), pFieldmark->GetFieldname()); // The date form field has the placeholder text in it uno::Reference xPara = getParagraph(1); sal_Unicode vEnSpaces[5] = { 8194, 8194, 8194, 8194, 8194 }; CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), xPara->getString()); // Undo insertion dispatchCommand(mxComponent, ".uno:Undo", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Redo insertion dispatchCommand(mxComponent, ".uno:Redo", {}); aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDATE), pFieldmark->GetFieldname()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDateFormFieldContentOperations) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a date form field dispatchCommand(mxComponent, ".uno:DatePickerFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IDateFieldmark* pFieldmark = dynamic_cast<::sw::mark::IDateFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDATE), pFieldmark->GetFieldname()); // Check the default content added by insertion uno::Reference xPara = getParagraph(1); sal_Unicode vEnSpaces[5] = { 8194, 8194, 8194, 8194, 8194 }; CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), pFieldmark->GetContent()); // Set content to empty string pFieldmark->ReplaceContent(""); CPPUNIT_ASSERT_EQUAL(OUString(""), pFieldmark->GetContent()); // Replace empty string with a valid content pFieldmark->ReplaceContent("2019-10-23"); CPPUNIT_ASSERT_EQUAL(OUString("2019-10-23"), pFieldmark->GetContent()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDateFormFieldCurrentDateHandling) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a date form field dispatchCommand(mxComponent, ".uno:DatePickerFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IDateFieldmark* pFieldmark = dynamic_cast<::sw::mark::IDateFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDATE), pFieldmark->GetFieldname()); // The default content is not a valid date uno::Reference xPara = getParagraph(1); sal_Unicode vEnSpaces[5] = { 8194, 8194, 8194, 8194, 8194 }; CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), pFieldmark->GetContent()); std::pair aResult = pFieldmark->GetCurrentDate(); CPPUNIT_ASSERT(!aResult.first); // Check empty string pFieldmark->ReplaceContent(""); aResult = pFieldmark->GetCurrentDate(); CPPUNIT_ASSERT(!aResult.first); // Check valid date // Set date format first sw::mark::IFieldmark::parameter_map_t* pParameters = pFieldmark->GetParameters(); (*pParameters)[ODF_FORMDATE_DATEFORMAT] <<= OUString("YYYY/MM/DD"); (*pParameters)[ODF_FORMDATE_DATEFORMAT_LANGUAGE] <<= OUString("en-US"); // Set date value and check whether the content is formatted correctly pFieldmark->SetCurrentDate(48000.0); aResult = pFieldmark->GetCurrentDate(); CPPUNIT_ASSERT(aResult.first); CPPUNIT_ASSERT_EQUAL(48000.0, aResult.second); CPPUNIT_ASSERT_EQUAL(OUString("2031/06/01"), pFieldmark->GetContent()); // Current date param contains date in a "standard format" OUString sCurrentDate; auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE); if (pResult != pParameters->end()) { pResult->second >>= sCurrentDate; } CPPUNIT_ASSERT_EQUAL(OUString("2031-06-01"), sCurrentDate); } #if !defined(_WIN32) CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testDateFormFieldCurrentDateInvalidation) { SwDoc* pDoc = createDoc(); CPPUNIT_ASSERT(pDoc); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); CPPUNIT_ASSERT(pMarkAccess); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pMarkAccess->getAllMarksCount()); // Insert a date form field dispatchCommand(mxComponent, ".uno:DatePickerFormField", {}); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); // Check whether the fieldmark is created auto aIter = pMarkAccess->getAllMarksBegin(); CPPUNIT_ASSERT(aIter != pMarkAccess->getAllMarksEnd()); ::sw::mark::IDateFieldmark* pFieldmark = dynamic_cast<::sw::mark::IDateFieldmark*>(*aIter); CPPUNIT_ASSERT(pFieldmark); CPPUNIT_ASSERT_EQUAL(OUString(ODF_FORMDATE), pFieldmark->GetFieldname()); // Set a date first sw::mark::IFieldmark::parameter_map_t* pParameters = pFieldmark->GetParameters(); pFieldmark->SetCurrentDate(48000.0); std::pair aResult = pFieldmark->GetCurrentDate(); CPPUNIT_ASSERT(aResult.first); CPPUNIT_ASSERT_EQUAL(48000.0, aResult.second); // Do the layouting to trigger invalidation // Since we have the current date consistent with the field content // This invalidation won't change anything calcLayout(); Scheduler::ProcessEventsToIdle(); // Current date param contains date in a "standard format" OUString sCurrentDate; auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE); if (pResult != pParameters->end()) { pResult->second >>= sCurrentDate; } // We have the current date parameter set CPPUNIT_ASSERT_EQUAL(OUString("2031-06-01"), sCurrentDate); // Now change the content of the field pFieldmark->ReplaceContent("[select date]"); // Do the layouting to trigger invalidation calcLayout(); Scheduler::ProcessEventsToIdle(); sCurrentDate.clear(); pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE); if (pResult != pParameters->end()) { pResult->second >>= sCurrentDate; } CPPUNIT_ASSERT_EQUAL(OUString(""), sCurrentDate); } #endif CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testOleSaveWhileEdit) { // Enable LOK mode, otherwise OCommonEmbeddedObject::SwitchStateTo_Impl() will throw when it // finds out that the test runs headless. comphelper::LibreOfficeKit::setActive(); // Load a document with a Draw doc in it. SwDoc* pDoc = createDoc("ole-save-while-edit.odt"); SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); pWrtShell->GotoObj(/*bNext=*/true, GotoObjFlags::Any); // Select the frame and switch to the frame shell. SwView* pView = pDoc->GetDocShell()->GetView(); pView->StopShellTimer(); // Start editing the OLE object. pWrtShell->LaunchOLEObj(); // Save the document without existing the OLE edit. uno::Reference xStorable(mxComponent, uno::UNO_QUERY); xStorable->storeToURL(maTempFile.GetURL(), {}); uno::Reference xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL()); // Without the accompanying fix in place, this test would have failed: the OLE object lost its // replacement on save if the edit was active while saving. CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Object 1")); // Dispose the document while LOK is still active to avoid leaks. mxComponent->dispose(); mxComponent.clear(); comphelper::LibreOfficeKit::setActive(false); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf105330) { load(DATA_DIRECTORY, "tdf105330.odt"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell(); pWrtShell->Down(/*bSelect=*/false); SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); SwView* pView = pDoc->GetDocShell()->GetView(); SfxUInt16Item aRows(SID_ATTR_TABLE_ROW, 1); SfxUInt16Item aColumns(SID_ATTR_TABLE_COLUMN, 1); pView->GetViewFrame()->GetDispatcher()->ExecuteList(FN_INSERT_TABLE, SfxCallMode::SYNCHRON, { &aRows, &aColumns }); sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); // Without the accompanying fix in place, height was only 1 twips (practically invisible). // Require at least 12pt height (font size under the cursor), in twips. CPPUNIT_ASSERT_GREATEREQUAL( static_cast(240), pWrtShell->GetVisibleCursor()->GetTextCursor().GetSize().getHeight()); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf118311) { load(DATA_DIRECTORY, "tdf118311.fodt"); SwXTextDocument* pDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pDoc); SwDocShell* pDocShell = pDoc->GetDocShell(); CPPUNIT_ASSERT(pDocShell); SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); // Jump to the first cell, selecting its content uno::Sequence aSearch(comphelper::InitPropertySequence({ { "SearchItem.SearchString", uno::makeAny(OUString("a")) }, { "SearchItem.Backward", uno::makeAny(false) }, })); dispatchCommand(mxComponent, ".uno:ExecuteSearch", aSearch); // .uno:Cut doesn't remove the table, only the selected content of the first cell dispatchCommand(mxComponent, ".uno:Cut", {}); xmlDocPtr pXmlDoc = parseLayoutDump(); assertXPath(pXmlDoc, "//page[1]//body/tab"); // .uno:SelectAll selects the whole table, and UNO command Cut cuts it dispatchCommand(mxComponent, ".uno:SelectAll", {}); dispatchCommand(mxComponent, ".uno:Cut", {}); discardDumpedLayout(); pXmlDoc = parseLayoutDump(); assertXPath(pXmlDoc, "//page[1]//body/tab", 0); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf128335) { // Load the bugdoc, which has 3 textboxes. SwDoc* pDoc = createDoc("tdf128335.odt"); // Select the 3rd textbox. SwView* pView = pDoc->GetDocShell()->GetView(); pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Make sure SwTextShell is replaced with SwDrawShell right now, not after 120 ms, as set in the // SwView ctor. pView->StopShellTimer(); SwXTextDocument* pXTextDocument = dynamic_cast(mxComponent.get()); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB); Scheduler::ProcessEventsToIdle(); // Cut it. pView->GetViewFrame()->GetDispatcher()->Execute(SID_CUT, SfxCallMode::SYNCHRON); // Paste it: this makes the 3rd textbox anchored in the 2nd one. pView->GetViewFrame()->GetDispatcher()->Execute(SID_PASTE, SfxCallMode::SYNCHRON); // Select all shapes. uno::Reference xSelectionSupplier( pXTextDocument->getCurrentController(), uno::UNO_QUERY); xSelectionSupplier->select(pXTextDocument->getDrawPages()->getByIndex(0)); // Cut them. // Without the accompanying fix in place, this test would have crashed as the textboxes were // deleted in an incorrect order. pView->GetViewFrame()->GetDispatcher()->Execute(SID_CUT, SfxCallMode::SYNCHRON); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf128603) { // Load the bugdoc, which has 3 textboxes. SwDoc* pDoc = createDoc("tdf128603.odt"); // Select the 3rd textbox. SwView* pView = pDoc->GetDocShell()->GetView(); pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON); // Make sure SwTextShell is replaced with SwDrawShell right now, not after 120 ms, as set in the // SwView ctor. pView->StopShellTimer(); SwXTextDocument* pXTextDocument = dynamic_cast(mxComponent.get()); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_TAB); Scheduler::ProcessEventsToIdle(); // Cut it. pView->GetViewFrame()->GetDispatcher()->Execute(SID_CUT, SfxCallMode::SYNCHRON); // Paste it: this makes the 3rd textbox anchored in the 2nd one. pView->GetViewFrame()->GetDispatcher()->Execute(SID_PASTE, SfxCallMode::SYNCHRON); // Undo all of this. sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); rUndoManager.Undo(); rUndoManager.Undo(); // Make sure the content indexes still match. const SwFrameFormats& rSpzFrameFormats = *pDoc->GetSpzFrameFormats(); CPPUNIT_ASSERT_EQUAL(static_cast(6), rSpzFrameFormats.size()); const SwNodeIndex* pIndex4 = rSpzFrameFormats[4]->GetContent().GetContentIdx(); CPPUNIT_ASSERT(pIndex4); const SwNodeIndex* pIndex5 = rSpzFrameFormats[5]->GetContent().GetContentIdx(); CPPUNIT_ASSERT(pIndex5); // Without the accompanying fix in place, this test would have failed with: // - Expected: 11 // - Actual : 14 // i.e. the shape content index and the frame content index did not match after undo, even if // their "other text box format" pointers pointed to each other. CPPUNIT_ASSERT_EQUAL(pIndex4->GetIndex(), pIndex5->GetIndex()); } // only care that it doesn't assert/crash CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testOfz18563) { OUString sURL(m_directories.getURLFromSrc("/sw/qa/extras/uiwriter/data2/ofz18563.docx")); SvFileStream aFileStream(sURL, StreamMode::READ); TestImportDOCX(aFileStream); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf90069) { SwDoc* pDoc = createDoc("tdf90069.docx"); SwXTextDocument* pTextDoc = dynamic_cast(mxComponent.get()); CPPUNIT_ASSERT(pTextDoc); SwDocShell* pDocShell = pTextDoc->GetDocShell(); CPPUNIT_ASSERT(pDocShell); SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); CPPUNIT_ASSERT(pWrtShell); sal_uLong nIndex = pWrtShell->GetCursor()->GetNode().GetIndex(); dispatchCommand(mxComponent, ".uno:InsertRowsAfter", {}); pWrtShell->Down(false); pWrtShell->Insert("foo"); SwTextNode* pTextNodeA1 = static_cast(pDoc->GetNodes()[nIndex]); CPPUNIT_ASSERT(pTextNodeA1->GetText().startsWith("Insert")); nIndex = pWrtShell->GetCursor()->GetNode().GetIndex(); SwTextNode* pTextNodeA2 = static_cast(pDoc->GetNodes()[nIndex]); CPPUNIT_ASSERT_EQUAL(OUString("foo"), pTextNodeA2->GetText()); CPPUNIT_ASSERT_EQUAL(true, pTextNodeA2->GetSwAttrSet().HasItem(RES_CHRATR_FONT)); OUString sFontName = pTextNodeA2->GetSwAttrSet().GetItem(RES_CHRATR_FONT)->GetFamilyName(); CPPUNIT_ASSERT_EQUAL(OUString("Lohit Devanagari"), sFontName); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf129655) { createDoc("tdf129655-vtextbox.odt"); xmlDocPtr pXmlDoc = parseLayoutDump(); assertXPath(pXmlDoc, "//fly/txt[@WritingMode='Vertical']", 1); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */