summaryrefslogtreecommitdiffstats
path: root/sw/qa/extras/uiwriter/uiwriter2.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sw/qa/extras/uiwriter/uiwriter2.cxx3023
1 files changed, 3023 insertions, 0 deletions
diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx
new file mode 100644
index 000000000..1354244a8
--- /dev/null
+++ b/sw/qa/extras/uiwriter/uiwriter2.cxx
@@ -0,0 +1,3023 @@
+/* -*- 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 <swmodeltestbase.hxx>
+
+#include <sstream>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/style/LineSpacing.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/scheduler.hxx>
+#include <vcl/settings.hxx>
+#include <ndtxt.hxx>
+#include <swdtflvr.hxx>
+#include <wrtsh.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <flyfrm.hxx>
+#include <fmtanchr.hxx>
+#include <UndoManager.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+#include <itabenum.hxx>
+#include <fmtfsize.hxx>
+#include <xmloff/odffields.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/stritem.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <swacorr.hxx>
+#include <txtfrm.hxx>
+#include <redline.hxx>
+#include <view.hxx>
+#include <cmdid.h>
+#include <AnnotationWin.hxx>
+#include <PostItMgr.hxx>
+#include <postithelper.hxx>
+#include <fmtcntnt.hxx>
+#include <frameformats.hxx>
+#include <shellio.hxx>
+#include <editeng/fontitem.hxx>
+
+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<Resetter> preTest(const char* filename) override
+ {
+ m_aSavedSettings = Application::GetSettings();
+ if (OString(filename).indexOf("LocaleArabic") != -1)
+ {
+ std::unique_ptr<Resetter> 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<SwXTextDocument*>(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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(14729933), getProperty<sal_Int32>(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<sal_Int32>(getParagraph(2), "FillColor"));
+ // the paragraph style should not be reset
+ CPPUNIT_ASSERT_EQUAL(OUString("00Background"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("00Background"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+
+ // Save it and load it back.
+ reload("writer8", "tdf47471_paraStyleBackgroundRT.odt");
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(14605542), getProperty<sal_Int32>(getParagraph(2), "FillColor"));
+ // on round-trip, the paragraph style name was lost
+ CPPUNIT_ASSERT_EQUAL(OUString("00Background"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("00Background"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdfChangeNumberingListAutoFormat)
+{
+ createDoc("tdf117923.docx");
+ // Ensure that all text portions are calculated before testing.
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwViewShell* pViewShell
+ = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ CPPUNIT_ASSERT(pViewShell);
+ pViewShell->Reformat();
+
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+
+ // Check that we actually test the line we need
+ assertXPathContent(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]", "GHI GHI GHI GHI");
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nType",
+ "PortionType::Number");
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "rText", "2.");
+ // The numbering height was 960 in DOC format.
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "220");
+
+ // tdf#127606: now it's possible to change formatting of numbering
+ // increase font size (220 -> 260)
+ dispatchCommand(mxComponent, ".uno:SelectAll", {});
+ dispatchCommand(mxComponent, ".uno:Grow", {});
+ pViewShell->Reformat();
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "260");
+
+ // save it to DOCX
+ reload("Office Open XML Text", "tdf117923.docx");
+ pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ pViewShell
+ = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ pViewShell->Reformat();
+ discardDumpedLayout();
+ pXmlDoc = parseLayoutDump();
+ // this was 220
+ assertXPath(pXmlDoc, "/root/page/body/tab/row/cell/txt[3]/Special", "nHeight", "260");
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf101534)
+{
+ // Copy the first paragraph of the document.
+ load(DATA_DIRECTORY, "tdf101534.fodt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->EndPara(/*bSelect=*/true);
+ rtl::Reference<SwTransferable> 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<RES_LR_SPACE, RES_LR_SPACE>{});
+ 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<SwXTextDocument*>(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<SwXTextDocument*>(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, testRedlineSplitContentNode)
+{
+ load(DATA_DIRECTORY, "try2.fodt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwWrtShell* const pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+
+ SwViewOption aViewOptions(*pWrtShell->GetViewOptions());
+ // these are required so that IsBlank() is true
+ aViewOptions.SetBlank(true);
+ aViewOptions.SetViewMetaChars(true);
+ pWrtShell->ApplyViewOptions(aViewOptions);
+
+ // enable redlining
+ dispatchCommand(mxComponent, ".uno:TrackChanges", {});
+ // hide
+ dispatchCommand(mxComponent, ".uno:ShowTrackedChanges", {});
+
+ SwDocShell* const pDocShell = pTextDoc->GetDocShell();
+ SwDoc* const pDoc = pDocShell->GetDoc();
+ CPPUNIT_ASSERT_MESSAGE("redlining should be on",
+ pDoc->getIDocumentRedlineAccess().IsRedlineOn());
+ CPPUNIT_ASSERT_MESSAGE(
+ "redlines should be visible",
+ IDocumentRedlineAccess::IsShowChanges(pDoc->getIDocumentRedlineAccess().GetRedlineFlags()));
+
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+
+ pWrtShell->CalcLayout();
+ pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 18, /*bBasicCall=*/false);
+ pWrtShell->SplitNode(true);
+ rUndoManager.Undo();
+ // crashed
+ pWrtShell->SplitNode(true);
+ rUndoManager.Undo();
+ rUndoManager.Redo();
+ rUndoManager.Undo();
+ rUndoManager.Redo();
+ rUndoManager.Undo();
+ pWrtShell->SplitNode(true);
+ rUndoManager.Undo();
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf137245)
+{
+ SwDoc* const pDoc(createDoc());
+ SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwAutoCorrect corr(*SvxAutoCorrCfg::Get().GetAutoCorrect());
+ corr.GetSwFlags().bSetBorder = true;
+ // sigh, it's a global... err i mean Singleton design pattern *eyeroll*
+ SvxSwAutoFormatFlags flags(*SwEditShell::GetAutoFormatFlags());
+ comphelper::ScopeGuard const g([=]() { SwEditShell::SetAutoFormatFlags(&flags); });
+ flags.bSetBorder = true;
+ SwEditShell::SetAutoFormatFlags(&flags);
+
+ {
+ SwFormatAnchor anchor(RndStdIds::FLY_AT_PARA);
+ anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
+ SfxItemSet flySet(pDoc->GetAttrPool(),
+ svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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);
+ }
+ {
+ SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
+ anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
+ SfxItemSet flySet(pDoc->GetAttrPool(),
+ svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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);
+ }
+ // move cursor back to body
+ pWrtShell->SttEndDoc(false);
+ // keep first paragraph empty so that its flys may be deleted too
+ //pWrtShell->Insert("abc");
+ pWrtShell->SplitNode(false);
+
+ {
+ SwFormatAnchor anchor(RndStdIds::FLY_AT_PARA);
+ anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
+ SfxItemSet flySet(pDoc->GetAttrPool(),
+ svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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);
+ }
+ {
+ SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
+ anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
+ SfxItemSet flySet(pDoc->GetAttrPool(),
+ svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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);
+ }
+
+ const SwFrameFormats& rFormats = *pDoc->GetSpzFrameFormats();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rFormats.size());
+
+ // move cursor back to body
+ pWrtShell->SttEndDoc(false);
+ pWrtShell->Insert("---");
+ pWrtShell->SplitNode(true);
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rFormats.size());
+
+ // check that the AutoFormat did something
+ pWrtShell->SttEndDoc(true);
+ SfxItemSet set{ pDoc->GetAttrPool(), svl::Items<RES_BOX, RES_BOX>{} };
+ pWrtShell->GetCurParAttr(set);
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::SET, set.GetItemState(RES_BOX, false));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf132236)
+{
+ load(DATA_DIRECTORY, "tdf132236.odt");
+
+ SwXTextDocument* const pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // select everything and delete
+ SwWrtShell* const pWrtShell(pTextDoc->GetDocShell()->GetWrtShell());
+ pWrtShell->Down(true);
+ pWrtShell->Down(true);
+ pWrtShell->Down(true);
+ pWrtShell->Delete();
+ SwDoc* const pDoc(pWrtShell->GetDoc());
+ sw::UndoManager& rUndoManager(pDoc->GetUndoManager());
+ rUndoManager.Undo();
+
+ // check that the text frames exist inside their sections
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "/root/page[1]/body/section[1]/txt", 1);
+ assertXPath(pXmlDoc, "/root/page[1]/body/section[2]/txt", 2);
+ assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf131912)
+{
+ SwDoc* const pDoc = createDoc();
+ SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+ sw::UndoManager& rUndoManager = pDoc->GetUndoManager();
+
+ sw::UnoCursorPointer pCursor(
+ pDoc->CreateUnoCursor(SwPosition(SwNodeIndex(pDoc->GetNodes().GetEndOfContent(), -1))));
+
+ pDoc->getIDocumentContentOperations().InsertString(*pCursor, "foo");
+
+ {
+ SfxItemSet flySet(pDoc->GetAttrPool(),
+ svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR);
+ pWrtShell->StartOfSection(false);
+ pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false);
+ anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint());
+ 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);
+ }
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+
+ pCursor->SetMark();
+ pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0);
+ pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3);
+
+ // replace with more text
+ pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "blahblah", false);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("blahblah"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Redo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("blahblah"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Undo();
+
+ pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0);
+ pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3);
+
+ // replace with less text
+ pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "x", false);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("x"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Redo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("x"), pCursor->GetNode().GetTextNode()->GetText());
+
+ rUndoManager.Undo();
+
+ pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0);
+ pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3);
+
+ // regex replace with paragraph breaks
+ pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "xyz\\n\\nquux\\n", true);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText());
+ pWrtShell->StartOfSection(false);
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Redo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ // regex replace with paragraph join
+ pWrtShell->StartOfSection(false);
+ pWrtShell->Down(true);
+ pDoc->getIDocumentContentOperations().ReplaceRange(*pWrtShell->GetCursor(), "bar", true);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("bar"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("bar\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Redo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("bar"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("bar\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ pWrtShell->StartOfSection(false);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz"),
+ pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText());
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText());
+
+ rUndoManager.Undo();
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM));
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText());
+ pWrtShell->StartOfSection(false);
+ pWrtShell->EndOfSection(true);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), pWrtShell->GetCursor()->GetText());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf54819)
+{
+ load(DATA_DIRECTORY, "tdf54819.fodt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // remaining paragraph keeps its original style
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf54819_keep_numbering_with_Undo)
+{
+ load(DATA_DIRECTORY, "tdf54819b.odt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // heading
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ OUString sNumName = getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // solved problem: changing paragraph style after deletion
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+
+ sNumName = getProperty<OUString>(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<OUString>(getParagraph(2), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<OUString>(getParagraph(2), "ParaStyleName"));
+ // CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ // getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // heading
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ OUString sNumName = getProperty<OUString>(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<OUString>(getParagraph(4), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // solved problem: changing paragraph style after deletion
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+
+ // solved problem: apply numbering
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<OUString>(getParagraph(4), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // heading
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ OUString sNumName = getProperty<OUString>(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<OUString>(getParagraph(4), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // solved problem: changing paragraph style after deletion
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+
+ // solved problem: apply numbering
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // reject deletion
+ IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess());
+ rIDRA.AcceptAllRedline(false);
+
+ // heading
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Outline"),
+ getProperty<OUString>(getParagraph(2), "NumberingStyleName"));
+
+ // next paragraph: bulleted list item
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<OUString>(getParagraph(4), "ParaStyleName"));
+ sNumName = getProperty<OUString>(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<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{});
+ 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<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<table::XTableRows> xTableRows = xTextTable->getRows();
+
+ double origWidth = getProperty<double>(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<double>(xTextTable, "Width"), nToleranceW);
+
+ dispatchCommand(mxComponent, ".uno:SetMinimalColumnWidth", {});
+ CPPUNIT_ASSERT_MESSAGE("Table Width: minimized",
+ (origWidth - nToleranceW) > getProperty<double>(xTextTable, "Width"));
+
+ double origRowHeight = getProperty<double>(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<double>(xTableRows->getByIndex(2), "Height");
+ CPPUNIT_ASSERT_MESSAGE("Row Height: optimized",
+ (origRowHeight - nToleranceH) > optimalRowHeight);
+
+ dispatchCommand(mxComponent, ".uno:SetMinimalRowHeight", {});
+ double minimalRowHeight = getProperty<double>(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<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextTable> xTextTable(xTables->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<table::XTableRows> xTableRows = xTextTable->getRows();
+
+ auto aSeq = getProperty<uno::Sequence<text::TableColumnSeparator>>(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<uno::Sequence<text::TableColumnSeparator>>(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<SwXTextDocument*>(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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // second paragraph changes its style in "Show changes" mode
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf119019)
+{
+ // check handling of overlapping redlines
+ load(DATA_DIRECTORY, "tdf119019.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(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<SwTransferable> 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<SwXTextDocument*>(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<SwTransferable> 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<SwXTextDocument*>(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<OUString>(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<beans::PropertyValue> 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<OUString>(getParagraph(3), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ // first paragraph gets the same heading style
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf76817)
+{
+ load(DATA_DIRECTORY, "num-parent-style.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("1.1"),
+ getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // set Heading 2 style of paragraph 2 to Heading 1
+
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Down(/*bSelect=*/false);
+
+ uno::Sequence<beans::PropertyValue> 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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ // This was "1 Heading" instead of "2 Heading"
+ CPPUNIT_ASSERT_EQUAL(OUString("2"), getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ // This was "2.1 Heading"
+ CPPUNIT_ASSERT_EQUAL(OUString("3.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // set Heading 1 style of paragraph 3 to Heading 2
+
+ pWrtShell->Down(/*bSelect=*/false);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(3), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("3"), getProperty<OUString>(getParagraph(3), "ListLabelString"));
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues2 = comphelper::InitPropertySequence({
+ { "Style", uno::makeAny(OUString("Heading 2")) },
+ { "FamilyName", uno::makeAny(OUString("ParagraphStyles")) },
+ });
+ dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues2);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(3), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(3), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.2"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf76817_round_trip)
+{
+ load(DATA_DIRECTORY, "tdf76817.fodt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // save it to DOCX
+ reload("Office Open XML Text", "tdf76817.docx");
+ pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+ SwViewShell* pViewShell
+ = pTextDoc->GetDocShell()->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ pViewShell->Reformat();
+
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("1.1"),
+ getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // set Heading 2 style of paragraph 2 to Heading 1
+
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Down(/*bSelect=*/false);
+
+ uno::Sequence<beans::PropertyValue> 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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ // This was "1 Heading" instead of "2 Heading"
+ CPPUNIT_ASSERT_EQUAL(OUString("2"), getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ // This was "2.1 Heading"
+ CPPUNIT_ASSERT_EQUAL(OUString("3.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // set Heading 1 style of paragraph 3 to Heading 2
+
+ pWrtShell->Down(/*bSelect=*/false);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(3), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("3"), getProperty<OUString>(getParagraph(3), "ListLabelString"));
+
+ uno::Sequence<beans::PropertyValue> aPropertyValues2 = comphelper::InitPropertySequence({
+ { "Style", uno::makeAny(OUString("Heading 2")) },
+ { "FamilyName", uno::makeAny(OUString("ParagraphStyles")) },
+ });
+ dispatchCommand(mxComponent, ".uno:StyleApply", aPropertyValues2);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(3), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(3), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2.2"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf76817_custom_outline)
+{
+ load(DATA_DIRECTORY, "tdf76817.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(1), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("1"), getProperty<OUString>(getParagraph(1), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ // This wasn't numbered
+ CPPUNIT_ASSERT_EQUAL(OUString("1.1"),
+ getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ // This wasn't numbered
+ CPPUNIT_ASSERT_EQUAL(OUString("2.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+
+ // set Heading 2 style of paragraph 2 to Heading 1
+
+ SwWrtShell* pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->Down(/*bSelect=*/false);
+
+ uno::Sequence<beans::PropertyValue> 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<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1),
+ getProperty<sal_Int32>(getParagraph(2), "OutlineLevel"));
+ CPPUNIT_ASSERT_EQUAL(OUString("2"), getProperty<OUString>(getParagraph(2), "ListLabelString"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(4), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2),
+ getProperty<sal_Int32>(getParagraph(4), "OutlineLevel"));
+ // This wasn't numbered
+ CPPUNIT_ASSERT_EQUAL(OUString("3.1"),
+ getProperty<OUString>(getParagraph(4), "ListLabelString"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf123102)
+{
+ createDoc("tdf123102.odt");
+ // insert a new row after a vertically merged cell
+ dispatchCommand(mxComponent, ".uno:InsertRowsAfter", {});
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // This was "3." - caused by the hidden numbered paragraph of the new merged cell
+ assertXPath(pXmlDoc, "/root/page/body/tab/row[6]/cell[1]/txt/Special", "rText", "2.");
+}
+
+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<SwXTextDocument*>(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<size_t>(1), pAnchored->size());
+ SwAnchoredObject* pAnchoredObj = (*pAnchored)[0];
+
+ SwFlyFrame* pFlyFrame = dynamic_cast<SwFlyFrame*>(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<OUString> 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<SwXTextDocument*>(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<size_t>(1),
+ pAnchored->size());
+ SwAnchoredObject* pAnchoredObj = (*pAnchored)[0];
+
+ // The unfloat button is not visible until it gets selected
+ SwFlyFrame* pFlyFrame = dynamic_cast<SwFlyFrame*>(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<SwXTextDocument*>(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<size_t>(1), pAnchored->size());
+ SwAnchoredObject* pAnchoredObj = (*pAnchored)[0];
+
+ SwFlyFrame* pFlyFrame = dynamic_cast<SwFlyFrame*>(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<OUString> 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<SwXTextDocument*>(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<size_t>(1),
+ pAnchored->size());
+ SwAnchoredObject* pAnchoredObj = (*pAnchored)[0];
+ pFlyFrame = dynamic_cast<SwFlyFrame*>(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<Resetter> 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<beans::XPropertySet> xPageStyle(
+ getStyles("ParagraphStyles")->getByName("Default Paragraph 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<sal_Int32>(xPageStyle, "WritingMode"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122893)
+{
+ load(DATA_DIRECTORY, "tdf105413.fodt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(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<sal_Int32>(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<sal_Int32>(getParagraph(3), "ParaAdjust")); // center-aligned
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(200),
+ getProperty<style::LineSpacing>(getParagraph(3), "ParaLineSpacing")
+ .Height); // double line spacing
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(getParagraph(2), "ParaAdjust")); // left-aligned
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(100),
+ getProperty<style::LineSpacing>(getParagraph(2), "ParaLineSpacing")
+ .Height); // single line spacing
+ // first paragraph is also center-aligned with double line spacing
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty<sal_Int32>(getParagraph(1), "ParaAdjust"));
+ CPPUNIT_ASSERT_EQUAL(
+ sal_Int16(200), getProperty<style::LineSpacing>(getParagraph(1), "ParaLineSpacing").Height);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122901)
+{
+ load(DATA_DIRECTORY, "tdf105413.fodt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // all paragraphs with zero borders
+ for (int i = 1; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(getParagraph(i), "ParaTopMargin"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(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<sal_Int32>(getParagraph(3), "ParaTopMargin"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(101),
+ getProperty<sal_Int32>(getParagraph(3), "ParaBottomMargin"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(getParagraph(2), "ParaTopMargin"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(getParagraph(2), "ParaBottomMargin"));
+
+ // first paragraph is also center-aligned with double line spacing
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(101), getProperty<sal_Int32>(getParagraph(1), "ParaTopMargin"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(101),
+ getProperty<sal_Int32>(getParagraph(1), "ParaBottomMargin"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf122942)
+{
+ load(DATA_DIRECTORY, "tdf122942.odt");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(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<sal_uInt16>(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<size_t>(2), rFormats.size());
+
+ reload("writer8", "tdf122942.odt");
+ pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ pWrtShell = pTextDoc->GetDocShell()->GetWrtShell();
+ pDoc = pWrtShell->GetDoc();
+ const SwFrameFormats& rFormats2 = *pDoc->GetSpzFrameFormats();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(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, testTdf132160)
+{
+ load(DATA_DIRECTORY, "tdf132160.odt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ // this would crash due to delete redline starting with ToX
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ // this would crash due to insert redline ending on table node
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+
+ dispatchCommand(mxComponent, ".uno:Redo", {});
+
+ dispatchCommand(mxComponent, ".uno:Undo", {});
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf52391)
+{
+ load(DATA_DIRECTORY, "tdf52391.fodt");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ const uno::Reference<text::XTextRange> 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<SwXTextDocument*>(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<float>(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<float>(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<beans::PropertyValue> 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<beans::PropertyValue> 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<frame::XStorable> 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<text::XTextTablesSupplier> xTablesSupplier(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xTables(xTablesSupplier->getTextTables(),
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(100),
+ getProperty<sal_Int16>(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<text::XTextRange> 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<SwTextFrame*>(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<beans::XPropertySet> xShape(getShape(1), uno::UNO_QUERY);
+
+ // change the properties
+ // 8133 -> 8000
+ xShape->setPropertyValue("VertOrientPosition", uno::makeAny(static_cast<sal_Int32>(8000)));
+ // 5964 -> 5000
+ xShape->setPropertyValue("HoriOrientPosition", uno::makeAny(static_cast<sal_Int32>(5000)));
+ // 0 (frame) -> 8 (page print area)
+ xShape->setPropertyValue("VertOrientRelation", uno::makeAny(static_cast<sal_Int16>(8)));
+ // 8 (page print area) -> 0 (frame)
+ xShape->setPropertyValue("HoriOrientRelation", uno::makeAny(static_cast<sal_Int16>(0)));
+ }
+ // save it to docx
+ reload("Office Open XML Text", "floating-table-position.docx");
+
+ uno::Reference<beans::XPropertySet> xShape(getShape(1), uno::UNO_QUERY);
+
+ // test the new values
+ sal_Int32 nValue = getProperty<sal_Int32>(xShape, "VertOrientPosition");
+ CPPUNIT_ASSERT(sal_Int32(7999) <= nValue && nValue <= sal_Int32(8001));
+ nValue = getProperty<sal_Int32>(xShape, "HoriOrientPosition");
+ CPPUNIT_ASSERT(sal_Int32(4999) <= nValue && nValue <= sal_Int32(5001));
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(8), getProperty<sal_Int16>(xShape, "VertOrientRelation"));
+ CPPUNIT_ASSERT_EQUAL(sal_Int16(0), getProperty<sal_Int16>(xShape, "HoriOrientRelation"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf118699_redline_numbering)
+{
+ load(DATA_DIRECTORY, "tdf118699.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess());
+ rIDRA.AcceptAllRedline(true);
+
+ uno::Reference<beans::XPropertySet> 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<uno::Reference<container::XIndexAccess>>(getParagraph(5), "NumberingRules")
+ .is());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125881_redline_list_level)
+{
+ load(DATA_DIRECTORY, "tdf125881.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ uno::Reference<beans::XPropertySet> 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<beans::XPropertySet> 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<sal_Int16>(getParagraph(9), "NumberingLevel"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf125916_redline_restart_numbering)
+{
+ load(DATA_DIRECTORY, "tdf125916.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentRedlineAccess& rIDRA(pDoc->getIDocumentRedlineAccess());
+ rIDRA.AcceptAllRedline(true);
+
+ // check unnecessary numbering
+ uno::Reference<beans::XPropertySet> 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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(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<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // copied paragraph style
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(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<SwXTextDocument*>(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<SwXTextDocument*>(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 "before<image>after", 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<text::XTextRange> xPara = getParagraph(1);
+ CPPUNIT_ASSERT_EQUAL(OUString("Annotation"),
+ getProperty<OUString>(getRun(xPara, 1), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Frame"),
+ getProperty<OUString>(getRun(xPara, 2), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"),
+ getProperty<OUString>(getRun(xPara, 3), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Text"),
+ getProperty<OUString>(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<size_t>(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<SwXTextDocument*>(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<std::string>("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<sal_Int32>(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<text::XTextRange> xPara = getParagraph(1);
+ CPPUNIT_ASSERT_EQUAL(OUString("Text"),
+ getProperty<OUString>(getRun(xPara, 1), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Annotation"),
+ getProperty<OUString>(getRun(xPara, 2), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Frame"),
+ getProperty<OUString>(getRun(xPara, 3), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("AnnotationEnd"),
+ getProperty<OUString>(getRun(xPara, 4), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Text"),
+ getProperty<OUString>(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<sal_Int32>(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<sal_Int32>(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<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1),
+ getProperty<sal_Int32>(getParagraph(2), "ParaAdjust")); // right
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1),
+ getProperty<sal_Int32>(getParagraph(3), "ParaAdjust")); // right
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(getParagraph(4), "ParaAdjust")); // left
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(1),
+ getProperty<sal_Int32>(getParagraph(5), "ParaAdjust")); // right
+
+ CPPUNIT_ASSERT_EQUAL(OUString(""),
+ getProperty<OUString>(getParagraph(7), "NumberingStyleName"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("WWNum2"),
+ getProperty<OUString>(getParagraph(8), "NumberingStyleName"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(10), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(11), "ParaStyleName"));
+
+ // reject tracked paragraph adjustments
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(getParagraph(2), "ParaAdjust")); // left
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3),
+ getProperty<sal_Int32>(getParagraph(3), "ParaAdjust")); // center
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(3),
+ getProperty<sal_Int32>(getParagraph(4), "ParaAdjust")); // center
+ CPPUNIT_ASSERT_EQUAL(sal_Int32(0),
+ getProperty<sal_Int32>(getParagraph(5), "ParaAdjust")); // left
+
+ // tdf#126245 revert numbering changes
+ CPPUNIT_ASSERT_EQUAL(OUString("WWNum2"),
+ getProperty<OUString>(getParagraph(7), "NumberingStyleName"));
+
+ CPPUNIT_ASSERT_EQUAL(OUString(""),
+ getProperty<OUString>(getParagraph(8), "NumberingStyleName"));
+
+ // tdf#126243 revert paragraph styles
+ CPPUNIT_ASSERT_EQUAL(OUString("Standard"),
+ getProperty<OUString>(getParagraph(10), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 3"),
+ getProperty<OUString>(getParagraph(11), "ParaStyleName"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf120338_multiple_paragraph_join)
+{
+ load(DATA_DIRECTORY, "redline-para-join.docx");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(3), "ParaStyleName"));
+
+ // reject tracked paragraph styles
+ dispatchCommand(mxComponent, ".uno:RejectAllTrackedChanges", {});
+
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"),
+ getProperty<OUString>(getParagraph(1), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 2"),
+ getProperty<OUString>(getParagraph(2), "ParaStyleName"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Heading 3"),
+ getProperty<OUString>(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<SwLayoutFrame*>(pSecondPage)->GetLower();
+ CPPUNIT_ASSERT(pBodyFrame->IsLayoutFrame());
+ SwFrame* pTextFrame = static_cast<SwLayoutFrame*>(pBodyFrame)->GetLower();
+ CPPUNIT_ASSERT(pTextFrame->IsTextFrame());
+ sal_uLong nNodeIndex = static_cast<SwTextFrame*>(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<text::XTextRange> 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<text::XTextRange> 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<text::XTextRange> xPara = getParagraph(1);
+ sal_Unicode vEnSpaces[5] = { 8194, 8194, 8194, 8194, 8194 };
+ CPPUNIT_ASSERT_EQUAL(OUString(vEnSpaces, 5), pFieldmark->GetContent());
+ std::pair<bool, double> 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<bool, double> 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<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(maTempFile.GetURL(), {});
+
+ uno::Reference<packages::zip::XZipFileAccess2> 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<SwXTextDocument*>(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<long>(240),
+ pWrtShell->GetVisibleCursor()->GetTextCursor().GetSize().getHeight());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf118311)
+{
+ load(DATA_DIRECTORY, "tdf118311.fodt");
+
+ SwXTextDocument* pDoc = dynamic_cast<SwXTextDocument*>(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<beans::PropertyValue> 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", {});
+
+ xmlDocUniquePtr 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<SwXTextDocument*>(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<view::XSelectionSupplier> 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<SwXTextDocument*>(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<size_t>(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<SwXTextDocument*>(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<SwTextNode*>(pDoc->GetNodes()[nIndex]);
+ CPPUNIT_ASSERT(pTextNodeA1->GetText().startsWith("Insert"));
+ nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
+ SwTextNode* pTextNodeA2 = static_cast<SwTextNode*>(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");
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ assertXPath(pXmlDoc, "//fly/txt[@WritingMode='Vertical']", 1);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */