1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <swmodeltestbase.hxx>
12 #include <comphelper/processfactory.hxx>
13 #include <osl/file.hxx>
14 #include <comphelper/propertyvalue.hxx>
15 #include <comphelper/scopeguard.hxx>
16 #include <vcl/scheduler.hxx>
17 #include <sfx2/viewfrm.hxx>
18 #include <sfx2/dispatch.hxx>
20 #include <com/sun/star/frame/XDispatchHelper.hpp>
21 #include <com/sun/star/frame/XDispatchProvider.hpp>
22 #include <com/sun/star/frame/XComponentLoader.hpp>
23 #include <com/sun/star/frame/XStorable2.hpp>
24 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
26 #include <com/sun/star/view/XSelectionSupplier.hpp>
27 #include <com/sun/star/text/XParagraphCursor.hpp>
30 #include <unotxdoc.hxx>
33 #include <swmodule.hxx>
36 /// Covers sw/source/uibase/uiview/ fixes.
37 class SwUibaseUiviewTest
: public SwModelTestBase
41 : SwModelTestBase(u
"/sw/qa/uibase/uiview/data/"_ustr
)
46 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testUpdateAllObjectReplacements
)
48 // Make a temporary copy of the test document
49 createTempCopy(u
"updateall-objectreplacements.odt");
51 /* BASIC code that exhibits the problem:
53 desktop = CreateUnoService("com.sun.star.frame.Desktop")
54 Dim props(0) as new com.sun.star.beans.PropertyValue
55 props(0).Name = "Hidden"
57 component = desktop.loadComponentFromURL("file://.../test.odt", "_default", 0, props)
58 Wait 1000 ' workaround
59 dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
60 frame = component.CurrentController.Frame
61 dispatcher.executeDispatch(frame, ".uno:UpdateAll", "", 0, Array())
62 component.storeSelf(Array())
66 uno::Reference
<lang::XMultiServiceFactory
> xFactory(comphelper::getProcessServiceFactory());
69 uno::Reference
<uno::XInterface
> xInterface
70 = xFactory
->createInstance(u
"com.sun.star.frame.Desktop"_ustr
);
71 uno::Reference
<frame::XComponentLoader
> xComponentLoader(xInterface
, uno::UNO_QUERY
);
72 uno::Sequence
<beans::PropertyValue
> aLoadArgs
{ comphelper::makePropertyValue(u
"Hidden"_ustr
,
74 mxComponent
= xComponentLoader
->loadComponentFromURL(maTempFile
.GetURL(), u
"_default"_ustr
, 0,
77 // Perform the .uno:UpdateAll call and save
78 xInterface
= xFactory
->createInstance(u
"com.sun.star.frame.DispatchHelper"_ustr
);
79 uno::Reference
<frame::XDispatchHelper
> xDispatchHelper(xInterface
, uno::UNO_QUERY
);
80 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
81 uno::Reference
<frame::XDispatchProvider
> xDispatchProvider(
82 xModel
->getCurrentController()->getFrame(), uno::UNO_QUERY
);
83 uno::Sequence
<beans::PropertyValue
> aNoArgs
;
84 xDispatchHelper
->executeDispatch(xDispatchProvider
, u
".uno:UpdateAll"_ustr
, OUString(), 0,
86 uno::Reference
<frame::XStorable2
> xStorable(mxComponent
, uno::UNO_QUERY
);
87 xStorable
->storeSelf(aNoArgs
);
89 // Check the contents of the updated copy and verify that ObjectReplacements are there
90 uno::Reference
<packages::zip::XZipFileAccess2
> xNameAccess
91 = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xFactory
),
94 CPPUNIT_ASSERT(xNameAccess
->hasByName(u
"ObjectReplacements/Components"_ustr
));
95 CPPUNIT_ASSERT(xNameAccess
->hasByName(u
"ObjectReplacements/Components_1"_ustr
));
98 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testUpdateReplacementNosetting
)
100 // Load a copy of the document in hidden mode.
101 OUString aSourceURL
= createFileURL(u
"update-replacement-nosetting.odt");
102 CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None
, osl::File::copy(aSourceURL
, maTempFile
.GetURL()));
103 loadWithParams(maTempFile
.GetURL(), { comphelper::makePropertyValue(u
"Hidden"_ustr
, true) });
105 // Update "everything" (including object replacements) and save it.
106 dispatchCommand(mxComponent
, u
".uno:UpdateAll"_ustr
, {});
107 uno::Reference
<frame::XStorable2
> xStorable(mxComponent
, uno::UNO_QUERY
);
108 xStorable
->storeSelf({});
110 // Check the contents of the updated copy.
111 uno::Reference
<uno::XComponentContext
> xContext
= comphelper::getProcessComponentContext();
112 uno::Reference
<packages::zip::XZipFileAccess2
> xNameAccess
113 = packages::zip::ZipFileAccess::createWithURL(xContext
, maTempFile
.GetURL());
115 // Without the accompanying fix in place, this test would have failed, because the embedded
116 // object replacement image was not generated.
117 CPPUNIT_ASSERT(xNameAccess
->hasByName(u
"ObjectReplacements/Components"_ustr
));
120 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testKeepRatio
)
122 // Given a document with a custom KeepRatio:
123 createSwDoc("keep-ratio.fodt");
125 // Then make sure we read the custom value:
126 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
127 const SwViewOption
* pViewOption
= pWrtShell
->GetViewOptions();
128 comphelper::ScopeGuard
g([pWrtShell
, pViewOption
] {
129 SwViewOption
aViewOption(*pViewOption
);
130 aViewOption
.SetKeepRatio(false);
131 SwModule::get()->ApplyUsrPref(aViewOption
, &pWrtShell
->GetView());
133 // Without the accompanying fix in place, this test would have failed, because KeepRatio was not
134 // mapped to settings.xml
135 CPPUNIT_ASSERT(pViewOption
->IsKeepRatio());
137 // Then export as well:
138 save(u
"writer8"_ustr
);
139 xmlDocUniquePtr pXmlDoc
= parseExport(u
"settings.xml"_ustr
);
140 assertXPathContent(pXmlDoc
, "//config:config-item[@config:name='KeepRatio']", u
"true");
145 /// Interception implementation that disables .uno:Zoom on Image1, but not on Image2.
146 struct ImageInterceptor
: public cppu::WeakImplHelper
<frame::XDispatchProviderInterceptor
>
148 uno::Reference
<view::XSelectionSupplier
> m_xSelectionSupplier
;
149 uno::Reference
<frame::XDispatchProvider
> m_xMaster
;
150 uno::Reference
<frame::XDispatchProvider
> m_xSlave
;
155 ImageInterceptor(const uno::Reference
<lang::XComponent
>& xComponent
);
157 // XDispatchProviderInterceptor
158 uno::Reference
<frame::XDispatchProvider
> SAL_CALL
getMasterDispatchProvider() override
;
159 uno::Reference
<frame::XDispatchProvider
> SAL_CALL
getSlaveDispatchProvider() override
;
160 void SAL_CALL
setMasterDispatchProvider(
161 const uno::Reference
<frame::XDispatchProvider
>& xNewSupplier
) override
;
163 setSlaveDispatchProvider(const uno::Reference
<frame::XDispatchProvider
>& xNewSupplier
) override
;
166 uno::Reference
<frame::XDispatch
> SAL_CALL
queryDispatch(const util::URL
& rURL
,
167 const OUString
& rTargetFrameName
,
168 sal_Int32 SearchFlags
) override
;
169 uno::Sequence
<uno::Reference
<frame::XDispatch
>> SAL_CALL
170 queryDispatches(const uno::Sequence
<frame::DispatchDescriptor
>& rRequests
) override
;
174 ImageInterceptor::ImageInterceptor(const uno::Reference
<lang::XComponent
>& xComponent
)
176 uno::Reference
<frame::XModel2
> xModel(xComponent
, uno::UNO_QUERY
);
177 CPPUNIT_ASSERT(xModel
.is());
178 m_xSelectionSupplier
.set(xModel
->getCurrentController(), uno::UNO_QUERY
);
179 CPPUNIT_ASSERT(m_xSelectionSupplier
.is());
182 uno::Reference
<frame::XDispatchProvider
> ImageInterceptor::getMasterDispatchProvider()
187 uno::Reference
<frame::XDispatchProvider
> ImageInterceptor::getSlaveDispatchProvider()
192 void ImageInterceptor::setMasterDispatchProvider(
193 const uno::Reference
<frame::XDispatchProvider
>& xNewSupplier
)
195 m_xMaster
= xNewSupplier
;
198 void ImageInterceptor::setSlaveDispatchProvider(
199 const uno::Reference
<frame::XDispatchProvider
>& xNewSupplier
)
201 m_xSlave
= xNewSupplier
;
204 uno::Reference
<frame::XDispatch
> ImageInterceptor::queryDispatch(const util::URL
& rURL
,
205 const OUString
& rTargetFrameName
,
206 sal_Int32 nSearchFlags
)
208 // Disable the UNO command based on the currently selected image, i.e. this can't be cached when
209 // a different image is selected. Originally this was .uno:SetBorderStyle, but let's pick a
210 // command which is active when running cppunit tests:
211 if (rURL
.Complete
== ".uno:Zoom")
213 uno::Reference
<container::XNamed
> xImage
;
214 m_xSelectionSupplier
->getSelection() >>= xImage
;
215 if (xImage
.is() && xImage
->getName() == "Image1")
224 return m_xSlave
->queryDispatch(rURL
, rTargetFrameName
, nSearchFlags
);
227 uno::Sequence
<uno::Reference
<frame::XDispatch
>>
228 ImageInterceptor::queryDispatches(const uno::Sequence
<frame::DispatchDescriptor
>& /*rRequests*/)
233 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testSwitchBetweenImages
)
235 // Given a document with 2 images, and an interceptor catching an UNO command that specific to
236 // the current selection:
238 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
239 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
240 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
241 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
242 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
243 for (int i
= 0; i
< 2; ++i
)
245 uno::Reference
<beans::XPropertySet
> xTextGraphic(
246 xMSF
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
247 xTextGraphic
->setPropertyValue(u
"AnchorType"_ustr
,
248 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
249 xTextGraphic
->setPropertyValue(u
"Size"_ustr
, uno::Any(awt::Size(5000, 5000)));
250 uno::Reference
<text::XTextContent
> xTextContent(xTextGraphic
, uno::UNO_QUERY
);
251 xText
->insertTextContent(xCursor
, xTextContent
, false);
253 pWrtShell
->SttEndDoc(/*bStt=*/false);
254 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
255 uno::Reference
<frame::XDispatchProviderInterception
> xRegistration(
256 xModel
->getCurrentController()->getFrame(), uno::UNO_QUERY
);
257 rtl::Reference
pInterceptor(new ImageInterceptor(mxComponent
));
259 xRegistration
->registerDispatchProviderInterceptor(pInterceptor
);
260 pInterceptor
->m_nEnabled
= 0;
261 pInterceptor
->m_nDisabled
= 0;
263 // When selecting the first image:
266 // Then make sure the UNO command is disabled:
267 CPPUNIT_ASSERT_EQUAL(0, pInterceptor
->m_nEnabled
);
268 CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor
->m_nDisabled
);
270 // Given a clean state:
271 pInterceptor
->m_nEnabled
= 0;
272 pInterceptor
->m_nDisabled
= 0;
274 // When selecting the second image:
277 // Then make sure the UNO command is enabled:
278 CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor
->m_nEnabled
);
279 CPPUNIT_ASSERT_EQUAL(0, pInterceptor
->m_nDisabled
);
281 // Given a clean state:
282 pInterceptor
->m_nEnabled
= 0;
283 pInterceptor
->m_nDisabled
= 0;
285 // When selecting the first image, again (this time not changing the selection type):
288 // Then make sure the UNO command is disabled:
289 CPPUNIT_ASSERT_EQUAL(0, pInterceptor
->m_nEnabled
);
290 // Without the accompanying fix in place, this test would have failed with:
291 // - Expected greater or equal than: 1
293 // i.e. selecting the first image didn't result in a disabled UNO command.
294 CPPUNIT_ASSERT_GREATEREQUAL(1, pInterceptor
->m_nDisabled
);
297 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testPrintPreview
)
299 // Given a normal Writer view, in half-destroyed state, similar to what
300 // SfxViewFrame::SwitchToViewShell_Impl() does in practice:
302 SwDocShell
* pDocShell
= getSwDocShell();
303 SwView
* pView
= pDocShell
->GetView();
304 FmFormShell
* pFormShell
= pView
->GetFormShell();
305 pView
->SetFormShell(reinterpret_cast<FmFormShell
*>(-1));
308 // When selecting a shell, similar to what happens the doc size changes:
309 // Then make sure we don't crash:
310 pView
->SelectShell();
312 // Restore the state and shut down.
313 pView
->SetFormShell(pFormShell
);
316 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, TestTdf152839_Formtext
)
318 createSwDoc("tdf152839_formtext.rtf");
320 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
322 = getXPath(pXmlDoc
, "/root/page[1]/body/tab[1]/row[2]/cell[1]/txt/infos/bounds", "height")
324 CPPUNIT_ASSERT_EQUAL(sal_Int32(723), nHeight
);
327 CPPUNIT_TEST_FIXTURE(SwUibaseUiviewTest
, testEditInReadonly
)
329 createSwDoc("editinsection.odt");
331 SwDocShell
* pDocShell
= getSwDocShell();
332 SwView
* pView
= pDocShell
->GetView();
334 pView
->GetViewFrame().GetDispatcher()->Execute(SID_EDITDOC
, SfxCallMode::SYNCHRON
);
336 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
337 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
338 uno::Reference
<text::XParagraphCursor
> xParaCursor(xTextDocument
->getText()->createTextCursor(),
341 uno::Reference
<view::XSelectionSupplier
> xSelSupplier(xModel
->getCurrentController(),
342 uno::UNO_QUERY_THROW
);
344 xSelSupplier
->select(css::uno::Any(xParaCursor
));
345 std::unique_ptr
<SfxPoolItem
> pItem
;
346 SfxItemState eState
= pView
->GetViewFrame().GetBindings().QueryState(FN_INSERT_TABLE
, pItem
);
347 //status disabled in read only content
348 CPPUNIT_ASSERT_EQUAL(SfxItemState::DISABLED
, eState
);
350 //move cursor to section
351 xParaCursor
->gotoNextParagraph(false);
352 xSelSupplier
->select(css::uno::Any(xParaCursor
));
353 eState
= pView
->GetViewFrame().GetBindings().QueryState(FN_INSERT_TABLE
, pItem
);
354 //status default in editable section
355 CPPUNIT_ASSERT_EQUAL(SfxItemState::DEFAULT
, eState
);
357 CPPUNIT_PLUGIN_IMPLEMENT();
359 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */