summaryrefslogtreecommitdiffstats
path: root/sw/qa/uibase
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/qa/uibase
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/qa/uibase')
-rw-r--r--sw/qa/uibase/dochdl/dochdl.cxx79
-rw-r--r--sw/qa/uibase/fldui/fldui.cxx98
-rw-r--r--sw/qa/uibase/frmdlg/data/image.pngbin0 -> 766 bytes
-rw-r--r--sw/qa/uibase/frmdlg/data/wrapped-math-object.docxbin0 -> 20504 bytes
-rw-r--r--sw/qa/uibase/frmdlg/frmdlg.cxx63
-rw-r--r--sw/qa/uibase/shells/data/ole-preview-update.odtbin0 -> 20265 bytes
-rw-r--r--sw/qa/uibase/shells/data/ole-save-preview-update.odtbin0 -> 16006 bytes
-rw-r--r--sw/qa/uibase/shells/shells.cxx266
-rw-r--r--sw/qa/uibase/uiview/data/keep-ratio.fodt21
-rw-r--r--sw/qa/uibase/uiview/data/update-replacement-nosetting.odtbin0 -> 40982 bytes
-rw-r--r--sw/qa/uibase/uiview/data/updateall-objectreplacements.odtbin0 -> 35630 bytes
-rw-r--r--sw/qa/uibase/uiview/uiview.cxx317
-rw-r--r--sw/qa/uibase/uno/data/cond-field-cached-value.docxbin0 -> 12164 bytes
-rw-r--r--sw/qa/uibase/uno/uno.cxx95
-rw-r--r--sw/qa/uibase/wrtsh/wrtsh.cxx374
15 files changed, 1313 insertions, 0 deletions
diff --git a/sw/qa/uibase/dochdl/dochdl.cxx b/sw/qa/uibase/dochdl/dochdl.cxx
new file mode 100644
index 000000000..c882eab89
--- /dev/null
+++ b/sw/qa/uibase/dochdl/dochdl.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <vcl/transfer.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+
+#include <docsh.hxx>
+#include <swdtflvr.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+
+/// Covers sw/source/uibase/dochdl/ fixes.
+class SwUibaseDochdlTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseDochdlTest, testSelectPasteFormat)
+{
+ // Create a new document and cut a character.
+ SwDoc* pDoc = createSwDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Insert2("x");
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
+ rtl::Reference<SwTransferable> pTransfer = new SwTransferable(*pWrtShell);
+ pTransfer->Cut();
+
+ // Decide what format to use when doing a Writer->Writer paste and both RTF and ODF is an
+ // available format.
+ TransferableDataHelper aHelper(pTransfer);
+ sal_uInt8 nAction = EXCHG_OUT_ACTION_INSERT_STRING;
+ SotClipboardFormatId nFormat = SotClipboardFormatId::RICHTEXT;
+ SwTransferable::SelectPasteFormat(aHelper, nAction, nFormat);
+
+ CPPUNIT_ASSERT_EQUAL(EXCHG_OUT_ACTION_INSERT_OLE, nAction);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 85 (EMBED_SOURCE)
+ // - Actual : 145 (RICHTEXT)
+ // i.e. RTF was selected for Writer->Writer out of process copying, which is worse than ODF.
+ CPPUNIT_ASSERT_EQUAL(SotClipboardFormatId::EMBED_SOURCE, nFormat);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseDochdlTest, testComplexSelection)
+{
+ // Given a document where a text node has hints, but no as-char images.
+ SwDoc* pDoc = createSwDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Insert2("abc");
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
+ SfxItemSet aSet(pWrtShell->GetView().GetPool(),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>);
+ // Bold, italic, underline.
+ aSet.Put(SvxWeightItem(WEIGHT_BOLD, RES_CHRATR_WEIGHT));
+ aSet.Put(SvxPostureItem(ITALIC_NORMAL, RES_CHRATR_POSTURE));
+ aSet.Put(SvxUnderlineItem(LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE));
+ pWrtShell->SetAttrSet(aSet);
+ uno::Reference<datatransfer::XTransferable2> xTransfer = new SwTransferable(*pWrtShell);
+
+ // When checking if the selection is complex, then there should be no crash.
+ // Without the accompanying fix in place, this test would have crashed, because we read past the
+ // end of the hints array.
+ CPPUNIT_ASSERT(!xTransfer->isComplex());
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/fldui/fldui.cxx b/sw/qa/uibase/fldui/fldui.cxx
new file mode 100644
index 000000000..263195112
--- /dev/null
+++ b/sw/qa/uibase/fldui/fldui.cxx
@@ -0,0 +1,98 @@
+/* -*- 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 <com/sun/star/text/BibliographyDataType.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <fldmgr.hxx>
+#include <authfld.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// Covers sw/source/uibase/fldui/ fixes.
+class Test : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testBiblioPageNumberUpdate)
+{
+ // Given a document with 2 biblio fields, same properties, but different page number in the URL:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xField(
+ xFactory->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aFields = {
+ comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW),
+ comphelper::makePropertyValue("Identifier", OUString("AT")),
+ comphelper::makePropertyValue("Author", OUString("Author")),
+ comphelper::makePropertyValue("Title", OUString("Title")),
+ comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf#page=1")),
+ };
+ xField->setPropertyValue("Fields", uno::Any(aFields));
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<text::XTextContent> xContent(xField, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false);
+ xField.set(xFactory->createInstance("com.sun.star.text.TextField.Bibliography"),
+ uno::UNO_QUERY);
+ aFields = {
+ comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW),
+ comphelper::makePropertyValue("Identifier", OUString("AT")),
+ comphelper::makePropertyValue("Author", OUString("Author")),
+ comphelper::makePropertyValue("Title", OUString("Title")),
+ comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf#page=2")),
+ };
+ xField->setPropertyValue("Fields", uno::Any(aFields));
+ xContent.set(xField, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false);
+
+ // When changing the page number in the second field's URL:
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->SttEndDoc(/*bStt=*/false);
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ OUString aCoreFields[AUTH_FIELD_END];
+ aCoreFields[AUTH_FIELD_AUTHORITY_TYPE] = OUString::number(text::BibliographyDataType::WWW);
+ aCoreFields[AUTH_FIELD_IDENTIFIER] = "AT";
+ aCoreFields[AUTH_FIELD_AUTHOR] = "Author";
+ aCoreFields[AUTH_FIELD_TITLE] = "Title";
+ OUString aNewUrl = "http://www.example.com/test.pdf#page=42";
+ aCoreFields[AUTH_FIELD_URL] = aNewUrl;
+ OUStringBuffer aFieldBuffer;
+ for (const auto& rField : aCoreFields)
+ {
+ aFieldBuffer.append(rField).append(TOX_STYLE_DELIMITER);
+ }
+ SwFieldMgr aMgr(pWrtShell);
+ aMgr.UpdateCurField(0, aFieldBuffer.makeStringAndClear(), OUString());
+
+ // Then make sure that the second field's URL is updated:
+ auto pField = static_cast<SwAuthorityField*>(pWrtShell->GetCurField());
+ const SwAuthEntry* pEntry = pField->GetAuthEntry();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: http://www.example.com/test.pdf#page=42
+ // - Actual : http://www.example.com/test.pdf#page=2
+ // i.e. the second biblio field's URL was not updated.
+ CPPUNIT_ASSERT_EQUAL(aNewUrl, pEntry->GetAuthorField(AUTH_FIELD_URL));
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/frmdlg/data/image.png b/sw/qa/uibase/frmdlg/data/image.png
new file mode 100644
index 000000000..fdad35484
--- /dev/null
+++ b/sw/qa/uibase/frmdlg/data/image.png
Binary files differ
diff --git a/sw/qa/uibase/frmdlg/data/wrapped-math-object.docx b/sw/qa/uibase/frmdlg/data/wrapped-math-object.docx
new file mode 100644
index 000000000..c6f76dd5d
--- /dev/null
+++ b/sw/qa/uibase/frmdlg/data/wrapped-math-object.docx
Binary files differ
diff --git a/sw/qa/uibase/frmdlg/frmdlg.cxx b/sw/qa/uibase/frmdlg/frmdlg.cxx
new file mode 100644
index 000000000..790902ce3
--- /dev/null
+++ b/sw/qa/uibase/frmdlg/frmdlg.cxx
@@ -0,0 +1,63 @@
+/* -*- 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 <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/frmdlg/data/";
+
+/// Covers sw/source/uibase/frmdlg/ fixes.
+class SwUibaseFrmdlgTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseFrmdlgTest, testWrappedMathObject)
+{
+ // The document includes a Math object with explicit wrapping.
+ load(DATA_DIRECTORY, "wrapped-math-object.docx");
+ uno::Reference<drawing::XShape> xMath = getShape(1);
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 4 (AT_CHARACTER)
+ // - Actual : 1 (AS_CHARACTER)
+ // i.e. the object lost its wrapping, leading to an incorrect position.
+ CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AT_CHARACTER,
+ getProperty<text::TextContentAnchorType>(getShape(1), "AnchorType"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseFrmdlgTest, testAnchorTypeFromStyle)
+{
+ // Given a document with aGraphics style with anchor type set to as-character:
+ createSwDoc();
+ uno::Reference<beans::XPropertySet> xGraphics(getStyles("FrameStyles")->getByName("Graphics"),
+ uno::UNO_QUERY);
+ xGraphics->setPropertyValue("AnchorType", uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+
+ // When inserting an image:
+ OUString aImageURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "image.png";
+ uno::Sequence<beans::PropertyValue> aArgs = {
+ comphelper::makePropertyValue("FileName", aImageURL),
+ };
+ dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs);
+
+ // Then make sure the image's anchor type is as-char:
+ auto eActual = getProperty<text::TextContentAnchorType>(getShape(1), "AnchorType");
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1 (AS_CHARACTER)
+ // - Actual : 4 (AT_CHARACTER)
+ // i.e. the anchor type from the style was ignored.
+ CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER, eActual);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/shells/data/ole-preview-update.odt b/sw/qa/uibase/shells/data/ole-preview-update.odt
new file mode 100644
index 000000000..3fd4d2646
--- /dev/null
+++ b/sw/qa/uibase/shells/data/ole-preview-update.odt
Binary files differ
diff --git a/sw/qa/uibase/shells/data/ole-save-preview-update.odt b/sw/qa/uibase/shells/data/ole-save-preview-update.odt
new file mode 100644
index 000000000..353ce7fa0
--- /dev/null
+++ b/sw/qa/uibase/shells/data/ole-save-preview-update.odt
Binary files differ
diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx
new file mode 100644
index 000000000..5a99b8e3c
--- /dev/null
+++ b/sw/qa/uibase/shells/shells.cxx
@@ -0,0 +1,266 @@
+/* -*- 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 <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/text/BibliographyDataType.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+
+#include <IDocumentContentOperations.hxx>
+#include <cmdid.h>
+#include <fmtanchr.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <docsh.hxx>
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/shells/data/";
+
+/// Covers sw/source/uibase/shells/ fixes.
+class SwUibaseShellsTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testTdf130179)
+{
+ SwDoc* pDoc = createSwDoc();
+ IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations();
+ SwCursorShell* pShell(pDoc->GetEditShell());
+ SfxItemSet aFrameSet(pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>);
+ SfxItemSet aGrfSet(pDoc->GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, RES_GRFATR_END - 1>);
+ SwFormatAnchor aAnchor(RndStdIds::FLY_AT_PARA);
+ aFrameSet.Put(aAnchor);
+ GraphicObject aGrf;
+ CPPUNIT_ASSERT(rIDCO.InsertGraphicObject(*pShell->GetCursor(), aGrf, &aFrameSet, &aGrfSet));
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_GRF));
+
+ 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();
+
+ std::unique_ptr<SfxPoolItem> pItem;
+ pView->GetViewFrame()->GetBindings().QueryState(FN_POSTIT, pItem);
+ // Without the accompanying fix in place, this test would have failed with:
+ // assertion failed
+ // - Expression: !pItem
+ // i.e. comment insertion was enabled for an at-para anchored image.
+ CPPUNIT_ASSERT(!pItem);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testShapeTextAlignment)
+{
+// FIXME find out why this fails on macOS/Windows
+#if !defined(MACOSX) && !defined(_WIN32)
+ // Create a document with a rectangle in it.
+ SwDoc* pDoc = createSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ Point aStartPos(1000, 1000);
+ pWrtShell->BeginCreate(SdrObjKind::Rectangle, aStartPos);
+ Point aMovePos(2000, 2000);
+ pWrtShell->MoveCreate(aMovePos);
+ pWrtShell->EndCreate(SdrCreateCmd::ForceEnd);
+
+ // Start shape text edit.
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ // Select the shape.
+ pView->GetViewFrame()->GetDispatcher()->Execute(FN_CNTNT_TO_NEXT_FRAME, SfxCallMode::SYNCHRON);
+ pView->StopShellTimer();
+ // Start the actual text edit.
+ SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pPage->GetObjCount());
+ SdrObject* pObject = pPage->GetObj(0);
+ pView->EnterShapeDrawTextMode(pObject);
+ pView->AttrChangedNotify(nullptr);
+
+ // Change paragraph adjustment to center.
+ pView->GetViewFrame()->GetDispatcher()->Execute(SID_ATTR_PARA_ADJUST_CENTER,
+ SfxCallMode::SYNCHRON);
+
+ // End shape text edit.
+ pWrtShell->EndTextEdit();
+
+ const OutlinerParaObject* pOutliner = pObject->GetOutlinerParaObject();
+ // Without the accompanying fix in place, this test would have failed, because the shape had no
+ // text or text formatting. In other words the paragraph adjustment command was ignored.
+ CPPUNIT_ASSERT(pOutliner);
+ const SfxItemSet& rParaAttribs = pOutliner->GetTextObject().GetParaAttribs(0);
+ SvxAdjust eAdjust = rParaAttribs.GetItem(EE_PARA_JUST)->GetAdjust();
+ CPPUNIT_ASSERT_EQUAL(SvxAdjust::Center, eAdjust);
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testOleSavePreviewUpdate)
+{
+ // Load a document with 2 charts in it. The second is down enough that you have to scroll to
+ // trigger its rendering. Previews are missing for both.
+ load(DATA_DIRECTORY, "ole-save-preview-update.odt");
+
+ // Explicitly update OLE previews, etc.
+ dispatchCommand(mxComponent, ".uno:UpdateAll", {});
+
+ // Save the document and see if we get the previews.
+ 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, because the object
+ // replacements were not generated, even after UpdateAll.
+ CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Object 1"));
+ CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Object 2"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testOlePreviewUpdate)
+{
+ // Given a document with an embedded Writer object:
+ load(DATA_DIRECTORY, "ole-preview-update.odt");
+
+ // When updating "all" (including OLE previews):
+ dispatchCommand(mxComponent, ".uno:UpdateAll", {});
+
+ // Then make sure the preview is no longer a 0-sized stream:
+ 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());
+ uno::Reference<io::XInputStream> xInputStream(
+ xNameAccess->getByName("ObjectReplacements/Object 1"), uno::UNO_QUERY);
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
+ // Without the accompanying fix in place, this test would have failed, the stream was still
+ // empty.
+ CPPUNIT_ASSERT_GREATER(static_cast<sal_uInt64>(0), pStream->remainingSize());
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testBibliographyUrlContextMenu)
+{
+ // Given a document with a bibliography field:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xField(
+ xFactory->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aFields = {
+ comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW),
+ comphelper::makePropertyValue("Identifier", OUString("AT")),
+ comphelper::makePropertyValue("Author", OUString("Author")),
+ comphelper::makePropertyValue("Title", OUString("Title")),
+ comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf#page=1")),
+ };
+ xField->setPropertyValue("Fields", uno::Any(aFields));
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<text::XTextContent> xContent(xField, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false);
+
+ // When selecting the field and opening the context menu:
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
+ SfxDispatcher* pDispatcher = pDocShell->GetViewShell()->GetViewFrame()->GetDispatcher();
+ css::uno::Any aState;
+ SfxItemState eState = pDispatcher->QueryState(SID_OPEN_HYPERLINK, aState);
+
+ // Then the "open hyperlink" menu item should be visible:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 32 (SfxItemState::DEFAULT)
+ // - Actual : 1 (SfxItemState::DISABLED)
+ // i.e. the menu item was not visible for biblio entry fields with an URL.
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, eState);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testBibliographyLocalCopyContextMenu)
+{
+ // Given a document with a bibliography field's local copy:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xField(
+ xFactory->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aFields = {
+ comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW),
+ comphelper::makePropertyValue("Identifier", OUString("AT")),
+ comphelper::makePropertyValue("Author", OUString("Author")),
+ comphelper::makePropertyValue("Title", OUString("Title")),
+ comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf#page=1")),
+ comphelper::makePropertyValue("LocalURL", OUString("file:///home/me/test.pdf")),
+ };
+ xField->setPropertyValue("Fields", uno::Any(aFields));
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<text::XTextContent> xContent(xField, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xContent, /*bAbsorb=*/false);
+
+ // When selecting the field and opening the context menu:
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 1, /*bBasicCall=*/false);
+ SfxDispatcher* pDispatcher = pDocShell->GetViewShell()->GetViewFrame()->GetDispatcher();
+ css::uno::Any aState;
+ SfxItemState eState = pDispatcher->QueryState(FN_OPEN_LOCAL_URL, aState);
+
+ // Then the "open local copy" menu item should be visible:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 32 (SfxItemState::DEFAULT)
+ // - Actual : 1 (SfxItemState::DISABLED)
+ // i.e. the context menu was disabled all the time, even for biblio fields.
+ CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT, eState);
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, testContentControlPageBreak)
+{
+ // Given a document with a content control and a cursor inside the content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "test", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->SttEndDoc(/*bStt=*/true);
+ pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+
+ // When trying to insert a page break:
+ dispatchCommand(mxComponent, ".uno:InsertPagebreak", {});
+
+ // Then make sure that the document still has a single page:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 2
+ // i.e. inline content control had its start and end in different text nodes, which is not
+ // allowed.
+ CPPUNIT_ASSERT_EQUAL(1, getPages());
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/uiview/data/keep-ratio.fodt b/sw/qa/uibase/uiview/data/keep-ratio.fodt
new file mode 100644
index 000000000..7cfffbec5
--- /dev/null
+++ b/sw/qa/uibase/uiview/data/keep-ratio.fodt
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:settings>
+ <config:config-item-set config:name="ooo:view-settings">
+ <config:config-item-map-indexed config:name="Views">
+ <config:config-item-map-entry>
+ <config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleTop" config:type="long">0</config:config-item>
+ <config:config-item config:name="VisibleRight" config:type="long">40190</config:config-item>
+ <config:config-item config:name="VisibleBottom" config:type="long">22728</config:config-item>
+ <config:config-item config:name="KeepRatio" config:type="boolean">true</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-indexed>
+ </config:config-item-set>
+ </office:settings>
+ <office:body>
+ <office:text>
+ <text:p/>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt b/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt
new file mode 100644
index 000000000..055c3d1a2
--- /dev/null
+++ b/sw/qa/uibase/uiview/data/update-replacement-nosetting.odt
Binary files differ
diff --git a/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt b/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt
new file mode 100644
index 000000000..35decf73f
--- /dev/null
+++ b/sw/qa/uibase/uiview/data/updateall-objectreplacements.odt
Binary files differ
diff --git a/sw/qa/uibase/uiview/uiview.cxx b/sw/qa/uibase/uiview/uiview.cxx
new file mode 100644
index 000000000..6b608e0ae
--- /dev/null
+++ b/sw/qa/uibase/uiview/uiview.cxx
@@ -0,0 +1,317 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <com/sun/star/frame/XDispatchHelper.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/XStorable2.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <swmodule.hxx>
+#include <view.hxx>
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/uiview/data/";
+
+/// Covers sw/source/uibase/uiview/ fixes.
+class SwUibaseUiviewTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateAllObjectReplacements)
+{
+ // Make a temporary copy of the test document
+ utl::TempFile tmp;
+ tmp.EnableKillingFile();
+ OUString sTempCopy = tmp.GetURL();
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None,
+ osl::File::copy(m_directories.getURLFromSrc(DATA_DIRECTORY)
+ + "updateall-objectreplacements.odt",
+ sTempCopy));
+
+ /* BASIC code that exhibits the problem:
+
+ desktop = CreateUnoService("com.sun.star.frame.Desktop")
+ Dim props(0) as new com.sun.star.beans.PropertyValue
+ props(0).Name = "Hidden"
+ props(0).Value = true
+ component = desktop.loadComponentFromURL("file://.../test.odt", "_default", 0, props)
+ Wait 1000 ' workaround
+ dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
+ frame = component.CurrentController.Frame
+ dispatcher.executeDispatch(frame, ".uno:UpdateAll", "", 0, Array())
+ component.storeSelf(Array())
+ component.dispose()
+ */
+
+ uno::Reference<lang::XMultiServiceFactory> xFactory(comphelper::getProcessServiceFactory());
+
+ // Load the copy
+ uno::Reference<uno::XInterface> xInterface
+ = xFactory->createInstance("com.sun.star.frame.Desktop");
+ uno::Reference<frame::XComponentLoader> xComponentLoader(xInterface, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aLoadArgs{ comphelper::makePropertyValue("Hidden", true) };
+ mxComponent = xComponentLoader->loadComponentFromURL(sTempCopy, "_default", 0, aLoadArgs);
+
+ // Perform the .uno:UpdateAll call and save
+ xInterface = xFactory->createInstance("com.sun.star.frame.DispatchHelper");
+ uno::Reference<frame::XDispatchHelper> xDispatchHelper(xInterface, uno::UNO_QUERY);
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ uno::Reference<frame::XDispatchProvider> xDispatchProvider(
+ xModel->getCurrentController()->getFrame(), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aNoArgs;
+ xDispatchHelper->executeDispatch(xDispatchProvider, ".uno:UpdateAll", OUString(), 0, aNoArgs);
+ uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeSelf(aNoArgs);
+
+ // Check the contents of the updated copy and verify that ObjectReplacements are there
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+ = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xFactory),
+ sTempCopy);
+
+ CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components"));
+ CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components_1"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testUpdateReplacementNosetting)
+{
+ // Load a copy of the document in hidden mode.
+ OUString aSourceURL
+ = m_directories.getURLFromSrc(DATA_DIRECTORY) + "update-replacement-nosetting.odt";
+ CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, osl::File::copy(aSourceURL, maTempFile.GetURL()));
+ mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument",
+ { comphelper::makePropertyValue("Hidden", true) });
+
+ // Update "everything" (including object replacements) and save it.
+ dispatchCommand(mxComponent, ".uno:UpdateAll", {});
+ uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeSelf({});
+
+ // Check the contents of the updated copy.
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ uno::Reference<packages::zip::XZipFileAccess2> xNameAccess
+ = packages::zip::ZipFileAccess::createWithURL(xContext, maTempFile.GetURL());
+
+ // Without the accompanying fix in place, this test would have failed, because the embedded
+ // object replacement image was not generated.
+ CPPUNIT_ASSERT(xNameAccess->hasByName("ObjectReplacements/Components"));
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testKeepRatio)
+{
+ // Given a document with a custom KeepRatio:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "keep-ratio.fodt";
+
+ // When loading that document:
+ mxComponent = loadFromDesktop(aURL);
+
+ // Then make sure we read the custom value:
+ auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
+ const SwViewOption* pViewOption = pWrtShell->GetViewOptions();
+ comphelper::ScopeGuard g([pWrtShell, pViewOption] {
+ SwViewOption aViewOption(*pViewOption);
+ aViewOption.SetKeepRatio(false);
+ SW_MOD()->ApplyUsrPref(aViewOption, &pWrtShell->GetView());
+ });
+ // Without the accompanying fix in place, this test would have failed, because KeepRatio was not
+ // mapped to settings.xml
+ CPPUNIT_ASSERT(pViewOption->IsKeepRatio());
+
+ // Then export as well:
+ uno::Reference<frame::XStorable2> xStorable(mxComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aStoreArgs = {
+ comphelper::makePropertyValue("FilterName", OUString("writer8")),
+ };
+ xStorable->storeToURL(maTempFile.GetURL(), aStoreArgs);
+ mbExported = true;
+ xmlDocUniquePtr pXmlDoc = parseExport("settings.xml");
+ assertXPathContent(pXmlDoc, "//config:config-item[@config:name='KeepRatio']", "true");
+}
+
+namespace
+{
+/// Interception implementation that disables .uno:Zoom on Image1, but not on Image2.
+struct ImageInterceptor : public cppu::WeakImplHelper<frame::XDispatchProviderInterceptor>
+{
+ uno::Reference<view::XSelectionSupplier> m_xSelectionSupplier;
+ uno::Reference<frame::XDispatchProvider> m_xMaster;
+ uno::Reference<frame::XDispatchProvider> m_xSlave;
+ int m_nEnabled = 0;
+ int m_nDisabled = 0;
+
+public:
+ ImageInterceptor(const uno::Reference<lang::XComponent>& xComponent);
+
+ // XDispatchProviderInterceptor
+ uno::Reference<frame::XDispatchProvider> SAL_CALL getMasterDispatchProvider() override;
+ uno::Reference<frame::XDispatchProvider> SAL_CALL getSlaveDispatchProvider() override;
+ void SAL_CALL setMasterDispatchProvider(
+ const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override;
+ void SAL_CALL
+ setSlaveDispatchProvider(const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override;
+
+ // XDispatchProvider
+ uno::Reference<frame::XDispatch> SAL_CALL queryDispatch(const util::URL& rURL,
+ const OUString& rTargetFrameName,
+ sal_Int32 SearchFlags) override;
+ uno::Sequence<uno::Reference<frame::XDispatch>> SAL_CALL
+ queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& rRequests) override;
+};
+}
+
+ImageInterceptor::ImageInterceptor(const uno::Reference<lang::XComponent>& xComponent)
+{
+ uno::Reference<frame::XModel2> xModel(xComponent, uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xModel.is());
+ m_xSelectionSupplier.set(xModel->getCurrentController(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(m_xSelectionSupplier.is());
+}
+
+uno::Reference<frame::XDispatchProvider> ImageInterceptor::getMasterDispatchProvider()
+{
+ return m_xMaster;
+}
+
+uno::Reference<frame::XDispatchProvider> ImageInterceptor::getSlaveDispatchProvider()
+{
+ return m_xSlave;
+}
+
+void ImageInterceptor::setMasterDispatchProvider(
+ const uno::Reference<frame::XDispatchProvider>& xNewSupplier)
+{
+ m_xMaster = xNewSupplier;
+}
+
+void ImageInterceptor::setSlaveDispatchProvider(
+ const uno::Reference<frame::XDispatchProvider>& xNewSupplier)
+{
+ m_xSlave = xNewSupplier;
+}
+
+uno::Reference<frame::XDispatch> ImageInterceptor::queryDispatch(const util::URL& rURL,
+ const OUString& rTargetFrameName,
+ sal_Int32 nSearchFlags)
+{
+ // Disable the UNO command based on the currently selected image, i.e. this can't be cached when
+ // a different image is selected. Originally this was .uno:SetBorderStyle, but let's pick a
+ // command which is active when running cppunit tests:
+ if (rURL.Complete == ".uno:Zoom")
+ {
+ uno::Reference<container::XNamed> xImage;
+ m_xSelectionSupplier->getSelection() >>= xImage;
+ if (xImage.is() && xImage->getName() == "Image1")
+ {
+ ++m_nDisabled;
+ return {};
+ }
+
+ ++m_nEnabled;
+ }
+
+ return m_xSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags);
+}
+
+uno::Sequence<uno::Reference<frame::XDispatch>>
+ImageInterceptor::queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& /*rRequests*/)
+{
+ return {};
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest, testSwitchBetweenImages)
+{
+ // Given a document with 2 images, and an interceptor catching an UNO command that specific to
+ // the current selection:
+ SwDoc* pDoc = createSwDoc();
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ for (int i = 0; i < 2; ++i)
+ {
+ uno::Reference<beans::XPropertySet> xTextGraphic(
+ xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ xTextGraphic->setPropertyValue("AnchorType",
+ uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ xTextGraphic->setPropertyValue("Size", uno::Any(awt::Size(5000, 5000)));
+ uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xTextContent, false);
+ }
+ pWrtShell->SttEndDoc(/*bStt=*/false);
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ uno::Reference<frame::XDispatchProviderInterception> xRegistration(
+ xModel->getCurrentController()->getFrame(), uno::UNO_QUERY);
+ rtl::Reference pInterceptor(new ImageInterceptor(mxComponent));
+
+ xRegistration->registerDispatchProviderInterceptor(pInterceptor);
+ pInterceptor->m_nEnabled = 0;
+ pInterceptor->m_nDisabled = 0;
+
+ // When selecting the first image:
+ uno::Reference<text::XTextGraphicObjectsSupplier> xGraphicObjectsSupplier(mxComponent,
+ uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xGraphicObjects(
+ xGraphicObjectsSupplier->getGraphicObjects(), uno::UNO_QUERY);
+ pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(0));
+ Scheduler::ProcessEventsToIdle();
+ SwView* pView = pDoc->GetDocShell()->GetView();
+ pView->StopShellTimer();
+
+ // Then make sure the UNO command is disabled:
+ CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nEnabled);
+ CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nDisabled);
+
+ // Given a clean state:
+ pInterceptor->m_nEnabled = 0;
+ pInterceptor->m_nDisabled = 0;
+
+ // When selecting the second image:
+ pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(1));
+ Scheduler::ProcessEventsToIdle();
+ pView->StopShellTimer();
+
+ // Then make sure the UNO command is enabled:
+ CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nEnabled);
+ CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nDisabled);
+
+ // Given a clean state:
+ pInterceptor->m_nEnabled = 0;
+ pInterceptor->m_nDisabled = 0;
+
+ // When selecting the first image, again (this time not changing the selection type):
+ pInterceptor->m_xSelectionSupplier->select(xGraphicObjects->getByIndex(0));
+ Scheduler::ProcessEventsToIdle();
+ pView->StopShellTimer();
+
+ // Then make sure the UNO command is disabled:
+ CPPUNIT_ASSERT_EQUAL(0, pInterceptor->m_nEnabled);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected greater or equal than: 1
+ // - Actual : 0
+ // i.e. selecting the first image didn't result in a disabled UNO command.
+ CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor->m_nDisabled);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/uno/data/cond-field-cached-value.docx b/sw/qa/uibase/uno/data/cond-field-cached-value.docx
new file mode 100644
index 000000000..a19b1240c
--- /dev/null
+++ b/sw/qa/uibase/uno/data/cond-field-cached-value.docx
Binary files differ
diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx
new file mode 100644
index 000000000..f4b337d8f
--- /dev/null
+++ b/sw/qa/uibase/uno/uno.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 <com/sun/star/frame/XModel2.hpp>
+#include <com/sun/star/text/XTextViewTextRangeSupplier.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+#include <vcl/scheduler.hxx>
+
+#include <docsh.hxx>
+#include <edtwin.hxx>
+#include <unotextrange.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+
+constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/uibase/uno/data/";
+
+/// Covers sw/source/uibase/uno/ fixes.
+class SwUibaseUnoTest : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testLockControllers)
+{
+ mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument");
+ {
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY_THROW);
+ xModel->lockControllers();
+ }
+ {
+ uno::Reference<util::XCloseable> xCloseable(mxComponent, uno::UNO_QUERY_THROW);
+ xCloseable->close(false);
+ }
+ // Without the accompanying fix in place, this test would have crashed.
+ mxComponent.clear();
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testCondFieldCachedValue)
+{
+ load(DATA_DIRECTORY, "cond-field-cached-value.docx");
+ Scheduler::ProcessEventsToIdle();
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual :
+ // i.e. the conditional field lost its cached content.
+ getParagraph(2, "1");
+}
+
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testCreateTextRangeByPixelPosition)
+{
+ // Given a document with 2 characters, and the pixel position of the point between them:
+ SwDoc* pDoc = createSwDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ pWrtShell->Insert2("AZ");
+ pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
+ Point aLogic = pWrtShell->GetCharRect().Center();
+ SwView* pView = pDocShell->GetView();
+ SwEditWin& rEditWin = pView->GetEditWin();
+ Point aPixel = rEditWin.LogicToPixel(aLogic);
+
+ // When converting that pixel position to a document model position (text range):
+ uno::Reference<frame::XModel2> xModel(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xControllers = xModel->getControllers();
+ uno::Reference<text::XTextViewTextRangeSupplier> xController(xControllers->nextElement(),
+ uno::UNO_QUERY);
+ awt::Point aPoint(aPixel.getX(), aPixel.getY());
+ uno::Reference<text::XTextRange> xTextRange
+ = xController->createTextRangeByPixelPosition(aPoint);
+
+ // Then make sure that text range points after the first character:
+ auto pTextRange = dynamic_cast<SwXTextRange*>(xTextRange.get());
+ SwPaM aPaM(pDoc->GetNodes());
+ pTextRange->GetPositions(aPaM);
+ sal_Int32 nActual = aPaM.GetPoint()->nContent.GetIndex();
+ // Without the needed PixelToLogic() call in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the returned text range pointed before the first character, not between the first and
+ // the second character.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nActual);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx
new file mode 100644
index 000000000..87537e887
--- /dev/null
+++ b/sw/qa/uibase/wrtsh/wrtsh.cxx
@@ -0,0 +1,374 @@
+/* -*- 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 <wrtsh.hxx>
+
+#include <optional>
+
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <comphelper/propertyvalue.hxx>
+
+#include <swmodeltestbase.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <formatlinebreak.hxx>
+#include <ndtxt.hxx>
+#include <textcontentcontrol.hxx>
+#include <fmtanchr.hxx>
+
+namespace
+{
+/// Covers sw/source/uibase/wrtsh/ fixes.
+class Test : public SwModelTestBase
+{
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertLineBreak)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a clearing break:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ std::optional<SwLineBreakClear> oClear = SwLineBreakClear::ALL;
+ pWrtShell->InsertLineBreak(oClear);
+
+ // Then make sure it's not just a plain linebreak:
+ uno::Reference<css::text::XTextRange> xTextPortion = getRun(getParagraph(1), 1);
+ auto aPortionType = getProperty<OUString>(xTextPortion, "TextPortionType");
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: LineBreak
+ // - Actual : Text
+ // i.e. the line break lost its "clear" property.
+ CPPUNIT_ASSERT_EQUAL(OUString("LineBreak"), aPortionType);
+ auto xLineBreak = getProperty<uno::Reference<text::XTextContent>>(xTextPortion, "LineBreak");
+ auto eClear = getProperty<sal_Int16>(xLineBreak, "Clear");
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(SwLineBreakClear::ALL), eClear);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testGotoContentControl)
+{
+ // Given a document with a content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "test", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // When going to that content control in placeholder mode:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwNodeOffset nIndex = pWrtShell->GetCursor()->GetNode().GetIndex();
+ SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the content control is selected (without the dummy character):
+ // Without the accompanying fix in place, this test would have failed, the user had to manually
+ // select the placeholder text.
+ sal_Int32 nStart = pWrtShell->GetCursor()->Start()->nContent.GetIndex();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nStart);
+ sal_Int32 nEnd = pWrtShell->GetCursor()->End()->nContent.GetIndex();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5), nEnd);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTickCheckboxContentControl)
+{
+ // Given a document with a checkbox (checked) content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, OUString(u"☒"), /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("Checkbox", uno::Any(true));
+ xContentControlProps->setPropertyValue("Checked", uno::Any(true));
+ xContentControlProps->setPropertyValue("CheckedState", uno::Any(OUString(u"☒")));
+ xContentControlProps->setPropertyValue("UncheckedState", uno::Any(OUString(u"☐")));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // When clicking on that content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the checkbox is no longer checked:
+ // Without the accompanying fix in place, this test would have failed:
+ // - Expected: ☐
+ // - Actual : ☒
+ // i.e. the text node's text was "Ballot Box with X", not just "Ballot Box".
+ CPPUNIT_ASSERT_EQUAL(OUString(u"☐"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertContentControl)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT);
+
+ // Then make sure that the matching text attribute is added to the document model:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ // Without the accompanying fix in place, this test would have failed, nothing happened on
+ // InsertContentControl().
+ CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL));
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertCheckboxContentControl)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->InsertContentControl(SwContentControlType::CHECKBOX);
+
+ // Then make sure that the matching text attribute is added to the document model:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ // Without the accompanying fix in place, this test would have failed, the inserted content
+ // control wasn't a checkbox one.
+ CPPUNIT_ASSERT(pContentControl->GetCheckbox());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testSelectDropdownContentControl)
+{
+ // Given a document with a dropdown content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "choose an item", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ {
+ uno::Sequence<beans::PropertyValues> aListItems = {
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("red"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("R"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("green"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("G"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::Any(OUString("blue"))),
+ comphelper::makePropertyValue("Value", uno::Any(OUString("B"))),
+ },
+ };
+ xContentControlProps->setPropertyValue("ListItems", uno::Any(aListItems));
+ }
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // When clicking on that content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ rFormatContentControl.GetContentControl()->SetSelectedListItem(0);
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the document text is updated:
+ // Without the accompanying fix in place, this test would have failed:
+ // - Expected: red
+ // - Actual : choose an item
+ // i.e. the document text was unchanged instead of display text of the first list item.
+ CPPUNIT_ASSERT_EQUAL(OUString("red"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertDropdownContentControl)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
+
+ // Then make sure that the matching text attribute is added to the document model:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ // Without the accompanying fix in place, this test would have failed:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the inserted content control was a default (rich text) one, not a dropdown.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pContentControl->GetListItems().size());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testReplacePictureContentControl)
+{
+ // Given a document with a picture content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<beans::XPropertySet> xTextGraphic(
+ xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ xTextGraphic->setPropertyValue("AnchorType",
+ uno::Any(text::TextContentAnchorType_AS_CHARACTER));
+ uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor, xTextContent, false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::Any(true));
+ xContentControlProps->setPropertyValue("Picture", uno::Any(true));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // When clicking on that content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoObj(/*bNext=*/true, GotoObjFlags::Any);
+ pWrtShell->EnterSelFrameMode();
+ const SwFrameFormat* pFlyFormat = pWrtShell->GetFlyFrameFormat();
+ const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
+ const SwPosition* pAnchorPos = rFormatAnchor.GetContentAnchor();
+ SwTextNode* pTextNode = pAnchorPos->nNode.GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the picture is replaced:
+ CPPUNIT_ASSERT(!rFormatContentControl.GetContentControl()->GetShowingPlaceHolder());
+ // Without the accompanying fix in place, this test would have failed, there was no special
+ // handling for picture content control (how to interact with them), and the default handler
+ // killed the image selection.
+ CPPUNIT_ASSERT(pWrtShell->IsFrameSelected());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertPictureContentControl)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->InsertContentControl(SwContentControlType::PICTURE);
+
+ // Then make sure that the matching text attribute is added to the document model:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ // Without the accompanying fix in place, this test would have failed, there was no special
+ // handling for picture content control, no placeholder fly content was inserted.
+ CPPUNIT_ASSERT(pContentControl->GetPicture());
+ CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(1, RES_TXTATR_FLYCNT));
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testSelectDateContentControl)
+{
+ // Given a document with a date content control:
+ SwDoc* pDoc = createSwDoc();
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "test", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ xContentControlProps->setPropertyValue("Date", uno::Any(true));
+ xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("YYYY-MM-DD")));
+ xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US")));
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // When clicking on that content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ rFormatContentControl.GetContentControl()->SetSelectedDate(44705);
+ pWrtShell->GotoContentControl(rFormatContentControl);
+
+ // Then make sure that the document text is updated:
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2022-05-24
+ // - Actual : test
+ // i.e. the content control was not updated.
+ CPPUNIT_ASSERT_EQUAL(OUString("2022-05-24"), pTextNode->GetExpandText(pWrtShell->GetLayout()));
+ CPPUNIT_ASSERT_EQUAL(OUString("2022-05-24T00:00:00Z"),
+ rFormatContentControl.GetContentControl()->GetCurrentDate());
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertDateContentControl)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a date content control:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->InsertContentControl(SwContentControlType::DATE);
+
+ // Then make sure that the matching text attribute is added to the document model:
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ // Without the accompanying fix in place, this test would have failed, there was no special
+ // handling for date content control.
+ CPPUNIT_ASSERT(pContentControl->GetDate());
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */