diff options
Diffstat (limited to '')
94 files changed, 21969 insertions, 0 deletions
diff --git a/embeddedobj/CppunitTest_embeddedobj_general.mk b/embeddedobj/CppunitTest_embeddedobj_general.mk new file mode 100644 index 000000000..32fea16fb --- /dev/null +++ b/embeddedobj/CppunitTest_embeddedobj_general.mk @@ -0,0 +1,44 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# 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/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,embeddedobj_general)) + +$(eval $(call gb_CppunitTest_use_externals,embeddedobj_general,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,embeddedobj_general, \ + embeddedobj/qa/cppunit/general \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,embeddedobj_general, \ + comphelper \ + cppu \ + embobj \ + sal \ + test \ + unotest \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,embeddedobj_general)) + +$(eval $(call gb_CppunitTest_use_ure,embeddedobj_general)) +$(eval $(call gb_CppunitTest_use_vcl,embeddedobj_general)) + +$(eval $(call gb_CppunitTest_use_rdb,embeddedobj_general,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,embeddedobj_general,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,embeddedobj_general)) + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/CppunitTest_embeddedobj_msole.mk b/embeddedobj/CppunitTest_embeddedobj_msole.mk new file mode 100644 index 000000000..2ffb7f64d --- /dev/null +++ b/embeddedobj/CppunitTest_embeddedobj_msole.mk @@ -0,0 +1,48 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# 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/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,embeddedobj_msole)) + +$(eval $(call gb_CppunitTest_use_externals,embeddedobj_msole,\ + boost_headers \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,embeddedobj_msole, \ + embeddedobj/qa/cppunit/msole \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,embeddedobj_msole, \ + comphelper \ + cppu \ + embobj \ + sal \ + test \ + unotest \ + utl \ + vcl \ + tl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,embeddedobj_msole)) + +$(eval $(call gb_CppunitTest_use_ure,embeddedobj_msole)) +$(eval $(call gb_CppunitTest_use_vcl,embeddedobj_msole)) + +$(eval $(call gb_CppunitTest_use_rdb,embeddedobj_msole,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,embeddedobj_msole,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,embeddedobj_msole)) + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/IwyuFilter_embeddedobj.yaml b/embeddedobj/IwyuFilter_embeddedobj.yaml new file mode 100644 index 000000000..cee5248e9 --- /dev/null +++ b/embeddedobj/IwyuFilter_embeddedobj.yaml @@ -0,0 +1,35 @@ +--- +assumeFilename: embeddedobj/source/commonembedding/embedobj.cxx +excludelist: + embeddedobj/qa/cppunit/general.cxx: + - comphelper/scopeguard.hxx + embeddedobj/source/inc/dummyobject.hxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + embeddedobj/source/inc/oleembobj.hxx: + # Needed on WIN + - osl/thread.h + embeddedobj/source/msole/olevisual.cxx: + # Needed on WIN + - com/sun/star/embed/EmbedMisc.hpp + - comphelper/seqstream.hxx + - filter/msfilter/classids.hxx + embeddedobj/source/msole/oleembed.cxx: + # Needed on WIN + - com/sun/star/embed/NeedsRunningStateException.hpp + - com/sun/star/embed/StateChangeInProgressException.hpp + - targetstatecontrol.hxx + embeddedobj/source/msole/olemisc.cxx: + # Needed on WIN + - com/sun/star/embed/EmbedStates.hpp + embeddedobj/source/msole/olepersist.cxx: + # Needed on WIN + - com/sun/star/embed/EmbedVerbs.hpp + - osl/thread.hxx + - closepreventer.hxx + embeddedobj/source/commonembedding/embedobj.cxx: + # Don't propose hxx -> h change in URE libs + - cppuhelper/interfacecontainer.hxx + embeddedobj/source/msole/xolefactory.cxx: + # Actually used + - com/sun/star/beans/PropertyValue.hpp diff --git a/embeddedobj/Library_embobj.mk b/embeddedobj/Library_embobj.mk new file mode 100644 index 000000000..18cf55518 --- /dev/null +++ b/embeddedobj/Library_embobj.mk @@ -0,0 +1,58 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# +# 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/. +# + +$(eval $(call gb_Library_Library,embobj)) + +$(eval $(call gb_Library_use_custom_headers,embobj,\ + officecfg/registry \ +)) + +$(eval $(call gb_Library_set_componentfile,embobj,embeddedobj/util/embobj,services)) + +$(eval $(call gb_Library_set_include,embobj,\ + -I$(SRCDIR)/embeddedobj/source/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_add_defs,embobj,\ + -DEMBOBJ_DLLIMPLEMENTATION \ +)) + +$(eval $(call gb_Library_use_external,embobj,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,embobj)) + +$(eval $(call gb_Library_use_libraries,embobj,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + svt \ + utl \ + vcl \ + tl \ + i18nlangtag \ +)) + +$(eval $(call gb_Library_add_exception_objects,embobj,\ + embeddedobj/source/commonembedding/embedobj \ + embeddedobj/source/commonembedding/inplaceobj \ + embeddedobj/source/commonembedding/miscobj \ + embeddedobj/source/commonembedding/persistence \ + embeddedobj/source/commonembedding/specialobject \ + embeddedobj/source/commonembedding/visobj \ + embeddedobj/source/commonembedding/xfactory \ + embeddedobj/source/general/docholder \ + embeddedobj/source/general/dummyobject \ + embeddedobj/source/general/intercept \ + embeddedobj/source/general/xcreator \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/Library_emboleobj.mk b/embeddedobj/Library_emboleobj.mk new file mode 100644 index 000000000..b7fde953c --- /dev/null +++ b/embeddedobj/Library_emboleobj.mk @@ -0,0 +1,67 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# +# 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/. +# + +$(eval $(call gb_Library_Library,emboleobj)) + +$(eval $(call gb_Library_set_componentfile,emboleobj,embeddedobj/source/msole/emboleobj$(if $(filter WNT,$(OS)),.windows),services)) + +$(eval $(call gb_Library_set_include,emboleobj,\ + -I$(SRCDIR)/embeddedobj/source/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_use_external,emboleobj,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,emboleobj)) + +$(eval $(call gb_Library_use_libraries,emboleobj,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + utl \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,emboleobj,\ + embeddedobj/source/msole/closepreventer \ + embeddedobj/source/msole/oleembed \ + embeddedobj/source/msole/olemisc \ + embeddedobj/source/msole/olepersist \ + embeddedobj/source/msole/olevisual \ + embeddedobj/source/msole/ownview \ + embeddedobj/source/msole/xolefactory \ +)) + +ifeq ($(OS),WNT) + +$(eval $(call gb_Library_use_libraries,emboleobj,\ + embobj \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,emboleobj,\ + gdi32 \ + ole32 \ + oleaut32 \ + uuid \ +)) + +$(eval $(call gb_Library_add_exception_objects,emboleobj,\ + embeddedobj/source/msole/advisesink \ + embeddedobj/source/msole/graphconvert \ + embeddedobj/source/msole/olecomponent \ + embeddedobj/source/msole/olewrapclient \ + embeddedobj/source/msole/xdialogcreator \ +)) + +endif + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/Makefile b/embeddedobj/Makefile new file mode 100644 index 000000000..0997e6284 --- /dev/null +++ b/embeddedobj/Makefile @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/Module_embeddedobj.mk b/embeddedobj/Module_embeddedobj.mk new file mode 100644 index 000000000..659b406b9 --- /dev/null +++ b/embeddedobj/Module_embeddedobj.mk @@ -0,0 +1,30 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# +# 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/. +# + +$(eval $(call gb_Module_Module,embeddedobj)) + +$(eval $(call gb_Module_add_targets,embeddedobj,\ + Library_embobj \ + Library_emboleobj \ +)) + +ifeq ($(OS),LINUX) +$(eval $(call gb_Module_add_slowcheck_targets,embeddedobj,\ + CppunitTest_embeddedobj_general \ +)) +endif + +ifeq ($(OS),WNT) +$(eval $(call gb_Module_add_slowcheck_targets,embeddedobj,\ + CppunitTest_embeddedobj_msole \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/embeddedobj/README.md b/embeddedobj/README.md new file mode 100644 index 000000000..247ef1fda --- /dev/null +++ b/embeddedobj/README.md @@ -0,0 +1,3 @@ +# Embedding Objects Into LibreOffice + +Code for embedding objects into LibreOffice (reverse of `embedserv` module). diff --git a/embeddedobj/qa/cppunit/data/insert-file-config.doc b/embeddedobj/qa/cppunit/data/insert-file-config.doc Binary files differnew file mode 100644 index 000000000..182a70c34 --- /dev/null +++ b/embeddedobj/qa/cppunit/data/insert-file-config.doc diff --git a/embeddedobj/qa/cppunit/data/insert-file-config.pdf b/embeddedobj/qa/cppunit/data/insert-file-config.pdf Binary files differnew file mode 100644 index 000000000..630245740 --- /dev/null +++ b/embeddedobj/qa/cppunit/data/insert-file-config.pdf diff --git a/embeddedobj/qa/cppunit/data/insert-file-config.vsdx b/embeddedobj/qa/cppunit/data/insert-file-config.vsdx Binary files differnew file mode 100644 index 000000000..bb3a129cf --- /dev/null +++ b/embeddedobj/qa/cppunit/data/insert-file-config.vsdx diff --git a/embeddedobj/qa/cppunit/data/ole2.ole b/embeddedobj/qa/cppunit/data/ole2.ole new file mode 100644 index 000000000..c0013db40 --- /dev/null +++ b/embeddedobj/qa/cppunit/data/ole2.ole @@ -0,0 +1,19 @@ +{\object\objemb\objw240\objh240{\*\objclass PBrush}{\*\objdata 01050000020000000700000050427275736800000000000000000040030000 +424d36030000000000003600000028000000100000001000000001001800000000000003000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000001050000050000000d0000004d45544146494c455049435400a701000059feffffe40000000800a701a7010000 +0100090000036e00000000004500000000000400000003010800050000000b0200000000050000000c0211001100030000001e000400000007010400040000000701040045000000410b2000cc00100010000000000010001000000000002800000010000000100000000100010000000000000000000000000000000000 +000000000000000000000000ffffff00ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101040000002701ffff030000000000}{\result {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid793696 +{\*\shppict{\pict{\*\picprop\shplid1027{\sp{\sn shapeType}{\sv 75}}{\sp{\sn fFlipH}{\sv 0}}{\sp{\sn fFlipV}{\sv 0}}{\sp{\sn fLockAspectRatio}{\sv 1}}{\sp{\sn pictureGray}{\sv 0}} +{\sp{\sn pictureBiLevel}{\sv 0}}{\sp{\sn pictureActive}{\sv 0}}{\sp{\sn fRecolorFillAsPicture}{\sv 0}}{\sp{\sn fUseShapeAnchor}{\sv 0}}{\sp{\sn fFilled}{\sv 0}}{\sp{\sn fHitTestFill}{\sv 1}} +{\sp{\sn fillShape}{\sv 1}}{\sp{\sn fillUseRect}{\sv 0}}{\sp{\sn fNoFillHitTest}{\sv 0}}{\sp{\sn fLine}{\sv 0}}{\sp{\sn fPreferRelativeResize}{\sv 1}}{\sp{\sn fReallyHidden}{\sv 0}} +{\sp{\sn fScriptAnchor}{\sv 0}}{\sp{\sn fFakeMaster}{\sv 0}}{\sp{\sn fCameFromImgDummy}{\sv 0}}{\sp{\sn fLayoutInCell}{\sv 1}}}\picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0 +\picw423\pich423\picwgoal240\pichgoal240\pngblip\bliptag602933164{\*\blipuid 23f007ac3ac2aed53753eaea27c13e03}89504e470d0a1a0a0000000d494844520000001000000010080200000090916836000000017352474200aece1ce9000000097048597300000ec700000ec70138 +922f760000001f49444154384f63fcffff3f0329808914c520b5a31a8809b1d1501a1ca10400556d031d6ec895ac0000000049454e44ae426082}}{\nonshppict{\pict\picscalex100\picscaley100\piccropl0\piccropr0\piccropt0\piccropb0 +\picw423\pich423\picwgoal240\pichgoal240\wmetafile8\bliptag602933164\blipupi96{\*\blipuid 23f007ac3ac2aed53753eaea27c13e03}0100090000036e00000000004500000000000400000003010800050000000b0200000000050000000c0211001100030000001e00040000000701040004000000 +0701040045000000410b2000cc001000100000000000100010000000000028000000100000001000000001000100000000000000000000000000000000000000 +00000000000000000000ffffff00ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101ffff0101040000002701ffff030000000000}}}}} diff --git a/embeddedobj/qa/cppunit/data/ole2.png b/embeddedobj/qa/cppunit/data/ole2.png Binary files differnew file mode 100644 index 000000000..fdad35484 --- /dev/null +++ b/embeddedobj/qa/cppunit/data/ole2.png diff --git a/embeddedobj/qa/cppunit/data/reqif-ole2.xhtml b/embeddedobj/qa/cppunit/data/reqif-ole2.xhtml new file mode 100644 index 000000000..716ecd1bd --- /dev/null +++ b/embeddedobj/qa/cppunit/data/reqif-ole2.xhtml @@ -0,0 +1,5 @@ +<reqif-xhtml:div> + <reqif-xhtml:object data="ole2.ole" type="text/rtf"> + <reqif-xhtml:object data="ole2.png" type="image/png">OLE Object</reqif-xhtml:object> + </reqif-xhtml:object> +</reqif-xhtml:div> diff --git a/embeddedobj/qa/cppunit/general.cxx b/embeddedobj/qa/cppunit/general.cxx new file mode 100644 index 000000000..c7824fe87 --- /dev/null +++ b/embeddedobj/qa/cppunit/general.cxx @@ -0,0 +1,156 @@ +/* -*- 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 <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/embed/XStorage.hpp> + +#include <comphelper/embeddedobjectcontainer.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/scopeguard.hxx> +#include <comphelper/storagehelper.hxx> +#include <officecfg/Office/Common.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers embeddedobj/source/general/ fixes. +class Test : public test::BootstrapFixture, public unotest::MacrosTest +{ +private: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } +}; + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} +} + +CPPUNIT_TEST_FIXTURE(Test, testInsertFileConfig) +{ + // Explicitly disable Word->Writer mapping for this test. + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::set(false, pBatch); + pBatch->commit(); + comphelper::ScopeGuard g([]() { + std::shared_ptr<comphelper::ConfigurationChanges> pBatchReset( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::set(true, + pBatchReset); + pBatchReset->commit(); + }); + getComponent().set( + loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // Insert a file as an embedded object. + uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + comphelper::EmbeddedObjectContainer aContainer(xStorage); + OUString aFileName + = m_directories.getURLFromSrc(u"embeddedobj/qa/cppunit/data/insert-file-config.doc"); + uno::Sequence<beans::PropertyValue> aMedium{ comphelper::makePropertyValue("URL", aFileName) }; + OUString aName("Object 1"); + uno::Reference<embed::XEmbeddedObject> xObject + = aContainer.InsertEmbeddedObject(aMedium, aName); + + // Make sure that the insertion fails: + // 1) the user explicitly requested that the data is not loaded into Writer + // 2) this is non-Windows, so OLE embedding is not an option + // so silently still loading the data into Writer would be bad. + CPPUNIT_ASSERT(!xObject.is()); +} + +CPPUNIT_TEST_FIXTURE(Test, testInsertFileConfigVsdx) +{ + // Explicitly disable Word->Writer mapping for this test. + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Microsoft::Import::VisioToDraw::set(false, pBatch); + pBatch->commit(); + comphelper::ScopeGuard g([]() { + std::shared_ptr<comphelper::ConfigurationChanges> pBatchReset( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Microsoft::Import::VisioToDraw::set(true, pBatchReset); + pBatchReset->commit(); + }); + getComponent().set( + loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // Insert a file as an embedded object. + uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + comphelper::EmbeddedObjectContainer aContainer(xStorage); + OUString aFileName + = m_directories.getURLFromSrc(u"embeddedobj/qa/cppunit/data/insert-file-config.vsdx"); + uno::Sequence<beans::PropertyValue> aMedium{ comphelper::makePropertyValue("URL", aFileName) }; + OUString aName("Object 1"); + uno::Reference<embed::XEmbeddedObject> xObject + = aContainer.InsertEmbeddedObject(aMedium, aName); + + // Make sure that the insertion fails: + // 1) the user explicitly requested that the data is not loaded into Writer + // 2) this is non-Windows, so OLE embedding is not an option + // so silently still loading the data into Writer would be bad. + CPPUNIT_ASSERT(!xObject.is()); +} + +CPPUNIT_TEST_FIXTURE(Test, testInsertFileConfigPdf) +{ + // Explicitly disable Word->Writer mapping for this test. + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Adobe::Import::PDFToDraw::set(false, pBatch); + pBatch->commit(); + comphelper::ScopeGuard g([]() { + std::shared_ptr<comphelper::ConfigurationChanges> pBatchReset( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Filter::Adobe::Import::PDFToDraw::set(true, pBatchReset); + pBatchReset->commit(); + }); + getComponent().set( + loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // Insert a PDF file as an embedded object. + uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + comphelper::EmbeddedObjectContainer aContainer(xStorage); + OUString aFileName + = m_directories.getURLFromSrc(u"embeddedobj/qa/cppunit/data/insert-file-config.pdf"); + uno::Sequence<beans::PropertyValue> aMedium{ comphelper::makePropertyValue("URL", aFileName) }; + OUString aName("Object 1"); + uno::Reference<embed::XEmbeddedObject> xObject + = aContainer.InsertEmbeddedObject(aMedium, aName); + + // Make sure that the insertion fails: + // 1) the user explicitly requested that the data is not loaded into Writer + // 2) this is non-Windows, so OLE embedding is not an option + // so silently still loading the data into Writer would be bad. + CPPUNIT_ASSERT(!xObject.is()); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/qa/cppunit/msole.cxx b/embeddedobj/qa/cppunit/msole.cxx new file mode 100644 index 000000000..85be38938 --- /dev/null +++ b/embeddedobj/qa/cppunit/msole.cxx @@ -0,0 +1,145 @@ +/* -*- 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 <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> +#include <test/xmltesttools.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/packages/zip/ZipFileAccess.hpp> + +#include <comphelper/embeddedobjectcontainer.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/scopeguard.hxx> +#include <comphelper/storagehelper.hxx> +#include <officecfg/Office/Common.hxx> +#include <unotools/tempfile.hxx> +#include <osl/thread.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/outdev.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers embeddedobj/source/msole/ fixes. +class Test : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools +{ +private: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } + void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; +}; +} + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +void Test::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) +{ + XmlTestTools::registerODFNamespaces(pXmlXpathCtx); +} + +namespace +{ +class OdtExportThread : public osl::Thread +{ + uno::Reference<lang::XComponent> mxComponent; + OUString maURL; + +public: + OdtExportThread(const uno::Reference<lang::XComponent>& xComponent, const OUString& rURL); + virtual void SAL_CALL run() override; +}; + +OdtExportThread::OdtExportThread(const uno::Reference<lang::XComponent>& xComponent, + const OUString& rURL) + : mxComponent(xComponent) + , maURL(rURL) +{ +} + +void OdtExportThread::run() +{ + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + uno::Sequence<beans::PropertyValue> aStoreProperties = { + comphelper::makePropertyValue("FilterName", OUString("writer8")), + }; + xStorable->storeToURL(maURL, aStoreProperties); +} +} + +CPPUNIT_TEST_FIXTURE(Test, testSaveOnThread) +{ + // Given an embedded object which hosts mspaint data: + if (Application::GetDefaultDevice()->GetDPIX() != 96) + { + return; + } + + DBG_TESTSOLARMUTEX(); + OUString aURL = m_directories.getURLFromSrc(u"embeddedobj/qa/cppunit/data/reqif-ole2.xhtml"); + uno::Sequence<beans::PropertyValue> aLoadProperties = { + comphelper::makePropertyValue("FilterName", OUString("HTML (StarWriter)")), + comphelper::makePropertyValue("FilterOptions", OUString("xhtmlns=reqif-xhtml")), + }; + getComponent().set(loadFromDesktop(aURL, "com.sun.star.text.TextDocument", aLoadProperties)); + + // When saving that document on a thread: + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + OdtExportThread aThread(getComponent(), aTempFile.GetURL()); + aThread.create(); + { + SolarMutexReleaser r; + while (aThread.isRunning()) + { + SolarMutexGuard g; + Application::Reschedule(/*bHandleAllCurrentEvents=*/true); + } + } + + // Then make sure its visible area's width is correct. + uno::Reference<packages::zip::XZipFileAccess2> xNameAccess + = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, aTempFile.GetURL()); + uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("content.xml"), + uno::UNO_QUERY); + std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true)); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // 16 pixels, assuming 96 DPI. + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0.1665in + // - Actual : 1.9685in + // i.e. we wrote a hardcoded 5cm width, not the real one. + assertXPath(pXmlDoc, "//style:graphic-properties", "visible-area-width", "0.1665in"); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/qa/embedding/EmbeddingTest.java b/embeddedobj/qa/embedding/EmbeddingTest.java new file mode 100644 index 000000000..c2ab6faf0 --- /dev/null +++ b/embeddedobj/qa/embedding/EmbeddingTest.java @@ -0,0 +1,25 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.embedding; + +public interface EmbeddingTest +{ + boolean test(); +} + diff --git a/embeddedobj/qa/embedding/EmbeddingUnitTest.java b/embeddedobj/qa/embedding/EmbeddingUnitTest.java new file mode 100644 index 000000000..245dd1225 --- /dev/null +++ b/embeddedobj/qa/embedding/EmbeddingUnitTest.java @@ -0,0 +1,82 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package complex.embedding; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.connection.XConnector; +import com.sun.star.connection.XConnection; + +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.XNamingService; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.container.*; +import com.sun.star.beans.*; +import com.sun.star.lang.*; + +import complexlib.ComplexTestCase; + +import complex.embedding.*; + +import util.utils; +import java.util.*; +import java.io.*; + +/* This unit test for storage objects is designed to + * test most important statements from storage service + * specification. + * + * Regression tests are added to extend the tested + * functionalities. + */ +public class EmbeddingUnitTest extends ComplexTestCase +{ + private XMultiServiceFactory m_xMSF = null; + + public String[] getTestMethodNames() + { + return new String[] { + "ExecuteTest01" }; + } + + public String getTestObjectName() + { + return "EmbeddingUnitTest"; + } + + public void before() + { + m_xMSF = (XMultiServiceFactory)param.getMSF(); + if ( m_xMSF == null ) + { + failed( "Can't create service factory!" ); + return; + } + } + + public void ExecuteTest01() + { + EmbeddingTest aTest = new Test01( m_xMSF, log ); + assure( "Test01 failed!", aTest.test() ); + } + +} + diff --git a/embeddedobj/qa/embedding/Test01.java b/embeddedobj/qa/embedding/Test01.java new file mode 100644 index 000000000..3a8b425bb --- /dev/null +++ b/embeddedobj/qa/embedding/Test01.java @@ -0,0 +1,153 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.embedding; + +import com.sun.star.uno.XInterface; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.beans.XPropertySet; +import com.sun.star.frame.XLoadable; +import com.sun.star.drawing.XDrawPagesSupplier; +import com.sun.star.drawing.XDrawPages; +import com.sun.star.drawing.XDrawPage; +import com.sun.star.drawing.XShape; +import com.sun.star.graphic.XGraphic; + +import com.sun.star.embed.*; + +import share.LogWriter; +import complex.embedding.TestHelper; +import complex.embedding.EmbeddingTest; + +public class Test01 implements EmbeddingTest { + + XMultiServiceFactory m_xMSF; + TestHelper m_aTestHelper; + + public Test01( XMultiServiceFactory xMSF, LogWriter aLogWriter ) + { + m_xMSF = xMSF; + m_aTestHelper = new TestHelper( aLogWriter, "Test01: " ); + } + + public boolean test() + { + try + { + Object oDoc = m_xMSF.createInstance( "com.sun.star.comp.Draw.DrawingDocument" ); + XLoadable xLoad = (XLoadable) UnoRuntime.queryInterface( XLoadable.class, oDoc ); + if ( xLoad == null ) + { + m_aTestHelper.Error( "Can not get XLoadable!" ); + return false; + } + + xLoad.initNew(); + + XDrawPagesSupplier xDPSupply = (XDrawPagesSupplier) UnoRuntime.queryInterface( XDrawPagesSupplier.class, oDoc ); + if ( xDPSupply == null ) + { + m_aTestHelper.Error( "Can not get XDrawPagesSupplier!" ); + return false; + } + + XDrawPages xDrawPages = xDPSupply.getDrawPages(); + if ( xDrawPages == null ) + { + m_aTestHelper.Error( "Can not get XDrawPages object!" ); + return false; + } + + if ( xDrawPages.getCount() == 0 ) + { + m_aTestHelper.Error( "There must be at least one page in the document!" ); + return false; + } + + Object oPage = xDrawPages.getByIndex( 0 ); + XDrawPage xPage = (XDrawPage) UnoRuntime.queryInterface( XDrawPage.class, oPage ); + if ( xPage == null ) + { + m_aTestHelper.Error( "Can not get access to drawing page!" ); + return false; + } + + XMultiServiceFactory xDrFactory = ( XMultiServiceFactory ) UnoRuntime.queryInterface( XMultiServiceFactory.class, + oDoc ); + if ( xDrFactory == null ) + { + m_aTestHelper.Error( "Can not get drawing factory!" ); + return false; + } + + Object oShape = xDrFactory.createInstance( "com.sun.star.drawing.OLE2Shape" ); + XShape xShape = ( XShape ) UnoRuntime.queryInterface( XShape.class, oShape ); + if ( xShape == null ) + { + m_aTestHelper.Error( "Can not create new shape!" ); + return false; + } + + XPropertySet xShapeProps = ( XPropertySet ) UnoRuntime.queryInterface( XPropertySet.class, oShape ); + if ( xShapeProps == null ) + { + m_aTestHelper.Error( "Can not get access to shapes properties!" ); + return false; + } + + xPage.add( xShape ); + xShapeProps.setPropertyValue( "CLSID", "078B7ABA-54FC-457F-8551-6147e776a997" ); + + Object oEmbObj = xShapeProps.getPropertyValue( "EmbeddedObject" ); + XEmbeddedObject xEmbObj = ( XEmbeddedObject ) UnoRuntime.queryInterface( XEmbeddedObject.class, oEmbObj ); + if ( xEmbObj == null ) + { + m_aTestHelper.Error( "Embedded object can not be accessed!" ); + return false; + } + + XEmbeddedClient xClient = xEmbObj.getClientSite(); + if ( xClient == null ) + { + m_aTestHelper.Error( "The client for the object must be set!" ); + return false; + } + + Object oReplacement = xShapeProps.getPropertyValue( "Graphic" ); + XGraphic xReplGraph = ( XGraphic ) UnoRuntime.queryInterface( XGraphic.class, oReplacement ); + if ( xReplGraph == null ) + { + m_aTestHelper.Error( "The replacement graphic should be available!" ); + return false; + } + + return true; + } + catch( Exception e ) + { + m_aTestHelper.Error( "Exception: " + e ); + return false; + } + } +} + diff --git a/embeddedobj/qa/embedding/TestHelper.java b/embeddedobj/qa/embedding/TestHelper.java new file mode 100644 index 000000000..299311171 --- /dev/null +++ b/embeddedobj/qa/embedding/TestHelper.java @@ -0,0 +1,54 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.embedding; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.AnyConverter; + +import com.sun.star.lang.*; +import com.sun.star.embed.*; +import com.sun.star.packages.*; +import com.sun.star.io.*; +import com.sun.star.beans.*; + +import share.LogWriter; + +public class TestHelper { + + LogWriter m_aLogWriter; + String m_sTestPrefix; + + public TestHelper( LogWriter aLogWriter, String sTestPrefix ) + { + m_aLogWriter = aLogWriter; + m_sTestPrefix = sTestPrefix; + } + + public void Error( String sError ) + { + m_aLogWriter.println( m_sTestPrefix + "Error: " + sError ); + } + + public void Message( String sMessage ) + { + m_aLogWriter.println( m_sTestPrefix + sMessage ); + } +} + diff --git a/embeddedobj/qa/embedding/makefile.mk b/embeddedobj/qa/embedding/makefile.mk new file mode 100644 index 000000000..483440c22 --- /dev/null +++ b/embeddedobj/qa/embedding/makefile.mk @@ -0,0 +1,75 @@ +# +# 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ = ..$/.. +TARGET = EmbeddingUnitTest +PRJNAME = embeddedobj +PACKAGE = complex$/embedding + +# --- Settings ----------------------------------------------------- +.INCLUDE: settings.mk + + +#----- compile .java files ----------------------------------------- + +JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar OOoRunner.jar + +JAVAFILES =\ + EmbeddingUnitTest.java\ + EmbeddingTest.java\ + TestHelper.java\ + Test01.java + +JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class) + +#----- make a jar from compiled files ------------------------------ + +MAXLINELENGTH = 100000 + +JARCLASSDIRS = $(PACKAGE) +JARTARGET = $(TARGET).jar +JARCOMPRESS = TRUE + +# --- Parameters for the test -------------------------------------- + +# start an office if the parameter is set for the makefile +.IF "$(OFFICE)" == "" +CT_APPEXECCOMMAND = +.ELSE +CT_APPEXECCOMMAND = -AppExecutionCommand "$(OFFICE)$/soffice --accept=socket,host=localhost,port=8100;urp;" +.ENDIF + +# test base is java complex +CT_TESTBASE = -TestBase java_complex + +# test looks something like the.full.package.TestName +CT_TEST = -o $(PACKAGE:s\$/\.\).$(JAVAFILES:b) + +# start the runner application +CT_APP = org.openoffice.Runner + +# --- Targets ------------------------------------------------------ + +.INCLUDE: target.mk + +RUN: run + +run: + java -cp $(CLASSPATH) $(CT_APP) $(CT_TESTBASE) $(CT_APPEXECCOMMAND) $(CT_TEST) + + diff --git a/embeddedobj/source/commonembedding/embedobj.cxx b/embeddedobj/source/commonembedding/embedobj.cxx new file mode 100644 index 000000000..ef6279419 --- /dev/null +++ b/embeddedobj/source/commonembedding/embedobj.cxx @@ -0,0 +1,732 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/ObjectSaveVetoException.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/XEmbeddedClient.hpp> +#include <com/sun/star/embed/XInplaceClient.hpp> +#include <com/sun/star/embed/XWindowSupplier.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> +#include <com/sun/star/embed/Aspects.hpp> + +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/lok.hxx> +#include <sal/log.hxx> + +#include <vcl/svapp.hxx> + +#include <targetstatecontrol.hxx> + +#include <commonembobj.hxx> +#include "embedobj.hxx" +#include <specialobject.hxx> +#include <array> + +using namespace ::com::sun::star; + +awt::Rectangle GetRectangleInterception( const awt::Rectangle& aRect1, const awt::Rectangle& aRect2 ) +{ + awt::Rectangle aResult; + + OSL_ENSURE( aRect1.Width >= 0 && aRect2.Width >= 0 && aRect1.Height >= 0 && aRect2.Height >= 0, + "Offset must not be less than zero!" ); + + aResult.X = std::max(aRect1.X, aRect2.X); + aResult.Y = std::max(aRect1.Y, aRect2.Y); + + sal_Int32 nRight1 = aRect1.X + aRect1.Width; + sal_Int32 nBottom1 = aRect1.Y + aRect1.Height; + sal_Int32 nRight2 = aRect2.X + aRect2.Width; + sal_Int32 nBottom2 = aRect2.Y + aRect2.Height; + aResult.Width = std::min( nRight1, nRight2 ) - aResult.X; + aResult.Height = std::min( nBottom1, nBottom2 ) - aResult.Y; + + return aResult; +} + +namespace +{ + using IntermediateStatesMap = std::array<std::array<uno::Sequence< sal_Int32 >, NUM_SUPPORTED_STATES>, NUM_SUPPORTED_STATES>; + const IntermediateStatesMap & getIntermediateStatesMap() + { + static const IntermediateStatesMap map = [] () { + IntermediateStatesMap tmp; + + // intermediate states + // In the following table the first index points to starting state, + // the second one to the target state, and the sequence referenced by + // first two indexes contains intermediate states, that should be + // passed by object to reach the target state. + // If the sequence is empty that means that indirect switch from start + // state to the target state is forbidden, only if direct switch is possible + // the state can be reached. + + tmp[0][2] = { embed::EmbedStates::RUNNING }; + + tmp[0][3] = { embed::EmbedStates::RUNNING, + embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[0][4] = {embed::EmbedStates::RUNNING}; + + tmp[1][3] = { embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[2][0] = { embed::EmbedStates::RUNNING }; + + tmp[3][0] = { embed::EmbedStates::INPLACE_ACTIVE, + embed::EmbedStates::RUNNING }; + + tmp[3][1] = { embed::EmbedStates::INPLACE_ACTIVE }; + + tmp[4][0] = { embed::EmbedStates::RUNNING }; + + return tmp; + }(); + return map; + } + + // accepted states + const css::uno::Sequence< sal_Int32 > & getAcceptedStates() + { + static const css::uno::Sequence< sal_Int32 > states { + /* [0] */ embed::EmbedStates::LOADED, + /* [1] */ embed::EmbedStates::RUNNING, + /* [2] */ embed::EmbedStates::INPLACE_ACTIVE, + /* [3] */ embed::EmbedStates::UI_ACTIVE, + /* [4] */ embed::EmbedStates::ACTIVE }; + assert(states.getLength() == NUM_SUPPORTED_STATES); + return states; + } + +} + +sal_Int32 OCommonEmbeddedObject::ConvertVerbToState_Impl( sal_Int32 nVerb ) +{ + auto it = m_aVerbTable.find( nVerb ); + if (it != m_aVerbTable.end()) + return it->second; + + throw lang::IllegalArgumentException(); // TODO: unexpected verb provided +} + + +void OCommonEmbeddedObject::Deactivate() +{ + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + + // no need to lock for the initialization + uno::Reference< embed::XEmbeddedClient > xClientSite = m_xClientSite; + if ( !xClientSite.is() ) + throw embed::WrongStateException(); //TODO: client site is not set! + + // tdf#131146 close frame before saving of the document + // (during CloseFrame() call some changes could be detected not registered in util::XModifiable) + m_xDocHolder->CloseFrame(); + + // store document if it is modified + if ( xModif.is() && xModif->isModified() ) + { + try { + xClientSite->saveObject(); + + // tdf#141529 take note that an eventually used linked file + // got changed/saved/written and that we need to copy it back if the + // hosting file/document gets saved + if(m_aLinkTempFile.is()) + m_bLinkTempFileChanged = true; + } + catch( const embed::ObjectSaveVetoException& ) + { + } + catch( const uno::Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw embed::StorageWrappedTargetException( + "The client could not store the object!", + static_cast< ::cppu::OWeakObject* >( this ), + anyEx ); + } + } + + xClientSite->visibilityChanged( false ); +} + + +void OCommonEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ,::osl::ResettableMutexGuard& rGuard ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pContainer == nullptr ) + return; + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + + // should be locked after the method is finished successfully + rGuard.clear(); + + while (pIterator.hasMoreElements()) + { + try + { + if ( bBeforeChange ) + static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState ); + else + static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // even if the listener complains ignore it for now + } + + if ( m_bDisposed ) + return; + } + + rGuard.reset(); +} + +void OCommonEmbeddedObject::SetInplaceActiveState() +{ + if ( !m_xClientSite.is() ) + throw embed::WrongStateException( "client site not set, yet", *this ); + + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY ); + if ( !xInplaceClient.is() || !xInplaceClient->canInplaceActivate() ) + throw embed::WrongStateException(); //TODO: can't activate inplace + xInplaceClient->activatingInplace(); + + uno::Reference< embed::XWindowSupplier > xClientWindowSupplier( xInplaceClient, uno::UNO_QUERY_THROW ); + + m_xClientWindow = xClientWindowSupplier->getWindow(); + m_aOwnRectangle = xInplaceClient->getPlacement(); + m_aClipRectangle = xInplaceClient->getClipRectangle(); + awt::Rectangle aRectangleToShow = GetRectangleInterception( m_aOwnRectangle, m_aClipRectangle ); + + // create own window based on the client window + // place and resize the window according to the rectangles + uno::Reference< awt::XWindowPeer > xClientWindowPeer( m_xClientWindow, uno::UNO_QUERY_THROW ); + + // dispatch provider may not be provided + uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider(); + bool bOk = m_xDocHolder->ShowInplace( xClientWindowPeer, aRectangleToShow, xContainerDP ); + m_nObjectState = embed::EmbedStates::INPLACE_ACTIVE; + if ( !bOk ) + { + SwitchStateTo_Impl( embed::EmbedStates::RUNNING ); + throw embed::WrongStateException(); //TODO: can't activate inplace + } +} + +void OCommonEmbeddedObject::SwitchStateTo_Impl( sal_Int32 nNextState ) +{ + // TODO: may be needs interaction handler to detect whether the object state + // can be changed even after errors + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + // after the object reaches the running state the cloned size is not necessary any more + m_bHasClonedSize = false; + + if ( m_bIsLinkURL ) + { + m_xDocHolder->SetComponent( LoadLink_Impl(), m_bReadOnly ); + } + else + { + if ( !dynamic_cast<OSpecialEmbeddedObject*>(this) ) + { + // in case embedded object is in loaded state the contents must + // be stored in the related storage and the storage + // must be created already + if ( !m_xObjectStorage.is() ) + throw io::IOException(); //TODO: access denied + + m_xDocHolder->SetComponent( LoadDocumentFromStorage_Impl(), m_bReadOnly ); + } + else + { + // objects without persistence will be initialized internally + uno::Sequence < uno::Any > aArgs{ uno::Any( + uno::Reference < embed::XEmbeddedObject >( this )) }; + uno::Reference< util::XCloseable > xDocument( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( GetDocumentServiceName(), aArgs, m_xContext), + uno::UNO_QUERY ); + + uno::Reference < container::XChild > xChild( xDocument, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + + m_xDocHolder->SetComponent( xDocument, m_bReadOnly ); + } + } + + if ( !m_xDocHolder->GetComponent().is() ) + throw embed::UnreachableStateException(); //TODO: can't open document + + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state, only RUNNING state allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::RUNNING ) + { + if ( nNextState == embed::EmbedStates::LOADED ) + { + m_nClonedMapUnit = m_xDocHolder->GetMapUnit( embed::Aspects::MSOLE_CONTENT ); + m_bHasClonedSize = m_xDocHolder->GetExtent( embed::Aspects::MSOLE_CONTENT, &m_aClonedSize ); + + // actually frame should not exist at this point + m_xDocHolder->CloseDocument( false, false ); + + m_nObjectState = nNextState; + } + else + { + if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE ) + { + SetInplaceActiveState(); + } + else if ( nNextState == embed::EmbedStates::ACTIVE ) + { + if ( !m_xClientSite.is() ) + throw embed::WrongStateException(); //TODO: client site is not set! + + // create frame and load document in the frame + m_xDocHolder->Show(); + + m_xClientSite->visibilityChanged( true ); + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state,only LOADED/INPLACE_ACTIVE/ACTIVE allowed"); // TODO + } + } + } + else if ( m_nObjectState == embed::EmbedStates::INPLACE_ACTIVE ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + + m_xClientSite->visibilityChanged( true ); + + xInplaceClient->deactivatedInplace(); + Deactivate(); + m_nObjectState = nNextState; + } + else if ( nNextState == embed::EmbedStates::UI_ACTIVE ) + { + if ( !(m_nMiscStatus & embed::EmbedMisc::MS_EMBED_NOUIACTIVATE) ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + // TODO: + uno::Reference< css::frame::XLayoutManager > xContainerLM = + xInplaceClient->getLayoutManager(); + if ( !xContainerLM.is() ) + throw embed::WrongStateException(); //TODO: can't activate UI + // dispatch provider may not be provided + uno::Reference< frame::XDispatchProvider > xContainerDP = xInplaceClient->getInplaceDispatchProvider(); + + // get the container module name + OUString aModuleName; + try + { + uno::Reference< embed::XComponentSupplier > xCompSupl( m_xClientSite, uno::UNO_QUERY_THROW ); + uno::Reference< uno::XInterface > xContDoc( xCompSupl->getComponent(), uno::UNO_QUERY_THROW ); + + uno::Reference< frame::XModuleManager2 > xManager( frame::ModuleManager::create( m_xContext ) ); + + aModuleName = xManager->identify( xContDoc ); + } + catch( const uno::Exception& ) + {} + + if (!comphelper::LibreOfficeKit::isActive()) + { + // if currently another object is UIactive it will be deactivated; usually this will activate the LM of + // the container. Locking the LM will prevent flicker. + xContainerLM->lock(); + xInplaceClient->activatingUI(); + bool bOk = m_xDocHolder->ShowUI( xContainerLM, xContainerDP, aModuleName ); + xContainerLM->unlock(); + + if ( bOk ) + { + m_nObjectState = nNextState; + m_xDocHolder->ResizeHatchWindow(); + } + else + { + xInplaceClient->deactivatedUI(); + throw embed::WrongStateException(); //TODO: can't activate UI + } + } + } + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state,only RUNNING/UI_ACTIVE allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + { + if ( nNextState == embed::EmbedStates::RUNNING ) + { + Deactivate(); + m_nObjectState = nNextState; + } + else + { + SAL_WARN( "embeddedobj.common", "Unacceptable state switch!" ); + throw uno::RuntimeException("invalid next state, only RUNNING state allowed"); // TODO + } + } + else if ( m_nObjectState == embed::EmbedStates::UI_ACTIVE ) + { + if ( nNextState == embed::EmbedStates::INPLACE_ACTIVE ) + { + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY_THROW ); + uno::Reference< css::frame::XLayoutManager > xContainerLM = + xInplaceClient->getLayoutManager(); + + bool bOk = false; + if ( xContainerLM.is() ) + bOk = m_xDocHolder->HideUI( xContainerLM ); + + if ( !bOk ) + throw embed::WrongStateException(); //TODO: can't activate UI + m_nObjectState = nNextState; + m_xDocHolder->ResizeHatchWindow(); + xInplaceClient->deactivatedUI(); + } + } + else + throw embed::WrongStateException( "The object is in unacceptable state!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + + +uno::Sequence< sal_Int32 > const & OCommonEmbeddedObject::GetIntermediateStatesSequence_Impl( sal_Int32 nNewState ) +{ + sal_Int32 nCurInd = 0; + auto & rAcceptedStates = getAcceptedStates(); + for ( nCurInd = 0; nCurInd < rAcceptedStates.getLength(); nCurInd++ ) + if ( rAcceptedStates[nCurInd] == m_nObjectState ) + break; + + if ( nCurInd == rAcceptedStates.getLength() ) + throw embed::WrongStateException( "The object is in unacceptable state!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int32 nDestInd = 0; + for ( nDestInd = 0; nDestInd < rAcceptedStates.getLength(); nDestInd++ ) + if ( rAcceptedStates[nDestInd] == nNewState ) + break; + + if ( nDestInd == rAcceptedStates.getLength() ) + throw embed::UnreachableStateException( + "The state either not reachable, or the object allows the state only as an intermediate one!", + static_cast< ::cppu::OWeakObject* >(this), + m_nObjectState, + nNewState ); + + return getIntermediateStatesMap()[nCurInd][nDestInd]; +} + + +void SAL_CALL OCommonEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int32 nOldState = m_nObjectState; + + if ( m_nTargetState != -1 ) + { + // means that the object is currently trying to reach the target state + throw embed::StateChangeInProgressException( OUString(), + uno::Reference< uno::XInterface >(), + m_nTargetState ); + } + else + { + TargetStateControl_Impl aControl( m_nTargetState, nNewState ); + + // in case the object is already in requested state + if ( m_nObjectState == nNewState ) + { + // if active object is activated again, bring its window to top + if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + m_xDocHolder->Show(); + + return; + } + + // retrieve sequence of states that should be passed to reach desired state + uno::Sequence< sal_Int32 > aIntermediateStates = GetIntermediateStatesSequence_Impl( nNewState ); + + // notify listeners that the object is going to change the state + StateChangeNotification_Impl( true, nOldState, nNewState,aGuard ); + + try { + for ( sal_Int32 state : std::as_const(aIntermediateStates) ) + SwitchStateTo_Impl( state ); + + SwitchStateTo_Impl( nNewState ); + } + catch( const uno::Exception& ) + { + if ( nOldState != m_nObjectState ) + // notify listeners that the object has changed the state + StateChangeNotification_Impl( false, nOldState, m_nObjectState, aGuard ); + + throw; + } + } + + // notify listeners that the object has changed the state + StateChangeNotification_Impl( false, nOldState, nNewState, aGuard ); + + // let the object window be shown + if ( nNewState == embed::EmbedStates::UI_ACTIVE || nNewState == embed::EmbedStates::INPLACE_ACTIVE ) + PostEvent_Impl( "OnVisAreaChanged" ); +} + + +uno::Sequence< sal_Int32 > SAL_CALL OCommonEmbeddedObject::getReachableStates() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return getAcceptedStates(); +} + + +sal_Int32 SAL_CALL OCommonEmbeddedObject::getCurrentState() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_nObjectState; +} + + +void SAL_CALL OCommonEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + SolarMutexGuard aSolarGuard; + //TODO: a gross hack to avoid deadlocks when this is called from the + // outside and OCommonEmbeddedObject::changeState, with m_aMutex locked, + // calls into framework code that tries to lock the solar mutex, while + // another thread (through Window::ImplCallPaint, say) calls + // OCommonEmbeddedObject::getComponent with the solar mutex locked and + // then tries to lock m_aMutex (see fdo#56818); the alternative would be + // to get locking done right in this class, but that looks like a + // daunting task + + osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for internal documents this call is just a duplicate of changeState + sal_Int32 nNewState = -1; + try + { + nNewState = ConvertVerbToState_Impl( nVerbID ); + } + catch( const uno::Exception& ) + {} + + if ( nNewState == -1 ) + { + // TODO/LATER: Save Copy as... verb ( -8 ) is implemented by container + // TODO/LATER: check if the verb is a supported one and if it is produce related operation + } + else + { + aGuard.clear(); + changeState( nNewState ); + } +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL OCommonEmbeddedObject::getSupportedVerbs() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aObjectVerbs; +} + + +void SAL_CALL OCommonEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_xClientSite != xClient) + { + if ( m_nObjectState != embed::EmbedStates::LOADED && m_nObjectState != embed::EmbedStates::RUNNING ) + throw embed::WrongStateException( + "The client site can not be set currently!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xClientSite = xClient; + } +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL OCommonEmbeddedObject::getClientSite() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_xClientSite; +} + + +void SAL_CALL OCommonEmbeddedObject::update() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + PostEvent_Impl( "OnVisAreaChanged" ); +} + + +void SAL_CALL OCommonEmbeddedObject::setUpdateMode( sal_Int32 nMode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nMode == embed::EmbedUpdateModes::ALWAYS_UPDATE + || nMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE, + "Unknown update mode!" ); + m_nUpdateMode = nMode; +} + + +sal_Int64 SAL_CALL OCommonEmbeddedObject::getStatus( sal_Int64 ) +{ + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_nMiscStatus; +} + + +void SAL_CALL OCommonEmbeddedObject::setContainerName( const OUString& sName ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aContainerName = sName; +} + +void OCommonEmbeddedObject::SetOleState(bool bIsOleUpdate) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + m_bOleUpdate = bIsOleUpdate; +} + +css::uno::Reference< css::uno::XInterface > SAL_CALL OCommonEmbeddedObject::getParent() +{ + return m_xParent; +} + +void SAL_CALL OCommonEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) +{ + m_xParent = xParent; + if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED ) + { + uno::Reference < container::XChild > xChild( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( xParent ); + } +} + +// XDefaultSizeTransmitter +void SAL_CALL OCommonEmbeddedObject::setDefaultSize( const css::awt::Size& rSize_100TH_MM ) +{ + //#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this method + m_aDefaultSizeForChart_In_100TH_MM = rSize_100TH_MM; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/embedobj.hxx b/embeddedobj/source/commonembedding/embedobj.hxx new file mode 100644 index 000000000..5b1d8b235 --- /dev/null +++ b/embeddedobj/source/commonembedding/embedobj.hxx @@ -0,0 +1,26 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/awt/Rectangle.hpp> + +css::awt::Rectangle GetRectangleInterception( const css::awt::Rectangle& aRect1, const css::awt::Rectangle& aRect2 ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/inplaceobj.cxx b/embeddedobj/source/commonembedding/inplaceobj.cxx new file mode 100644 index 000000000..c58d53358 --- /dev/null +++ b/embeddedobj/source/commonembedding/inplaceobj.cxx @@ -0,0 +1,73 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <commonembobj.hxx> +#include "embedobj.hxx" + + +using namespace ::com::sun::star; + +static bool RectanglesEqual( const awt::Rectangle& aRect1, const awt::Rectangle& aRect2 ) +{ + return ( aRect1.X == aRect2.X + && aRect1.Y == aRect2.Y + && aRect1.Width == aRect2.Width + && aRect1.Height == aRect2.Height ); +} + +void SAL_CALL OCommonEmbeddedObject::setObjectRectangles( const awt::Rectangle& aPosRect, + const awt::Rectangle& aClipRect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState != embed::EmbedStates::INPLACE_ACTIVE + && m_nObjectState != embed::EmbedStates::UI_ACTIVE ) + throw embed::WrongStateException( "The object is not activated inplace!", + static_cast< ::cppu::OWeakObject* >(this) ); + + awt::Rectangle aNewRectToShow = GetRectangleInterception( aPosRect, aClipRect ); + awt::Rectangle aOldRectToShow = GetRectangleInterception( m_aOwnRectangle, m_aClipRectangle ); + + // the clip rectangle changes view only in case interception is also changed + if ( !RectanglesEqual( m_aOwnRectangle, aPosRect ) + || ( !RectanglesEqual( m_aClipRectangle, aPosRect ) && !RectanglesEqual( aOldRectToShow, aNewRectToShow ) ) ) + m_xDocHolder->PlaceFrame( aNewRectToShow ); + + m_aOwnRectangle = aPosRect; + m_aClipRectangle = aClipRect; +} + +void SAL_CALL OCommonEmbeddedObject::enableModeless( sal_Bool /*bEnable*/ ) +{ + // TODO: notify model that it can not use modal dialogs +} + +void SAL_CALL OCommonEmbeddedObject::translateAccelerators( + const uno::Sequence< awt::KeyEvent >& /*aKeys*/ ) +{ + // TODO: UI activation related +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/miscobj.cxx b/embeddedobj/source/commonembedding/miscobj.cxx new file mode 100644 index 000000000..0b15376c2 --- /dev/null +++ b/embeddedobj/source/commonembedding/miscobj.cxx @@ -0,0 +1,788 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <commonembobj.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/XInplaceClient.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/storagehelper.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/mimeconfighelper.hxx> + +#include <vcl/weld.hxx> +#include <unotools/resmgr.hxx> +#include <vcl/stdtext.hxx> +#include <strings.hrc> +#include <osl/file.hxx> +#include <comphelper/DirectoryHelper.hxx> + +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "persistence.hxx" + +#include <cassert> + +using namespace ::com::sun::star; + + +OCommonEmbeddedObject::OCommonEmbeddedObject( const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::NamedValue >& aObjProps ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_bClosed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( rxContext ) +, m_nMiscStatus( 0 ) +, m_bEmbeddedScriptSupport( true ) +, m_bDocumentRecoverySupport( true ) +, m_bWaitSaveCompleted( false ) +, m_bIsLinkURL( false ) +, m_bLinkTempFileChanged( false ) +, m_pLinkFile( ) +, m_bOleUpdate( false ) +, m_bInHndFunc( false ) +, m_bLinkHasPassword( false ) +, m_aLinkTempFile( ) +, m_bHasClonedSize( false ) +, m_nClonedMapUnit( 0 ) +{ + CommonInit_Impl( aObjProps ); +} + + +OCommonEmbeddedObject::OCommonEmbeddedObject( + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Sequence< beans::NamedValue >& aObjProps, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& aObjectDescr ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_bClosed( false ) +, m_nObjectState( embed::EmbedStates::LOADED ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( rxContext ) +, m_nMiscStatus( 0 ) +, m_bEmbeddedScriptSupport( true ) +, m_bDocumentRecoverySupport( true ) +, m_bWaitSaveCompleted( false ) +, m_bIsLinkURL( true ) +, m_bLinkTempFileChanged( false ) +, m_pLinkFile( ) +, m_bOleUpdate( false ) +, m_bInHndFunc( false ) +, m_bLinkHasPassword( false ) +, m_aLinkTempFile( ) +, m_bHasClonedSize( false ) +, m_nClonedMapUnit( 0 ) +{ + // linked object has no own persistence so it is in loaded state starting from creation + LinkInit_Impl( aObjProps, aMediaDescr, aObjectDescr ); +} + + +void OCommonEmbeddedObject::CommonInit_Impl( const uno::Sequence< beans::NamedValue >& aObjectProps ) +{ + OSL_ENSURE( m_xContext.is(), "No ServiceFactory is provided!" ); + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + m_xDocHolder = new DocumentHolder( m_xContext, this ); + + // parse configuration entries + // TODO/LATER: in future UI names can be also provided here + for ( beans::NamedValue const & prop : aObjectProps ) + { + if ( prop.Name == "ClassID" ) + prop.Value >>= m_aClassID; + else if ( prop.Name == "ObjectDocumentServiceName" ) + prop.Value >>= m_aDocServiceName; + else if ( prop.Name == "ObjectDocumentFilterName" ) + prop.Value >>= m_aPresetFilterName; + else if ( prop.Name == "ObjectMiscStatus" ) + prop.Value >>= m_nMiscStatus; + else if ( prop.Name == "ObjectVerbs" ) + prop.Value >>= m_aObjectVerbs; + } + + if ( m_aClassID.getLength() != 16 /*|| !m_aDocServiceName.getLength()*/ ) + throw uno::RuntimeException(); // something goes really wrong + + // verbs table + for ( auto const & verb : std::as_const(m_aObjectVerbs) ) + { + if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_PRIMARY ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_SHOW ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_OPEN ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::INPLACE_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::UI_ACTIVE } ); + } + else if ( verb.VerbID == embed::EmbedVerbs::MS_OLEVERB_HIDE ) + { + m_aVerbTable.insert( { verb.VerbID, embed::EmbedStates::RUNNING } ); + } + } +} + + +void OCommonEmbeddedObject::LinkInit_Impl( + const uno::Sequence< beans::NamedValue >& aObjectProps, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& aObjectDescr ) +{ + // setPersistance has no effect on own links, so the complete initialization must be done here + + for ( beans::PropertyValue const & prop : aMediaDescr ) + if ( prop.Name == "URL" ) + prop.Value >>= m_aLinkURL; + else if ( prop.Name == "FilterName" ) + prop.Value >>= m_aLinkFilterName; + + OSL_ENSURE( m_aLinkURL.getLength() && m_aLinkFilterName.getLength(), "Filter and URL must be provided!" ); + + m_bReadOnly = true; + if ( m_aLinkFilterName.getLength() ) + { + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + OUString aExportFilterName = aHelper.GetExportFilterFromImportFilter( m_aLinkFilterName ); + m_bReadOnly = aExportFilterName != m_aLinkFilterName; + } + + if(m_bIsLinkURL && !m_bReadOnly) + { + // tdf#141529 we have a linked OLE object. To prevent the original OLE + // data to be changed each time the OLE gets changed (at deactivate), copy it to + // a temporary file. That file will be changed on activated OLE changes then. + // The moment the original gets changed itself will now be associated with the + // file/document embedding the OLE being changed (see other additions to the + // task-ID above) + // + // open OLE original data as read input file + if ( comphelper::DirectoryHelper::fileExists( m_aLinkURL ) ) + { + // create temporary file + m_aLinkTempFile = io::TempFile::create( m_xContext ); + + m_pLinkFile.reset( new FileChangedChecker( m_aLinkURL ) ); + handleLinkedOLE( CopyBackToOLELink::CopyLinkToTempInit ); + } + } + + if(m_aLinkTempFile.is()) + { + uno::Sequence< beans::PropertyValue > aAlternativeMediaDescr(aMediaDescr.getLength()); + auto aAlternativeMediaDescrRange = asNonConstRange(aAlternativeMediaDescr); + + for ( sal_Int32 a(0); a < aMediaDescr.getLength(); a++ ) + { + const beans::PropertyValue& rSource(aMediaDescr[a]); + beans::PropertyValue& rDestination(aAlternativeMediaDescrRange[a]); + + rDestination.Name = rSource.Name; + if(rSource.Name == "URL") + rDestination.Value <<= m_aLinkTempFile->getUri(); + else + rDestination.Value = rSource.Value; + } + + m_aDocMediaDescriptor = GetValuableArgs_Impl( aAlternativeMediaDescr, false ); + } + else + { + m_aDocMediaDescriptor = GetValuableArgs_Impl( aMediaDescr, false ); + } + + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + for ( beans::PropertyValue const & prop : aObjectDescr ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + prop.Value >>= xDispatchInterceptor; + break; + } + else if ( prop.Name == "Parent" ) + { + prop.Value >>= m_xParent; + } + + CommonInit_Impl( aObjectProps ); + + if ( xDispatchInterceptor.is() ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); +} + + +OCommonEmbeddedObject::~OCommonEmbeddedObject() +{ + if ( !(m_pInterfaceContainer || m_xDocHolder.is()) ) + return; + + osl_atomic_increment(&m_refCount); + if ( m_pInterfaceContainer ) + { + try { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + } catch( const uno::Exception& ) {} + m_pInterfaceContainer.reset(); + } + + try { + if ( m_xDocHolder.is() ) + { + m_xDocHolder->CloseFrame(); + try { + m_xDocHolder->CloseDocument( true, true ); + } catch ( const uno::Exception& ) {} + m_xDocHolder->FreeOffice(); + + m_xDocHolder.clear(); + } + } catch( const uno::Exception& ) {} +} + + +void OCommonEmbeddedObject::requestPositioning( const awt::Rectangle& aRect ) +{ + // the method is called in case object is inplace active and the object window was resized + + OSL_ENSURE( m_xClientSite.is(), "The client site must be set for inplace active object!" ); + if ( !m_xClientSite.is() ) + return; + + uno::Reference< embed::XInplaceClient > xInplaceClient( m_xClientSite, uno::UNO_QUERY ); + + OSL_ENSURE( xInplaceClient.is(), "The client site must support XInplaceClient to allow inplace activation!" ); + if ( xInplaceClient.is() ) + { + try { + xInplaceClient->changedPlacement( aRect ); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "embeddedobj", "Exception on request to resize!" ); + } + } +} + + +void OCommonEmbeddedObject::PostEvent_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pIC = m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if( !pIC ) + return; + + document::EventObject aEvent; + aEvent.EventName = aEventName; + aEvent.Source.set( static_cast< ::cppu::OWeakObject* >( this ) ); + // For now all the events are sent as object events + // aEvent.Source = ( xSource.is() ? xSource + // : uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >( this ) ) ); + comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + try + { + static_cast<document::XEventListener *>(aIt.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + aIt.remove(); + } + + // the listener could dispose the object. + if ( m_bDisposed ) + return; + } +} + + +static int ShowMsgDialog( TranslateId Msg, const OUString& sFileName ) +{ + std::locale aResLocale = Translate::Create( "emo" ); + OUString aMsg = Translate::get( Msg, aResLocale ); + OUString aBtn = Translate::get( BTN_OVERWRITE_TEXT, aResLocale ); + OUString aTemp = sFileName; + + osl::FileBase::getSystemPathFromFileURL( sFileName, aTemp ); + + aMsg = aMsg.replaceFirst( "%{filename}", aTemp ); + weld::Window* pParent = Application::GetFrameWeld( nullptr ); + + std::unique_ptr<weld::MessageDialog> xQueryBox (Application::CreateMessageDialog( pParent, + VclMessageType::Warning, VclButtonsType::NONE, aMsg ) ); + xQueryBox->add_button( aBtn, RET_YES ); + xQueryBox->add_button( GetStandardText( StandardButtonType::Cancel ), RET_CANCEL ); + xQueryBox->set_default_response( RET_CANCEL ); + + return xQueryBox->run(); +} + + +void OCommonEmbeddedObject::handleLinkedOLE( CopyBackToOLELink eState ) +{ + // do not refresh and autosave at the same time + // when refresh all, then get both Link and Ole Update, in this case ignore OLE-refresh + if ( m_bInHndFunc || m_bOleUpdate || !m_aLinkTempFile.is() ) + return; + + m_bInHndFunc = true; + + bool bLnkFileChg = m_pLinkFile->hasFileChanged( false ); + bool bTmpFileChg = m_bLinkTempFileChanged; + + + if ( eState != CopyBackToOLELink::CopyLinkToTempInit && !bLnkFileChg && !bTmpFileChg ) + { + // no changes + eState = CopyBackToOLELink::NoCopy; + } + else if ( ( eState == CopyBackToOLELink::CopyTempToLink ) && bLnkFileChg && !bTmpFileChg ) + { + // Save pressed, but the Link-file is changed, but not the temp-file + // in this case update the object with new link data + eState = CopyBackToOLELink::CopyLinkToTempRefresh; + } + else if ( ( eState == CopyBackToOLELink::CopyTempToLink ) && bLnkFileChg && bTmpFileChg ) + { + // Save pressed, but the Link-file is changed, question to user for overwrite + if ( ShowMsgDialog(STR_OVERWRITE_LINK, m_aLinkURL) == RET_CANCEL ) + eState = CopyBackToOLELink::NoCopy; + } + else if ( ( eState == CopyBackToOLELink::CopyLinkToTemp ) && bTmpFileChg ) + { + // Refresh pressed, but the Temp-file is changed, question to user for overwrite + // it is not important it has bLnkFileChg, always overwrite the temp-file + if ( ShowMsgDialog( STR_OVERWRITE_TEMP, m_aLinkURL ) == RET_CANCEL ) + eState = CopyBackToOLELink::NoCopy; + } + + auto writeFile = [ this ]( const OUString& SrcName, const OUString& DesName ) + { + uno::Reference < ucb::XSimpleFileAccess2 > xWriteAccess( ucb::SimpleFileAccess::create( m_xContext ) ); + uno::Reference < ucb::XSimpleFileAccess > xReadAccess( ucb::SimpleFileAccess::create( m_xContext ) ); + + try + { + uno::Reference < io::XInputStream > xInStream( xReadAccess->openFileRead (SrcName ) ); + + // This is *needed* since OTempFileService calls OTempFileService::readBytes which + // ensures the SvStream mpStream gets/is opened, *but* also sets the mnCachedPos from + // OTempFileService which still points to the end-of-file (from write-cc'ing). + uno::Reference < io::XSeekable > xSeek( xInStream, uno::UNO_QUERY_THROW ); + xSeek->seek( 0 ); + + xWriteAccess->writeFile( DesName, xInStream ); + m_bLinkTempFileChanged = false; + // store the new timestamp + m_pLinkFile->hasFileChanged(); + } + catch ( const uno::Exception& ex ) + { + OUString aMsg; + osl::FileBase::getSystemPathFromFileURL( SrcName, aMsg ); + aMsg = ex.Message + "\n\n" + aMsg; + weld::Window* pParent = Application::GetFrameWeld( nullptr ); + std::unique_ptr<weld::MessageDialog> xQueryBox( Application::CreateMessageDialog( pParent, + VclMessageType::Error, VclButtonsType::Ok, aMsg ) ); + + xQueryBox->run(); + } + }; + + switch ( eState ) + { + case CopyBackToOLELink::NoCopy: + break; + case CopyBackToOLELink::CopyLinkToTemp: // copy Link-File to Temp-File (Refresh) + case CopyBackToOLELink::CopyLinkToTempInit: //create temp file + writeFile( m_aLinkURL, m_aLinkTempFile->getUri() ); + break; + case CopyBackToOLELink::CopyTempToLink: // copy Temp-File to Link-File (Save) + // tdf#141529 if we have a changed copy of the original OLE data we now + // need to write it back 'over' the original OLE data + writeFile( m_aLinkTempFile->getUri(), m_aLinkURL ); + break; + case CopyBackToOLELink::CopyLinkToTempRefresh: // need a Refresh not save + // do nothing + break; + default: + break; + } + + m_bInHndFunc = false; +} + + +uno::Any SAL_CALL OCommonEmbeddedObject::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn; + + if ( rType == cppu::UnoType<embed::XEmbeddedObject>::get() ) + { + void * p = static_cast< embed::XEmbeddedObject * >( this ); + return uno::Any( &p, rType ); + } + else if (rType == cppu::UnoType<embed::XEmbedPersist2>::get()) + { + void* p = static_cast<embed::XEmbedPersist2*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XServiceInfo>::get()) + { + void* p = static_cast<lang::XServiceInfo*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XInitialization>::get()) + { + void* p = static_cast<lang::XInitialization*>(this); + return uno::Any(&p, rType); + } + else if (rType == cppu::UnoType<lang::XTypeProvider>::get()) + { + void* p = static_cast<lang::XTypeProvider*>(this); + return uno::Any(&p, rType); + } + else + aReturn = ::cppu::queryInterface( + rType, + static_cast< embed::XInplaceObject* >( this ), + static_cast< embed::XVisualObject* >( this ), + static_cast< embed::XCommonEmbedPersist* >( static_cast< embed::XEmbedPersist* >( this ) ), + static_cast< embed::XEmbedPersist* >( this ), + static_cast< embed::XLinkageSupport* >( this ), + static_cast< embed::XStateChangeBroadcaster* >( this ), + static_cast< embed::XClassifiedObject* >( this ), + static_cast< embed::XComponentSupplier* >( this ), + static_cast< util::XCloseable* >( this ), + static_cast< container::XChild* >( this ), + static_cast< chart2::XDefaultSizeTransmitter* >( this ), + static_cast< document::XEventBroadcaster* >( this ) ); + + if ( aReturn.hasValue() ) + return aReturn; + else + return ::cppu::OWeakObject::queryInterface( rType ) ; + +} + + +void SAL_CALL OCommonEmbeddedObject::acquire() + noexcept +{ + ::cppu::OWeakObject::acquire() ; +} + + +void SAL_CALL OCommonEmbeddedObject::release() + noexcept +{ + ::cppu::OWeakObject::release() ; +} + + +uno::Sequence< sal_Int8 > SAL_CALL OCommonEmbeddedObject::getClassID() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + return m_aClassID; +} + +OUString SAL_CALL OCommonEmbeddedObject::getClassName() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + return m_aClassName; +} + +void SAL_CALL OCommonEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& /*aClassID*/, const OUString& /*aClassName*/ ) +{ + // the object class info can not be changed explicitly + throw lang::NoSupportException(); //TODO: +} + + +uno::Reference< util::XCloseable > SAL_CALL OCommonEmbeddedObject::getComponent() +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + // add an exception + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw uno::RuntimeException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + return m_xDocHolder->GetComponent(); +} + + +void SAL_CALL OCommonEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + SolarMutexGuard aGuard; + if ( m_bClosed ) + throw lang::DisposedException(); // TODO + + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ) ); + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource ); + } + catch( const uno::RuntimeException& ) + { + pCloseIterator.remove(); + } + } + } + + m_pInterfaceContainer->disposeAndClear( aSource ); + m_pInterfaceContainer.reset(); + } + + m_bDisposed = true; // the object is disposed now for outside + + // it is possible that the document can not be closed, in this case if the argument is false + // the exception will be thrown otherwise in addition to exception the object must register itself + // as termination listener and listen for document events + + if ( m_xDocHolder.is() ) + { + m_xDocHolder->CloseFrame(); + + try { + m_xDocHolder->CloseDocument( bDeliverOwnership, bDeliverOwnership ); + } + catch( const uno::Exception& ) + { + if ( bDeliverOwnership ) + { + m_xDocHolder.clear(); + m_bClosed = true; + } + + throw; + } + + m_xDocHolder->FreeOffice(); + + m_xDocHolder.clear(); + } + + // TODO: for now the storage will be disposed by the object, but after the document + // will use the storage, the storage will be disposed by the document and recreated by the object + if ( m_xObjectStorage.is() ) + { + try { + m_xObjectStorage->dispose(); + } catch ( const uno::Exception& ) {} + + m_xObjectStorage.clear(); + m_xRecoveryStorage.clear(); + } + + m_bClosed = true; // the closing succeeded +} + + +void SAL_CALL OCommonEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2(m_aMutex)); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2(m_aMutex)); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OCommonEmbeddedObject::removeEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + SolarMutexGuard aGuard; + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +OUString SAL_CALL OCommonEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OCommonEmbeddedObject"; +} + +sal_Bool SAL_CALL OCommonEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OCommonEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OCommonEmbeddedObject" }; +} + +uno::Sequence<uno::Type> SAL_CALL OCommonEmbeddedObject::getTypes() +{ + static const uno::Sequence<uno::Type> aTypes{ + cppu::UnoType<embed::XEmbeddedObject>::get(), + cppu::UnoType<embed::XEmbedPersist2>::get(), + cppu::UnoType<embed::XLinkageSupport>::get(), + cppu::UnoType<embed::XInplaceObject>::get(), + cppu::UnoType<container::XChild>::get(), + cppu::UnoType<chart2::XDefaultSizeTransmitter>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XInitialization>::get(), + cppu::UnoType<lang::XTypeProvider>::get(), + }; + return aTypes; +} + +uno::Sequence<sal_Int8> SAL_CALL OCommonEmbeddedObject::getImplementationId() +{ + return uno::Sequence<sal_Int8>(); +} + +void SAL_CALL OCommonEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + if (!rArguments.hasElements()) + { + return; + } + + comphelper::SequenceAsHashMap aMap(rArguments[0]); + auto it = aMap.find("ReadOnly"); + if (it != aMap.end()) + { + it->second >>= m_bReadOnly; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/persistence.cxx b/embeddedobj/source/commonembedding/persistence.cxx new file mode 100644 index 000000000..65e88266a --- /dev/null +++ b/embeddedobj/source/commonembedding/persistence.cxx @@ -0,0 +1,1870 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <commonembobj.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/StorageFactory.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/frame/XModule.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> + +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <comphelper/fileformat.h> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/propertyvalue.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <unotools/configmgr.hxx> +#include "persistence.hxx" + +using namespace ::com::sun::star; + + +uno::Sequence< beans::PropertyValue > GetValuableArgs_Impl( const uno::Sequence< beans::PropertyValue >& aMedDescr, + bool bCanUseDocumentBaseURL ) +{ + uno::Sequence< beans::PropertyValue > aResult; + sal_Int32 nResLen = 0; + + for ( beans::PropertyValue const & prop : aMedDescr ) + { + if ( prop.Name == "ComponentData" || prop.Name == "DocumentTitle" + || prop.Name == "InteractionHandler" || prop.Name == "JumpMark" + // || prop.Name == "Password" // makes no sense for embedded objects + || prop.Name == "Preview" || prop.Name == "ReadOnly" + || prop.Name == "StartPresentation" || prop.Name == "RepairPackage" + || prop.Name == "StatusIndicator" || prop.Name == "ViewData" + || prop.Name == "ViewId" || prop.Name == "MacroExecutionMode" + || prop.Name == "UpdateDocMode" + || (prop.Name == "DocumentBaseURL" && bCanUseDocumentBaseURL) ) + { + aResult.realloc( ++nResLen ); + aResult.getArray()[nResLen-1] = prop; + } + } + + return aResult; +} + + +static uno::Sequence< beans::PropertyValue > addAsTemplate( const uno::Sequence< beans::PropertyValue >& aOrig ) +{ + bool bAsTemplateSet = false; + sal_Int32 nLength = aOrig.getLength(); + uno::Sequence< beans::PropertyValue > aResult( aOrig ); + + for ( sal_Int32 nInd = 0; nInd < nLength; nInd++ ) + { + if ( aResult[nInd].Name == "AsTemplate" ) + { + aResult.getArray()[nInd].Value <<= true; + bAsTemplateSet = true; + } + } + + if ( !bAsTemplateSet ) + { + aResult.realloc( nLength + 1 ); + auto pResult = aResult.getArray(); + pResult[nLength].Name = "AsTemplate"; + pResult[nLength].Value <<= true; + } + + return aResult; +} + + +static uno::Reference< io::XInputStream > createTempInpStreamFromStor( + const uno::Reference< embed::XStorage >& xStorage, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "The storage can not be empty!" ); + + uno::Reference< io::XInputStream > xResult; + + uno::Reference < io::XStream > xTempStream( io::TempFile::create(xContext), uno::UNO_QUERY_THROW ); + + uno::Reference < lang::XSingleServiceFactory > xStorageFactory( embed::StorageFactory::create(xContext) ); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTempStream), + uno::Any(embed::ElementModes::READWRITE) }; + uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + + try + { + xStorage->copyToStorage( xTempStorage ); + } catch( const uno::Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw embed::StorageWrappedTargetException( + "Can't copy storage!", + uno::Reference< uno::XInterface >(), + anyEx ); + } + + try { + if ( xTempStorage.is() ) + xTempStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + + try { + uno::Reference< io::XOutputStream > xTempOut = xTempStream->getOutputStream(); + if ( xTempOut.is() ) + xTempOut->closeOutput(); + } + catch ( const uno::Exception& ) + { + } + + xResult = xTempStream->getInputStream(); + + return xResult; + +} + + +static void TransferMediaType( const uno::Reference< embed::XStorage >& i_rSource, const uno::Reference< embed::XStorage >& i_rTarget ) +{ + try + { + const uno::Reference< beans::XPropertySet > xSourceProps( i_rSource, uno::UNO_QUERY_THROW ); + const uno::Reference< beans::XPropertySet > xTargetProps( i_rTarget, uno::UNO_QUERY_THROW ); + static const OUStringLiteral sMediaTypePropName( u"MediaType" ); + xTargetProps->setPropertyValue( sMediaTypePropName, xSourceProps->getPropertyValue( sMediaTypePropName ) ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("embeddedobj.common"); + } +} + + +static uno::Reference< util::XCloseable > CreateDocument( const uno::Reference< uno::XComponentContext >& _rxContext, + const OUString& _rDocumentServiceName, bool _bEmbeddedScriptSupport, const bool i_bDocumentRecoverySupport ) +{ + static constexpr OUStringLiteral sEmbeddedObject = u"EmbeddedObject"; + static constexpr OUStringLiteral sEmbeddedScriptSupport = u"EmbeddedScriptSupport"; + static constexpr OUStringLiteral sDocumentRecoverySupport = u"DocumentRecoverySupport"; + ::comphelper::NamedValueCollection aArguments; + aArguments.put( sEmbeddedObject, true ); + aArguments.put( sEmbeddedScriptSupport, _bEmbeddedScriptSupport ); + aArguments.put( sDocumentRecoverySupport, i_bDocumentRecoverySupport ); + + uno::Reference< uno::XInterface > xDocument; + try + { + xDocument = _rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + _rDocumentServiceName, aArguments.getWrappedPropertyValues(), _rxContext ); + } + catch( const uno::Exception& ) + { + // if an embedded object implementation does not support XInitialization, + // the default factory from cppuhelper will throw an + // IllegalArgumentException when we try to create the instance with arguments. + // Okay, so we fall back to creating the instance without any arguments. + OSL_FAIL("Consider implementing interface XInitialization to avoid duplicate construction"); + xDocument = _rxContext->getServiceManager()->createInstanceWithContext( _rDocumentServiceName, _rxContext ); + } + + SAL_WARN_IF(!xDocument.is(), "embeddedobj.common", "Service " << _rDocumentServiceName << " is not available?"); + return uno::Reference< util::XCloseable >( xDocument, uno::UNO_QUERY ); +} + + +static void SetDocToEmbedded( const uno::Reference< frame::XModel >& rDocument, const OUString& aModuleName ) +{ + if (!rDocument.is()) + return; + + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue("SetEmbedded", true) }; + rDocument->attachResource( OUString(), aSeq ); + + if ( !aModuleName.isEmpty() ) + { + try + { + uno::Reference< frame::XModule > xModule( rDocument, uno::UNO_QUERY_THROW ); + xModule->setIdentifier( aModuleName ); + } + catch( const uno::Exception& ) + {} + } +} + + +void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const uno::Reference< embed::XStorage >& xNewObjectStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + { + SAL_WARN_IF( xNewObjectStorage != m_xObjectStorage, "embeddedobj.common", "The storage must be the same!" ); + return; + } + + auto xOldObjectStorage = m_xObjectStorage; + m_xObjectStorage = xNewObjectStorage; + m_xParentStorage = xNewParentStorage; + m_aEntryName = aNewName; + + // the linked document should not be switched + if ( !m_bIsLinkURL ) + { + uno::Reference< document::XStorageBasedDocument > xDoc( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xDoc.is() ) + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + } + + try { + if ( xOldObjectStorage.is() ) + xOldObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } +} + + +void OCommonEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + return; + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + uno::Reference< embed::XStorage > xNewOwnStorage = xNewParentStorage->openStorageElement( aNewName, nStorageMode ); + SAL_WARN_IF( !xNewOwnStorage.is(), "embeddedobj.common", "The method can not return empty reference!" ); + + SwitchOwnPersistence( xNewParentStorage, xNewOwnStorage, aNewName ); +} + + +void OCommonEmbeddedObject::EmbedAndReparentDoc_Impl( const uno::Reference< util::XCloseable >& i_rxDocument ) const +{ + SetDocToEmbedded( uno::Reference< frame::XModel >( i_rxDocument, uno::UNO_QUERY ), m_aModuleName ); + + try + { + uno::Reference < container::XChild > xChild( i_rxDocument, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + } + catch( const lang::NoSupportException & ) + { + SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::EmbedAndReparentDoc: cannot set parent at document!" ); + } +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::InitNewDocument_Impl() +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY ); + uno::Reference< frame::XLoadable > xLoadable( xModel, uno::UNO_QUERY_THROW ); + + try + { + // set the document mode to embedded as the first action on document!!! + EmbedAndReparentDoc_Impl( xDocument ); + + // if we have a storage to recover the document from, do not use initNew, but instead load from that storage + bool bInitNew = true; + if ( m_xRecoveryStorage.is() ) + { + uno::Reference< document::XStorageBasedDocument > xDoc( xLoadable, uno::UNO_QUERY ); + SAL_WARN_IF( !xDoc.is(), "embeddedobj.common", "OCommonEmbeddedObject::InitNewDocument_Impl: cannot recover from a storage when the document is not storage based!" ); + if ( xDoc.is() ) + { + ::comphelper::NamedValueCollection aLoadArgs; + FillDefaultLoadArgs_Impl( m_xRecoveryStorage, aLoadArgs ); + + xDoc->loadFromStorage( m_xRecoveryStorage, aLoadArgs.getPropertyValues() ); + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + bInitNew = false; + } + } + + if ( bInitNew ) + { + // init document as a new + xLoadable->initNew(); + } + xModel->attachResource( xModel->getURL(), m_aDocMediaDescriptor ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadLink_Impl() +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + + sal_Int32 nLen = m_bLinkHasPassword ? 3 : 2; + uno::Sequence< beans::PropertyValue > aArgs( m_aDocMediaDescriptor.getLength() + nLen ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + if(m_aLinkTempFile.is()) + pArgs[0].Value <<= m_aLinkTempFile->getUri(); + else + pArgs[0].Value <<= m_aLinkURL; + + pArgs[1].Name = "FilterName"; + pArgs[1].Value <<= m_aLinkFilterName; + + if ( m_bLinkHasPassword ) + { + pArgs[2].Name = "Password"; + pArgs[2].Value <<= m_aLinkPassword; + } + + for ( sal_Int32 nInd = 0; nInd < m_aDocMediaDescriptor.getLength(); nInd++ ) + { + pArgs[nInd+nLen].Name = m_aDocMediaDescriptor[nInd].Name; + pArgs[nInd+nLen].Value = m_aDocMediaDescriptor[nInd].Value; + } + + try + { + handleLinkedOLE(CopyBackToOLELink::CopyLinkToTemp); + + // the document is not really an embedded one, it is a link + EmbedAndReparentDoc_Impl( xDocument ); + + // load the document + xLoadable->load( aArgs ); + + if ( !m_bLinkHasPassword ) + { + // check if there is a password to cache + uno::Reference< frame::XModel > xModel( xLoadable, uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::PropertyValue > aProps = xModel->getArgs(); + for ( beans::PropertyValue const & prop : aProps ) + if ( prop.Name == "Password" && ( prop.Value >>= m_aLinkPassword ) ) + { + m_bLinkHasPassword = true; + break; + } + } + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; + +} + + +OUString OCommonEmbeddedObject::GetFilterName( sal_Int32 nVersion ) const +{ + OUString aFilterName = GetPresetFilterName(); + if ( aFilterName.isEmpty() ) + { + OUString sDocumentServiceName = GetDocumentServiceName(); + if (utl::ConfigManager::IsFuzzing() && nVersion == SOFFICE_FILEFORMAT_CURRENT && + sDocumentServiceName == "com.sun.star.chart2.ChartDocument") + { + return "chart8"; + } + try { + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + aFilterName = aHelper.GetDefaultFilterFromServiceName(sDocumentServiceName, nVersion); + + // If no filter is found, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (aFilterName.isEmpty() && nVersion == SOFFICE_FILEFORMAT_CURRENT) + aFilterName = aHelper.GetDefaultFilterFromServiceName(GetDocumentServiceName(), SOFFICE_FILEFORMAT_60); + } catch( const uno::Exception& ) + {} + } + + return aFilterName; +} + + +void OCommonEmbeddedObject::FillDefaultLoadArgs_Impl( const uno::Reference< embed::XStorage >& i_rxStorage, + ::comphelper::NamedValueCollection& o_rLoadArgs ) const +{ + o_rLoadArgs.put( "DocumentBaseURL", GetBaseURL_Impl() ); + o_rLoadArgs.put( "HierarchicalDocumentName", m_aEntryName ); + o_rLoadArgs.put( "ReadOnly", m_bReadOnly ); + + OUString aFilterName = GetFilterName( ::comphelper::OStorageHelper::GetXStorageFormat( i_rxStorage ) ); + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "OCommonEmbeddedObject::FillDefaultLoadArgs_Impl: Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException(); // TODO: error message/code + + o_rLoadArgs.put( "FilterName", aFilterName ); +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::LoadDocumentFromStorage_Impl() +{ + ENSURE_OR_THROW( m_xObjectStorage.is(), "no object storage" ); + + const uno::Reference< embed::XStorage > xSourceStorage( m_xRecoveryStorage.is() ? m_xRecoveryStorage : m_xObjectStorage ); + + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + //#i103460# ODF: take the size given from the parent frame as default + uno::Reference< chart2::XChartDocument > xChart( xDocument, uno::UNO_QUERY ); + if( xChart.is() ) + { + uno::Reference< embed::XVisualObject > xChartVisualObject( xChart, uno::UNO_QUERY ); + if( xChartVisualObject.is() ) + xChartVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, m_aDefaultSizeForChart_In_100TH_MM ); + } + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY ); + uno::Reference< document::XStorageBasedDocument > xDoc( xDocument, uno::UNO_QUERY ); + if ( !xDoc.is() && !xLoadable.is() ) + throw uno::RuntimeException(); + + ::comphelper::NamedValueCollection aLoadArgs; + FillDefaultLoadArgs_Impl( xSourceStorage, aLoadArgs ); + + uno::Reference< io::XInputStream > xTempInpStream; + if ( !xDoc.is() ) + { + xTempInpStream = createTempInpStreamFromStor( xSourceStorage, m_xContext ); + if ( !xTempInpStream.is() ) + throw uno::RuntimeException(); + + OUString aTempFileURL; + try + { + // no need to let the file stay after the stream is removed since the embedded document + // can not be stored directly + uno::Reference< beans::XPropertySet > xTempStreamProps( xTempInpStream, uno::UNO_QUERY_THROW ); + xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL; + } + catch( const uno::Exception& ) + { + } + + SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" ); + + aLoadArgs.put( "URL", aTempFileURL ); + aLoadArgs.put( "InputStream", xTempInpStream ); + } + + + aLoadArgs.merge( m_aDocMediaDescriptor, true ); + + try + { + // set the document mode to embedded as the first step!!! + EmbedAndReparentDoc_Impl( xDocument ); + + if (m_bReadOnly) + { + aLoadArgs.put("ReadOnly", true); + } + + if ( xDoc.is() ) + { + xDoc->loadFromStorage( xSourceStorage, aLoadArgs.getPropertyValues() ); + if ( xSourceStorage != m_xObjectStorage ) + SwitchDocToStorage_Impl( xDoc, m_xObjectStorage ); + } + else + xLoadable->load( aLoadArgs.getPropertyValues() ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("embeddedobj.common"); + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< io::XInputStream > OCommonEmbeddedObject::StoreDocumentToTempStream_Impl( + sal_Int32 nStorageFormat, + const OUString& aBaseURL, + const OUString& aHierarchName ) +{ + uno::Reference < io::XOutputStream > xTempOut( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + uno::Reference< io::XInputStream > aResult( xTempOut, uno::UNO_QUERY_THROW ); + + uno::Reference< frame::XStorable > xStorable; + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_xDocHolder.is() ) + xStorable.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + } + + if( !xStorable.is() ) + throw uno::RuntimeException("No storage is provided for storing!"); // TODO: + + OUString aFilterName = GetFilterName( nStorageFormat ); + + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException("No filter name provided / Wrong document service name"); // TODO: + + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("FilterName", aFilterName), + comphelper::makePropertyValue("OutputStream", xTempOut), + comphelper::makePropertyValue("DocumentBaseURL", aBaseURL), + comphelper::makePropertyValue("HierarchicalDocumentName", aHierarchName) + }; + + xStorable->storeToURL( "private:stream", aArgs ); + try + { + xTempOut->closeOutput(); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Looks like stream was closed already" ); + } + + return aResult; +} + + +void OCommonEmbeddedObject::SaveObject_Impl() +{ + if ( !m_xClientSite.is() ) + return; + + try + { + // check whether the component is modified, + // if not there is no need for storing + uno::Reference< util::XModifiable > xModifiable( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModifiable.is() && !xModifiable->isModified() ) + return; + } + catch( const uno::Exception& ) + {} + + try { + m_xClientSite->saveObject(); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "The object was not stored!" ); + } +} + + +OUString OCommonEmbeddedObject::GetBaseURL_Impl() const +{ + OUString aBaseURL; + + if ( m_xClientSite.is() ) + { + try + { + uno::Reference< frame::XModel > xParentModel( m_xClientSite->getComponent(), uno::UNO_QUERY_THROW ); + const uno::Sequence< beans::PropertyValue > aModelProps = xParentModel->getArgs(); + for ( beans::PropertyValue const & prop : aModelProps ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + catch( const uno::Exception& ) + {} + } + + if ( aBaseURL.isEmpty() ) + { + for ( beans::PropertyValue const & prop : m_aDocMediaDescriptor ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + + if ( aBaseURL.isEmpty() ) + aBaseURL = m_aDefaultParentBaseURL; + + return aBaseURL; +} + + +OUString OCommonEmbeddedObject::GetBaseURLFrom_Impl( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + OUString aBaseURL; + + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "DocumentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + + if ( aBaseURL.isEmpty() ) + { + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "DefaultParentBaseURL" ) + { + prop.Value >>= aBaseURL; + break; + } + } + + return aBaseURL; +} + + +void OCommonEmbeddedObject::SwitchDocToStorage_Impl( const uno::Reference< document::XStorageBasedDocument >& xDoc, const uno::Reference< embed::XStorage >& xStorage ) +{ + xDoc->switchToStorage( xStorage ); + + uno::Reference< util::XModifiable > xModif( xDoc, uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + if ( m_xRecoveryStorage.is() ) + m_xRecoveryStorage.clear(); +} + +namespace { + +OUString getStringPropertyValue( const uno::Sequence<beans::PropertyValue>& rProps, std::u16string_view rName ) +{ + OUString aStr; + + for (beans::PropertyValue const & prop : rProps) + { + if (prop.Name == rName) + { + prop.Value >>= aStr; + break; + } + } + + return aStr; +} + +} + +void OCommonEmbeddedObject::StoreDocToStorage_Impl( + const uno::Reference<embed::XStorage>& xStorage, + const uno::Sequence<beans::PropertyValue>& rMediaArgs, + const uno::Sequence<beans::PropertyValue>& rObjArgs, + sal_Int32 nStorageFormat, + const OUString& aHierarchName, + bool bAttachToTheStorage ) +{ + SAL_WARN_IF( !xStorage.is(), "embeddedobj.common", "No storage is provided for storing!" ); + + if ( !xStorage.is() ) + throw uno::RuntimeException(); // TODO: + + uno::Reference< document::XStorageBasedDocument > xDoc; + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_xDocHolder.is() ) + xDoc.set( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + } + + OUString aBaseURL = GetBaseURLFrom_Impl(rMediaArgs, rObjArgs); + + if ( xDoc.is() ) + { + OUString aFilterName = GetFilterName( nStorageFormat ); + + // No filter found? Try the older format, e.g. Base has only that. + if (aFilterName.isEmpty() && nStorageFormat == SOFFICE_FILEFORMAT_CURRENT) + aFilterName = GetFilterName( SOFFICE_FILEFORMAT_60 ); + + SAL_WARN_IF( aFilterName.isEmpty(), "embeddedobj.common", "Wrong document service name!" ); + if ( aFilterName.isEmpty() ) + throw io::IOException(); // TODO: + + static constexpr OUStringLiteral sFilterName = u"FilterName"; + static constexpr OUStringLiteral sHierarchicalDocumentName = u"HierarchicalDocumentName"; + static constexpr OUStringLiteral sDocumentBaseURL = u"DocumentBaseURL"; + static constexpr OUStringLiteral sSourceShellID = u"SourceShellID"; + static constexpr OUStringLiteral sDestinationShellID = u"DestinationShellID"; + uno::Sequence<beans::PropertyValue> aArgs{ + comphelper::makePropertyValue(sFilterName, aFilterName), + comphelper::makePropertyValue(sHierarchicalDocumentName, aHierarchName), + comphelper::makePropertyValue(sDocumentBaseURL, aBaseURL), + comphelper::makePropertyValue(sSourceShellID, + getStringPropertyValue(rObjArgs, sSourceShellID)), + comphelper::makePropertyValue( + sDestinationShellID, getStringPropertyValue(rObjArgs, sDestinationShellID)) + }; + + xDoc->storeToStorage( xStorage, aArgs ); + if ( bAttachToTheStorage ) + SwitchDocToStorage_Impl( xDoc, xStorage ); + } + else + { + // store document to temporary stream based on temporary file + uno::Reference < io::XInputStream > xTempIn = StoreDocumentToTempStream_Impl( nStorageFormat, aBaseURL, aHierarchName ); + + SAL_WARN_IF( !xTempIn.is(), "embeddedobj.common", "The stream reference can not be empty!" ); + + // open storage based on document temporary file for reading + uno::Reference < lang::XSingleServiceFactory > xStorageFactory = embed::StorageFactory::create(m_xContext); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTempIn) }; + uno::Reference< embed::XStorage > xTempStorage( xStorageFactory->createInstanceWithArguments( aArgs ), + uno::UNO_QUERY_THROW ); + + // object storage must be committed automatically + xTempStorage->copyToStorage( xStorage ); + } +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateDocFromMediaDescr_Impl( + const uno::Sequence< beans::PropertyValue >& aMedDescr ) +{ + uno::Reference< util::XCloseable > xDocument( CreateDocument( m_xContext, GetDocumentServiceName(), + m_bEmbeddedScriptSupport, m_bDocumentRecoverySupport ) ); + + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + + try + { + // set the document mode to embedded as the first action on the document!!! + EmbedAndReparentDoc_Impl( xDocument ); + + xLoadable->load( addAsTemplate( aMedDescr ) ); + } + catch( const uno::Exception& ) + { + if ( xDocument.is() ) + { + try + { + xDocument->close( true ); + } + catch( const uno::Exception& ) + { + } + } + + throw; // TODO + } + + return xDocument; +} + + +uno::Reference< util::XCloseable > OCommonEmbeddedObject::CreateTempDocFromLink_Impl() +{ + uno::Reference< util::XCloseable > xResult; + + SAL_WARN_IF( !m_bIsLinkURL, "embeddedobj.common", "The object is not a linked one!" ); + + uno::Sequence< beans::PropertyValue > aTempMediaDescr; + + sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" ); + } + + if ( m_xDocHolder->GetComponent().is() ) + { + aTempMediaDescr.realloc( 4 ); + + // TODO/LATER: may be private:stream should be used as target URL + OUString aTempFileURL; + uno::Reference< io::XInputStream > xTempStream = StoreDocumentToTempStream_Impl( SOFFICE_FILEFORMAT_CURRENT, + OUString(), + OUString() ); + try + { + // no need to let the file stay after the stream is removed since the embedded document + // can not be stored directly + uno::Reference< beans::XPropertySet > xTempStreamProps( xTempStream, uno::UNO_QUERY_THROW ); + xTempStreamProps->getPropertyValue("Uri") >>= aTempFileURL; + } + catch( const uno::Exception& ) + { + } + + SAL_WARN_IF( aTempFileURL.isEmpty(), "embeddedobj.common", "Couldn't retrieve temporary file URL!" ); + + aTempMediaDescr + = { comphelper::makePropertyValue("URL", aTempFileURL), + comphelper::makePropertyValue("InputStream", xTempStream), + comphelper::makePropertyValue("FilterName", GetFilterName( nStorageFormat )), + comphelper::makePropertyValue("AsTemplate", true) }; + } + else + { + aTempMediaDescr = { comphelper::makePropertyValue( + "URL", + // tdf#141529 use URL of the linked TempFile if it exists + m_aLinkTempFile.is() ? m_aLinkTempFile->getUri() : m_aLinkURL), + comphelper::makePropertyValue("FilterName", m_aLinkFilterName) }; + } + + xResult = CreateDocFromMediaDescr_Impl( aTempMediaDescr ); + + return xResult; +} + + +void SAL_CALL OCommonEmbeddedObject::setPersistentEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // the type of the object must be already set + // a kind of typedetection should be done in the factory + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // May be LOADED should be forbidden here ??? + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + // if the object is not loaded + // it can not get persistent representation without initialization + + // if the object is loaded + // it can switch persistent representation only without initialization + + throw embed::WrongStateException( + "Can't change persistent representation of activated object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + { + if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + // saveCompleted is expected, handle it accordingly + if ( m_xNewParentStorage == xStorage && m_aNewEntryName == sEntName ) + { + saveCompleted( true ); + return; + } + + // if a completely different entry is provided, switch first back to the old persistence in saveCompleted + // and then switch to the target persistence + bool bSwitchFurther = ( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + saveCompleted( false ); + if ( !bSwitchFurther ) + return; + } + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + m_aEntryName = sEntName; + return; + } + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + bool bElExists = xNameAccess->hasByName( sEntName ); + + m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, + nEntryConnectionMode != embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ); + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + + // TODO: use lObjArgs for StoreVisualReplacement + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + if ( prop.Value >>= xDispatchInterceptor ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); + } + else if ( prop.Name == "DefaultParentBaseURL" ) + { + prop.Value >>= m_aDefaultParentBaseURL; + } + else if ( prop.Name == "Parent" ) + { + prop.Value >>= m_xParent; + } + else if ( prop.Name == "IndividualMiscStatus" ) + { + sal_Int64 nMiscStatus=0; + prop.Value >>= nMiscStatus; + m_nMiscStatus |= nMiscStatus; + } + else if ( prop.Name == "CloneFrom" ) + { + uno::Reference < embed::XEmbeddedObject > xObj; + prop.Value >>= xObj; + if ( xObj.is() ) + { + m_bHasClonedSize = true; + m_aClonedSize = xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + m_nClonedMapUnit = xObj->getMapUnit( embed::Aspects::MSOLE_CONTENT ); + } + } + else if ( prop.Name == "OutplaceFrameProperties" ) + { + uno::Sequence< uno::Any > aOutFrameProps; + uno::Sequence< beans::NamedValue > aOutFramePropsTyped; + if ( prop.Value >>= aOutFrameProps ) + { + m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps ); + } + else if ( prop.Value >>= aOutFramePropsTyped ) + { + aOutFrameProps.realloc( aOutFramePropsTyped.getLength() ); + uno::Any* pProp = aOutFrameProps.getArray(); + for ( const beans::NamedValue* pTypedProp = aOutFramePropsTyped.getConstArray(); + pTypedProp != aOutFramePropsTyped.getConstArray() + aOutFramePropsTyped.getLength(); + ++pTypedProp, ++pProp + ) + { + *pProp <<= *pTypedProp; + } + m_xDocHolder->SetOutplaceFrameProperties( aOutFrameProps ); + } + else + SAL_WARN( "embeddedobj.common", "OCommonEmbeddedObject::setPersistentEntry: illegal type for argument 'OutplaceFrameProperties'!" ); + } + else if ( prop.Name == "ModuleName" ) + { + prop.Value >>= m_aModuleName; + } + else if ( prop.Name == "EmbeddedScriptSupport" ) + { + OSL_VERIFY( prop.Value >>= m_bEmbeddedScriptSupport ); + } + else if ( prop.Name == "DocumentRecoverySupport" ) + { + OSL_VERIFY( prop.Value >>= m_bDocumentRecoverySupport ); + } + else if ( prop.Name == "RecoveryStorage" ) + { + OSL_VERIFY( prop.Value >>= m_xRecoveryStorage ); + } + + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + SwitchOwnPersistence( xStorage, sEntName ); + + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT ) + { + if ( bElExists ) + { + // the initialization from existing storage allows to leave object in loaded state + m_nObjectState = embed::EmbedStates::LOADED; + } + else + { + m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly ); + if ( !m_xDocHolder->GetComponent().is() ) + throw io::IOException(); // TODO: can not create document + + m_nObjectState = embed::EmbedStates::RUNNING; + } + } + else + { + if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE ) + throw io::IOException(); + + if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // the document just already changed its storage to store to + // the links to OOo documents for now ignore this call + // TODO: OOo links will have persistence so it will be switched here + } + else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT ) + { + if ( m_xRecoveryStorage.is() ) + TransferMediaType( m_xRecoveryStorage, m_xObjectStorage ); + + // TODO: + m_xDocHolder->SetComponent( InitNewDocument_Impl(), m_bReadOnly ); + + if ( !m_xDocHolder->GetComponent().is() ) + throw io::IOException(); // TODO: can not create document + + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + m_xDocHolder->SetComponent( CreateDocFromMediaDescr_Impl( lArguments ), m_bReadOnly ); + m_nObjectState = embed::EmbedStates::RUNNING; + } + //else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT ) + //{ + //TODO: + //} + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + } +} + + +void SAL_CALL OCommonEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + return; + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" ); + } + if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + try + { + nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" ); + } + + bool bTryOptimization = false; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + // StoreVisualReplacement and VisualReplacement args have no sense here + if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + bool bSwitchBackToLoaded = false; + + // Storing to different format can be done only in running state. + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // TODO/LATER: copying is not legal for documents with relative links. + if ( nTargetStorageFormat == nOriginalStorageFormat ) + { + bool bOptimizationWorks = false; + if ( bTryOptimization ) + { + try + { + // try to use optimized copying + uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW ); + xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName ); + bOptimizationWorks = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizationWorks ) + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + else + { + changeState( embed::EmbedStates::RUNNING ); + bSwitchBackToLoaded = true; + } + } + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + return; + + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE ); + + if ( !xSubStorage.is() ) + throw uno::RuntimeException(); //TODO + + aGuard.clear(); + // TODO/LATER: support hierarchical name for embedded objects in embedded objects + StoreDocToStorage_Impl( + xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false ); + aGuard.reset(); + + if ( bSwitchBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + // TODO: should the listener notification be done? +} + + +void SAL_CALL OCommonEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + bool AutoSaveEvent = false; + utl::MediaDescriptor lArgs(lObjArgs); + lArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] >>= AutoSaveEvent; + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + m_aNewEntryName = sEntName; + + if ( !AutoSaveEvent ) + handleLinkedOLE(CopyBackToOLELink::CopyTempToLink); + + return; + } + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + sal_Int32 nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + sal_Int32 nOriginalStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nTargetStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( xStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve target storage media type!" ); + } + if (nTargetStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nTargetStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + try + { + nOriginalStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve own storage media type!" ); + } + + PostEvent_Impl( "OnSaveAs" ); + + bool bTryOptimization = false; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + // StoreVisualReplacement and VisualReplacement args have no sense here + if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + bool bSwitchBackToLoaded = false; + + // Storing to different format can be done only in running state. + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // TODO/LATER: copying is not legal for documents with relative links. + if ( nTargetStorageFormat == nOriginalStorageFormat ) + { + bool bOptimizationWorks = false; + if ( bTryOptimization ) + { + try + { + // try to use optimized copying + uno::Reference< embed::XOptimizedStorage > xSource( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTarget( xStorage, uno::UNO_QUERY_THROW ); + xSource->copyElementDirectlyTo( m_aEntryName, xTarget, sEntName ); + bOptimizationWorks = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizationWorks ) + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + else + { + changeState( embed::EmbedStates::RUNNING ); + bSwitchBackToLoaded = true; + } + } + + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READWRITE ); + + if ( !xSubStorage.is() ) + throw uno::RuntimeException(); //TODO + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + aGuard.clear(); + // TODO/LATER: support hierarchical name for embedded objects in embedded objects + StoreDocToStorage_Impl( + xSubStorage, lArguments, lObjArgs, nTargetStorageFormat, sEntName, false ); + aGuard.reset(); + + if ( bSwitchBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + } + + m_bWaitSaveCompleted = true; + m_xNewObjectStorage = xSubStorage; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; + m_aNewDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true ); + + // TODO: register listeners for storages above, in case they are disposed + // an exception will be thrown on saveCompleted( true ) + + // TODO: should the listener notification be done here or in saveCompleted? +} + + +void SAL_CALL OCommonEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + // for now support of this interface is required to allow breaking of links and converting them to normal embedded + // objects, so the persist name must be handled correctly ( althowgh no real persist entry is used ) + // OSL_ENSURE( !m_bIsLinkURL, "This method implementation must not be used for links!" ); + if ( m_bIsLinkURL ) + { + if ( bUseNew ) + m_aEntryName = m_aNewEntryName; + m_aNewEntryName.clear(); + return; + } + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.common", "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewObjectStorage.is() && m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewObjectStorage.is() || !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStorage, m_aNewEntryName ); + m_aDocMediaDescriptor = m_aNewDocMediaDescriptor; + + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + PostEvent_Impl( "OnSaveAsDone"); + } + else + { + try { + m_xNewObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + m_xNewObjectStorage.clear(); + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_aNewDocMediaDescriptor.realloc( 0 ); + m_bWaitSaveCompleted = false; + + if ( bUseNew ) + { + // TODO: notify listeners + + if ( m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE ) + { + // TODO: update visual representation + } + } +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::hasEntry() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_xObjectStorage.is() ) + return true; + + return false; +} + + +OUString SAL_CALL OCommonEmbeddedObject::getEntryName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL OCommonEmbeddedObject::storeOwn() +{ + // during switching from Activated to Running and from Running to Loaded states the object will + // ask container to store the object, the container has to make decision + // to do so or not + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: access denied + + // nothing to do, if the object is in loaded state + if ( m_nObjectState == embed::EmbedStates::LOADED ) + return; + + PostEvent_Impl( "OnSave" ); + + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If an object is activated or in running state it must have a document!" ); + if ( !m_xDocHolder->GetComponent().is() ) + throw uno::RuntimeException(); + + if ( m_bIsLinkURL ) + { + // TODO: just store the document to its location + uno::Reference< frame::XStorable > xStorable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + // free the main mutex for the storing time + aGuard.clear(); + + xStorable->store(); + + aGuard.reset(); + } + else + { + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStorage.is(), "The object has no valid persistence!" ); + + if ( !m_xObjectStorage.is() ) + throw io::IOException(); //TODO: access denied + + sal_Int32 nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + try { + nStorageFormat = ::comphelper::OStorageHelper::GetXStorageFormat( m_xParentStorage ); + } + catch ( const beans::IllegalTypeException& ) + { + // the container just has an unknown type, use current file format + } + catch ( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.common", "Can not retrieve storage media type!" ); + } + if (nStorageFormat == SOFFICE_FILEFORMAT_60) + { + SAL_INFO("embeddedobj.common", "fdo#78159: Storing OOoXML as ODF"); + nStorageFormat = SOFFICE_FILEFORMAT_CURRENT; + // setting MediaType is done later anyway, no need to do it here + } + + aGuard.clear(); + uno::Sequence<beans::PropertyValue> aEmpty; + uno::Sequence<beans::PropertyValue> aMediaArgs{ comphelper::makePropertyValue( + "DocumentBaseURL", GetBaseURL_Impl()) }; + StoreDocToStorage_Impl( m_xObjectStorage, aMediaArgs, aEmpty, nStorageFormat, m_aEntryName, true ); + aGuard.reset(); + } + + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( xModif.is() ) + xModif->setModified( false ); + + PostEvent_Impl( "OnSaveDone" ); +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::isReadonly() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_bReadOnly; +} + + +void SAL_CALL OCommonEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // TODO: use lObjArgs + // for now this method is used only to switch readonly state + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + // the object is still not loaded + throw embed::WrongStateException( + "The object must be in loaded state to be reloaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bIsLinkURL ) + { + // reload of the link + OUString aOldLinkFilter = m_aLinkFilterName; + + OUString aNewLinkFilter; + for ( beans::PropertyValue const & prop : lArguments ) + { + if ( prop.Name == "URL" ) + { + // the new URL + prop.Value >>= m_aLinkURL; + m_aLinkFilterName.clear(); + } + else if ( prop.Name == "FilterName" ) + { + prop.Value >>= aNewLinkFilter; + m_aLinkFilterName.clear(); + } + } + + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + if ( m_aLinkFilterName.isEmpty() ) + { + if ( !aNewLinkFilter.isEmpty() ) + m_aLinkFilterName = aNewLinkFilter; + else + { + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + "URL", m_aLinkURL) }; + m_aLinkFilterName = aHelper.UpdateMediaDescriptorWithFilterName( aArgs, false ); + } + } + + if ( aOldLinkFilter != m_aLinkFilterName ) + { + uno::Sequence< beans::NamedValue > aObject = aHelper.GetObjectPropsByFilter( m_aLinkFilterName ); + + // TODO/LATER: probably the document holder could be cleaned explicitly as in the destructor + m_xDocHolder.clear(); + + LinkInit_Impl( aObject, lArguments, lObjArgs ); + } + } + + m_aDocMediaDescriptor = GetValuableArgs_Impl( lArguments, true ); + + // TODO: use lObjArgs for StoreVisualReplacement + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "OutplaceDispatchInterceptor" ) + { + uno::Reference< frame::XDispatchProviderInterceptor > xDispatchInterceptor; + if ( prop.Value >>= xDispatchInterceptor ) + m_xDocHolder->SetOutplaceDispatchInterceptor( xDispatchInterceptor ); + + break; + } + + // TODO: + // when document allows reloading through API the object can be reloaded not only in loaded state + + bool bOldReadOnlyValue = m_bReadOnly; + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + + if ( bOldReadOnlyValue == m_bReadOnly || m_bIsLinkURL ) + return; + + // close own storage + try { + if ( m_xObjectStorage.is() ) + m_xObjectStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + m_xObjectStorage = m_xParentStorage->openStorageElement( m_aEntryName, nStorageMode ); +} + +sal_Bool SAL_CALL OCommonEmbeddedObject::isStored() +{ + if (!m_xObjectStorage.is()) + return false; + + return m_xObjectStorage->getElementNames().hasElements(); +} + + +void SAL_CALL OCommonEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName ) +{ + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if (!m_bIsLinkURL || m_nObjectState == -1) + { + // it must be a linked initialized object + throw embed::WrongStateException( + "The object is not a valid linked object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + // the current implementation of OOo links does not implement this method since it does not implement + // all the set of interfaces required for OOo embedded object ( XEmbedPersist is not supported ). + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + m_bReadOnly = false; + + if ( m_xParentStorage != xStorage || m_aEntryName != sEntName ) + SwitchOwnPersistence( xStorage, sEntName ); + + // for linked object it means that it becomes embedded object + // the document must switch it's persistence also + + // TODO/LATER: handle the case when temp doc can not be created + // the document is a new embedded object so it must be marked as modified + uno::Reference< util::XCloseable > xDocument = CreateTempDocFromLink_Impl(); + try + { + if(m_xDocHolder.is() && m_xDocHolder->GetComponent().is()) + { + // tdf#141528 m_xDocHolder->GetComponent() may be not set, so add it + // to the try path to not get thrown out of the local context to the next + // higher try...catch on the stack. To make breakLink work it is + // *necessary* to execute the code below that resets the linked state, + // esp. the *.clear stuff and resetting m_bIsLink. + uno::Reference< util::XModifiable > xModif( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + // all other locations in this file check for xModif.is(), so do it here, too + if ( xModif.is() ) + xModif->setModified( true ); + } + } + catch( const uno::Exception& ) + {} + + m_xDocHolder->SetComponent( xDocument, m_bReadOnly ); + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "If document can't be created, an exception must be thrown!" ); + + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // the state is changed and can not be switched to loaded state back without saving + m_nObjectState = embed::EmbedStates::RUNNING; + StateChangeNotification_Impl( false, embed::EmbedStates::LOADED, m_nObjectState, aGuard ); + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE ) + m_xDocHolder->Show(); + + // tdf#141529 reset all stuff involved in linked state, including + // the OLE content copied to the temp file + m_bIsLinkURL = false; + m_aLinkTempFile.clear(); + m_aLinkFilterName.clear(); + m_aLinkURL.clear(); +} + + +sal_Bool SAL_CALL OCommonEmbeddedObject::isLink() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_bIsLinkURL; +} + + +OUString SAL_CALL OCommonEmbeddedObject::getLinkURL() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_bIsLinkURL ) + throw embed::WrongStateException( + "The object is not a link object!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aLinkURL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/persistence.hxx b/embeddedobj/source/commonembedding/persistence.hxx new file mode 100644 index 000000000..d24aee5e4 --- /dev/null +++ b/embeddedobj/source/commonembedding/persistence.hxx @@ -0,0 +1,28 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/beans/PropertyValue.hpp> + +css::uno::Sequence< css::beans::PropertyValue > GetValuableArgs_Impl( const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr, + bool bCanUseDocumentBaseURL ); + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/specialobject.cxx b/embeddedobj/source/commonembedding/specialobject.cxx new file mode 100644 index 000000000..3dc1eb7e4 --- /dev/null +++ b/embeddedobj/source/commonembedding/specialobject.cxx @@ -0,0 +1,190 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> + +#include <cppuhelper/queryinterface.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/supportsservice.hxx> + +#include <specialobject.hxx> + +using namespace ::com::sun::star; + + +OSpecialEmbeddedObject::OSpecialEmbeddedObject( const uno::Reference< uno::XComponentContext >& rxContext, const uno::Sequence< beans::NamedValue >& aObjectProps ) +: OCommonEmbeddedObject( rxContext, aObjectProps ) +{ + maSize.Width = maSize.Height = 10000; + m_nObjectState = embed::EmbedStates::LOADED; +} + + +uno::Any SAL_CALL OSpecialEmbeddedObject::queryInterface( const uno::Type& rType ) +{ + uno::Any aReturn = ::cppu::queryInterface( rType, + static_cast< embed::XEmbeddedObject* >( this ), + static_cast< embed::XInplaceObject* >( this ), + static_cast< embed::XCommonEmbedPersist* >( static_cast< embed::XEmbedPersist* >( this ) ), + static_cast< embed::XVisualObject* >( this ), + static_cast< embed::XClassifiedObject* >( this ), + static_cast< embed::XComponentSupplier* >( this ), + static_cast< util::XCloseable* >( this ), + static_cast< lang::XServiceInfo* >( this ), + static_cast< lang::XTypeProvider* >( this ), + static_cast< document::XEventBroadcaster* >( this ) ); + if ( aReturn.hasValue() ) + return aReturn; + else + return ::cppu::OWeakObject::queryInterface( rType ) ; + +} + + +embed::VisualRepresentation SAL_CALL OSpecialEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + // TODO: if object is in loaded state it should switch itself to the running state + if ( m_nObjectState == -1 || m_nObjectState == embed::EmbedStates::LOADED ) + throw embed::WrongStateException( "The own object has no model!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: return for the aspect of the document + embed::VisualRepresentation aVisualRepresentation; + return aVisualRepresentation; +} + +void SAL_CALL OSpecialEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + maSize = aSize; +} + +awt::Size SAL_CALL OSpecialEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no model!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return maSize; +} + +sal_Int32 SAL_CALL OSpecialEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + +void SAL_CALL OSpecialEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + if ( nNewState == embed::EmbedStates::UI_ACTIVE ) + nNewState = embed::EmbedStates::INPLACE_ACTIVE; + OCommonEmbeddedObject::changeState( nNewState ); +} + +void SAL_CALL OSpecialEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( nVerbID == -7 ) + { + + uno::Reference < ui::dialogs::XExecutableDialog > xDlg( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if ( !xDlg.is() ) + throw embed::UnreachableStateException(); + xDlg->execute(); + } + else + OCommonEmbeddedObject::doVerb( nVerbID ); +} + +void SAL_CALL OSpecialEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >&, + const uno::Sequence< beans::PropertyValue >&) +{ + // Allow IFrames to reload their content + SetInplaceActiveState(); +} + +OUString SAL_CALL OSpecialEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OSpecialEmbeddedObject"; +} + +sal_Bool SAL_CALL OSpecialEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OSpecialEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OSpecialEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/visobj.cxx b/embeddedobj/source/commonembedding/visobj.cxx new file mode 100644 index 000000000..851457609 --- /dev/null +++ b/embeddedobj/source/commonembedding/visobj.cxx @@ -0,0 +1,209 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/lang/DisposedException.hpp> + + +#include <commonembobj.hxx> +#include <sal/log.hxx> + + +using namespace ::com::sun::star; + +void SAL_CALL OCommonEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_bHasClonedSize = false; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + bool bSuccess = m_xDocHolder->SetExtent( nAspect, aSize ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( !bSuccess ) + throw uno::Exception("SetExtent failed", nullptr); // TODO: +} + +awt::Size SAL_CALL OCommonEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + + if ( m_bHasClonedSize ) + return m_aClonedSize; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + awt::Size aResult; + bool bSuccess = m_xDocHolder->GetExtent( nAspect, &aResult ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( !bSuccess ) + throw uno::Exception("GetExtent failed", nullptr); // TODO: + + return aResult; +} + +sal_Int32 SAL_CALL OCommonEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bHasClonedSize ) + return m_nClonedMapUnit; + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + changeState( embed::EmbedStates::RUNNING ); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + sal_Int32 nResult = m_xDocHolder->GetMapUnit( nAspect ); + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + if ( nResult < 0 ) + throw uno::Exception("result " + OUString::number(nResult), nullptr); // TODO: + + return nResult; +} + +embed::VisualRepresentation SAL_CALL OCommonEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The own object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.common", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + bool bBackToLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // restore original VisualAreaSize, because writer objects set + // themselves to a default size OLESIZE + awt::Size aOrigSize = getVisualAreaSize(nAspect); + changeState(embed::EmbedStates::RUNNING); + const bool bIsChart = GetDocumentServiceName() == "com.sun.star.chart2.ChartDocument"; + // tdf#108643 unless it's a chart, cause those are weird (#i103460#) + if (!bIsChart && aOrigSize != getVisualAreaSize(nAspect)) + setVisualAreaSize(nAspect, aOrigSize); + + // the links should be switched back to loaded state for now to avoid locking problems + bBackToLoaded = m_bIsLinkURL; + } + + SAL_WARN_IF( !m_xDocHolder->GetComponent().is(), "embeddedobj.common", "Running or Active object has no component!" ); + + // TODO: return for the aspect of the document + embed::VisualRepresentation aVisualRepresentation; + + uno::Reference< embed::XVisualObject > xVisualObject( m_xDocHolder->GetComponent(), uno::UNO_QUERY ); + if( xVisualObject.is()) + { + aVisualRepresentation = xVisualObject->getPreferredVisualRepresentation( nAspect ); + } + else + { + uno::Reference< datatransfer::XTransferable > xTransferable( m_xDocHolder->GetComponent(), uno::UNO_QUERY_THROW ); + + datatransfer::DataFlavor aDataFlavor( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", + "GDIMetaFile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + if( !xTransferable->isDataFlavorSupported( aDataFlavor )) + throw uno::RuntimeException(); + aVisualRepresentation.Data = xTransferable->getTransferData( aDataFlavor ); + aVisualRepresentation.Flavor = aDataFlavor; + } + + if ( bBackToLoaded ) + changeState( embed::EmbedStates::LOADED ); + + return aVisualRepresentation; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/xfactory.cxx b/embeddedobj/source/commonembedding/xfactory.cxx new file mode 100644 index 000000000..c6f57aa9c --- /dev/null +++ b/embeddedobj/source/commonembedding/xfactory.cxx @@ -0,0 +1,417 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/documentconstants.hxx> + +#include "xfactory.hxx" +#include <commonembobj.hxx> +#include <specialobject.hxx> + + +using namespace ::com::sun::star; + + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + if ( !xNameAccess->hasByName( sEntName ) ) + throw container::NoSuchElementException(); + + uno::Reference< uno::XInterface > xResult; + if ( !xStorage->isStorageElement( sEntName ) ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + // the object must be based on storage + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READ ); + + uno::Reference< beans::XPropertySet > xPropSet( xSubStorage, uno::UNO_QUERY_THROW ); + + OUString aMediaType; + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + } + catch ( const uno::Exception& ) + { + } + + try { + if ( xSubStorage.is() ) + xSubStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + xSubStorage.clear(); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByMediaType( aMediaType ); + + // If the sequence is empty, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (!aObject.hasElements() && aMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) + aObject = m_aConfigHelper.GetObjectPropsByMediaType(MIMETYPE_VND_SUN_XML_BASE_ASCII); + + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is FilterName + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + uno::Reference< uno::XInterface > xResult; + + // find document service name + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByFilter( aFilterName ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aTempMedDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceInitNew( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 4 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + xResult.set( static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::TRUNCATE_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 2 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + uno::Sequence< beans::PropertyValue > aTempMedDescr( lArguments ); + if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, aObject ); + if ( aFilterName.isEmpty() ) + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + nEntryConnectionMode, + aTempMedDescr, + lObjArgs ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceLink( + const uno::Reference< embed::XStorage >& /*xStorage*/, + const OUString& /*sEntName*/, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is URL, URL must exist + OUString aURL; + for ( beans::PropertyValue const & prop : std::as_const(aTempMedDescr) ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( "No URL for the link is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByFilter( aFilterName ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject, + aTempMedDescr, + lObjArgs ) ), + uno::UNO_QUERY ); + + return xResult; +} + +uno::Reference< uno::XInterface > SAL_CALL OOoEmbeddedObjectFactory::createInstanceLinkUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 2 ); + + uno::Sequence< beans::PropertyValue > aTempMedDescr( lArguments ); + + OUString aURL; + for ( beans::PropertyValue const & prop : std::as_const(aTempMedDescr) ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( "No URL for the link is provided!", + uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this) ), + 3 ); + + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, aObject ); + + if ( aFilterName.isEmpty() ) + { + // the object must be OOo embedded object, if it is not an exception must be thrown + throw io::IOException(); // TODO: + } + + xResult.set(static_cast< ::cppu::OWeakObject* > ( new OCommonEmbeddedObject( + m_xContext, + aObject, + aTempMedDescr, + lObjArgs ) ), + uno::UNO_QUERY ); + + return xResult; +} + +OUString SAL_CALL OOoEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OOoEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OOoEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OOoEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OOoEmbeddedObjectFactory", "com.sun.star.comp.embed.OOoEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OOoEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OOoEmbeddedObjectFactory(context)); +} + + +uno::Reference< uno::XInterface > SAL_CALL OOoSpecialEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& /*aClassName*/, + const uno::Reference< embed::XStorage >& /*xStorage*/, + const OUString& /*sEntName*/, + sal_Int32 /*nEntryConnectionMode*/, + const uno::Sequence< beans::PropertyValue >& /*lArguments*/, + const uno::Sequence< beans::PropertyValue >& /*lObjArgs*/ ) +{ + uno::Sequence< beans::NamedValue > aObject = m_aConfigHelper.GetObjectPropsByClassID( aClassID ); + if ( !aObject.hasElements() ) + throw io::IOException(); // unexpected mimetype of the storage + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OSpecialEmbeddedObject( + m_xContext, + aObject ) ), + uno::UNO_QUERY ); + return xResult; +} + +OUString SAL_CALL OOoSpecialEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OOoSpecialEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OOoSpecialEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OOoSpecialEmbeddedObjectFactory", "com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OOoSpecialEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OOoSpecialEmbeddedObjectFactory(context)); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/commonembedding/xfactory.hxx b/embeddedobj/source/commonembedding/xfactory.hxx new file mode 100644 index 000000000..056f50705 --- /dev/null +++ b/embeddedobj/source/commonembedding/xfactory.hxx @@ -0,0 +1,94 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/XLinkFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <comphelper/mimeconfighelper.hxx> + +class OOoEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::embed::XLinkFactory, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; + +public: + explicit OOoEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_aConfigHelper( rxContext ) + { + OSL_ENSURE( rxContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitNew( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromMediaDescriptor( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLink( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLinkUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +class OOoSpecialEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbedObjectFactory, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; + +public: + explicit OOoSpecialEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_aConfigHelper( rxContext ) + { + OSL_ENSURE( rxContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/docholder.cxx b/embeddedobj/source/general/docholder.cxx new file mode 100644 index 000000000..ee1a8dfa6 --- /dev/null +++ b/embeddedobj/source/general/docholder.cxx @@ -0,0 +1,1278 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/frame/TaskCreator.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XSynchronousFrameLoader.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/XControllerBorder.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/frame/XDispatchProviderInterception.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/embed/XHatchWindow.hpp> +#include <com/sun/star/embed/HatchWindowFactory.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XMenuBarMergingAcceptor.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/ui/XDockingAreaAcceptor.hpp> +#include <com/sun/star/ui/XUIElementSettings.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> + +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <unotools/resmgr.hxx> +#include <sfx2/strings.hrc> + +#include <comphelper/processfactory.hxx> +#include <comphelper/namedvaluecollection.hxx> + +#include <docholder.hxx> +#include <commonembobj.hxx> +#include <intercept.hxx> + +#define HATCH_BORDER_WIDTH (((m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE) && \ + m_pEmbedObj->getCurrentState()!=embed::EmbedStates::UI_ACTIVE) ? 0 : 4 ) + +using namespace ::com::sun::star; + +namespace { + +class IntCounterGuard +{ + sal_Int32& m_rFlag; +public: + explicit IntCounterGuard(sal_Int32& rFlag) + : m_rFlag(rFlag) + { + ++m_rFlag; + } + + ~IntCounterGuard() + { + if (m_rFlag) + --m_rFlag; + } +}; + +} + +static void InsertMenu_Impl( const uno::Reference< container::XIndexContainer >& xTargetMenu, + sal_Int32 nTargetIndex, + const uno::Reference< container::XIndexAccess >& xSourceMenu, + sal_Int32 nSourceIndex, + const OUString& aContModuleName, + const uno::Reference< frame::XDispatchProvider >& xSourceDisp ) +{ + sal_Int32 nInd = 0; + OUString aModuleIdentPropName( "ModuleIdentifier" ); + OUString aDispProvPropName( "DispatchProvider" ); + bool bModuleNameSet = false; + bool bDispProvSet = false; + + uno::Sequence< beans::PropertyValue > aSourceProps; + xSourceMenu->getByIndex( nSourceIndex ) >>= aSourceProps; + uno::Sequence< beans::PropertyValue > aTargetProps( aSourceProps.getLength() ); + auto aTargetPropsRange = asNonConstRange(aTargetProps); + for ( nInd = 0; nInd < aSourceProps.getLength(); nInd++ ) + { + aTargetPropsRange[nInd].Name = aSourceProps[nInd].Name; + if ( !aContModuleName.isEmpty() && aTargetProps[nInd].Name == aModuleIdentPropName ) + { + aTargetPropsRange[nInd].Value <<= aContModuleName; + bModuleNameSet = true; + } + else if ( aTargetProps[nInd].Name == aDispProvPropName ) + { + aTargetPropsRange[nInd].Value <<= xSourceDisp; + bDispProvSet = true; + } + else + aTargetPropsRange[nInd].Value = aSourceProps[nInd].Value; + } + + if ( !bModuleNameSet && !aContModuleName.isEmpty() ) + { + aTargetProps.realloc( ++nInd ); + auto pTargetProps = aTargetProps.getArray(); + pTargetProps[nInd-1].Name = aModuleIdentPropName; + pTargetProps[nInd-1].Value <<= aContModuleName; + } + + if ( !bDispProvSet && xSourceDisp.is() ) + { + aTargetProps.realloc( ++nInd ); + auto pTargetProps = aTargetProps.getArray(); + pTargetProps[nInd-1].Name = aDispProvPropName; + pTargetProps[nInd-1].Value <<= xSourceDisp; + } + + xTargetMenu->insertByIndex( nTargetIndex, uno::Any( aTargetProps ) ); +} + + +DocumentHolder::DocumentHolder( const uno::Reference< uno::XComponentContext >& xContext, + OCommonEmbeddedObject* pEmbObj ) +: m_pEmbedObj( pEmbObj ), + m_xContext( xContext ), + m_bReadOnly( false ), + m_bWaitForClose( false ), + m_bAllowClosing( false ), + m_bDesktopTerminated( false ), + m_nNoBorderResizeReact( 0 ), + m_nNoResizeReact( 0 ) +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext ); + osl_atomic_increment(&m_refCount); + try + { + xDesktop->addTerminateListener( this ); + } + catch ( const uno::Exception& ) + { + } + osl_atomic_decrement(&m_refCount); + + m_aOutplaceFrameProps = { uno::Any(beans::NamedValue{ "TopWindow", uno::Any(true) }), + uno::Any(beans::NamedValue{ "MakeVisible", uno::Any(false) }), + //TODO/LATER: should use parent document frame + uno::Any(beans::NamedValue{ "ParentFrame", uno::Any(xDesktop) }) }; +} + + +DocumentHolder::~DocumentHolder() +{ + osl_atomic_increment(&m_refCount); // to allow deregistration as a listener + + if( m_xFrame.is() ) + CloseFrame(); + + if ( m_xComponent.is() ) + { + try { + CloseDocument( true, false ); + } catch( const uno::Exception& ) {} + } + + if ( m_xInterceptor.is() ) + { + m_xInterceptor->DisconnectDocHolder(); + m_xInterceptor.clear(); + } + + if ( !m_bDesktopTerminated ) + FreeOffice(); +} + + +void DocumentHolder::CloseFrame() +{ + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + + uno::Reference<util::XCloseable> xCloseable( + m_xFrame,uno::UNO_QUERY ); + if( xCloseable.is() ) + try { + xCloseable->close( true ); + } + catch( const uno::Exception& ) { + } + else { + if( m_xFrame.is() ) + m_xFrame->dispose(); + } + + if ( m_xHatchWindow.is() ) + m_xHatchWindow->dispose(); + + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); +} + + +void DocumentHolder::FreeOffice() +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext ); + xDesktop->removeTerminateListener( this ); + + // the following code is commented out since for now there is still no completely correct way to detect + // whether the office can be terminated, so it is better to have unnecessary process running than + // to lose any data + +// uno::Reference< frame::XFramesSupplier > xFramesSupplier( xDesktop, uno::UNO_QUERY ); +// if ( xFramesSupplier.is() ) +// { +// uno::Reference< frame::XFrames > xFrames = xFramesSupplier->getFrames(); +// if ( xFrames.is() && !xFrames->hasElements() ) +// { +// try +// { +// xDesktop->terminate(); +// } +// catch( uno::Exception & ) +// {} +// } +// } +} + + +void DocumentHolder::CloseDocument( bool bDeliverOwnership, bool bWaitForClose ) +{ + if ( m_xComponent.is() ) + { + uno::Reference< document::XEventBroadcaster > xEventBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xEventBroadcaster.is() ) + xEventBroadcaster->removeEventListener( static_cast<document::XEventListener*>(this) ); + else + { + // the object does not support document::XEventBroadcaster interface + // use the workaround, register for modified events + uno::Reference< util::XModifyBroadcaster > xModifyBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xModifyBroadcaster.is() ) + xModifyBroadcaster->removeModifyListener( static_cast<util::XModifyListener*>(this) ); + } + + m_bAllowClosing = true; + m_bWaitForClose = bWaitForClose; + m_xComponent->close( bDeliverOwnership ); + } + + m_xComponent = nullptr; +} + + +void DocumentHolder::PlaceFrame( const awt::Rectangle& aNewRect ) +{ + OSL_ENSURE( m_xFrame.is() && m_xOwnWindow.is(), + "The object does not have windows required for inplace mode!" ); + + //TODO: may need mutex locking??? + if ( !(m_xFrame.is() && m_xOwnWindow.is()) ) + return; + + // the frame can be replaced only in inplace mode + frame::BorderWidths aOldWidths; + IntCounterGuard aGuard( m_nNoBorderResizeReact ); + + do + { + aOldWidths = m_aBorderWidths; + + awt::Rectangle aHatchRect = AddBorderToArea( aNewRect ); + + ResizeWindows_Impl( aHatchRect ); + + } while ( aOldWidths.Left != m_aBorderWidths.Left + || aOldWidths.Top != m_aBorderWidths.Top + || aOldWidths.Right != m_aBorderWidths.Right + || aOldWidths.Bottom != m_aBorderWidths.Bottom ); + + m_aObjRect = aNewRect; +} + + +void DocumentHolder::ResizeWindows_Impl( const awt::Rectangle& aHatchRect ) +{ + OSL_ENSURE( m_xFrame.is() && m_xOwnWindow.is() /*&& m_xHatchWindow.is()*/, + "The object does not have windows required for inplace mode!" ); + if ( m_xHatchWindow.is() ) + { + m_xOwnWindow->setPosSize( HATCH_BORDER_WIDTH, + HATCH_BORDER_WIDTH, + aHatchRect.Width - 2*HATCH_BORDER_WIDTH, + aHatchRect.Height - 2*HATCH_BORDER_WIDTH, + awt::PosSize::POSSIZE ); + + + m_xHatchWindow->setPosSize( aHatchRect.X, + aHatchRect.Y, + aHatchRect.Width, + aHatchRect.Height, + awt::PosSize::POSSIZE ); + } + else + m_xOwnWindow->setPosSize( aHatchRect.X + HATCH_BORDER_WIDTH, + aHatchRect.Y + HATCH_BORDER_WIDTH, + aHatchRect.Width - 2*HATCH_BORDER_WIDTH, + aHatchRect.Height - 2*HATCH_BORDER_WIDTH, + awt::PosSize::POSSIZE ); +} + + +bool DocumentHolder::SetFrameLMVisibility( const uno::Reference< frame::XFrame >& xFrame, bool bVisible ) +{ + bool bResult = false; + + try + { + uno::Reference< css::frame::XLayoutManager > xLayoutManager; + uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + xLayoutManager->setVisible( bVisible ); + + // MBA: locking is done only on the container LM, because it is not about hiding windows, it's about + // giving up control over the component window (and stopping to listen for resize events of the container window) + if ( bVisible ) + xLayoutManager->unlock(); + else + xLayoutManager->lock(); + + bResult = true; + } + } + catch( const uno::Exception& ) + {} + + return bResult; +} + + +bool DocumentHolder::ShowInplace( const uno::Reference< awt::XWindowPeer >& xParent, + const awt::Rectangle& aRectangleToShow, + const uno::Reference< frame::XDispatchProvider >& xContDisp ) +{ + OSL_ENSURE( !m_xFrame.is(), "A frame exists already!" ); + + if ( !m_xFrame.is() ) + { + uno::Reference < frame::XModel > xModel( GetComponent(), uno::UNO_QUERY ); + awt::Rectangle aHatchRectangle = AddBorderToArea( aRectangleToShow ); + + awt::Rectangle aOwnRectangle( HATCH_BORDER_WIDTH, + HATCH_BORDER_WIDTH, + aHatchRectangle.Width - 2*HATCH_BORDER_WIDTH, + aHatchRectangle.Height - 2*HATCH_BORDER_WIDTH ); + uno::Reference< awt::XWindow > xHWindow; + uno::Reference< awt::XWindowPeer > xMyParent( xParent ); + + if ( xModel.is() ) + { + + uno::Reference< embed::XHatchWindowFactory > xHatchFactory = + embed::HatchWindowFactory::create(m_xContext); + + uno::Reference< embed::XHatchWindow > xHatchWindow = + xHatchFactory->createHatchWindowInstance( xParent, + aHatchRectangle, + awt::Size( HATCH_BORDER_WIDTH, HATCH_BORDER_WIDTH ) ); + + uno::Reference< awt::XWindowPeer > xHatchWinPeer( xHatchWindow, uno::UNO_QUERY ); + xHWindow.set( xHatchWinPeer, uno::UNO_QUERY_THROW ); + + xHatchWindow->setController( uno::Reference< embed::XHatchWindowController >( + static_cast< embed::XHatchWindowController* >( this ) ) ); + + xMyParent = xHatchWinPeer; + } + else + { + aOwnRectangle.X += aHatchRectangle.X; + aOwnRectangle.Y += aHatchRectangle.Y; + } + + awt::WindowDescriptor aOwnWinDescriptor( awt::WindowClass_TOP, + "dockingwindow", + xMyParent, + 0, + awt::Rectangle(),//aOwnRectangle, + awt::WindowAttribute::SHOW | awt::VclWindowPeerAttribute::CLIPCHILDREN ); + + uno::Reference< awt::XToolkit2 > xToolkit = awt::Toolkit::create(m_xContext); + + uno::Reference< awt::XWindowPeer > xNewWinPeer = xToolkit->createWindow( aOwnWinDescriptor ); + uno::Reference< awt::XWindow > xOwnWindow( xNewWinPeer, uno::UNO_QUERY_THROW ); + uno::Reference< frame::XFrame > xContFrame( xContDisp, uno::UNO_QUERY ); + + // create a frame based on the specified window + uno::Reference< lang::XSingleServiceFactory > xFrameFact = frame::TaskCreator::create(m_xContext); + + uno::Sequence< uno::Any > aArgs( xContFrame.is() ? 2 : 1 ); + auto pArgs = aArgs.getArray(); + beans::NamedValue aArg; + + aArg.Name = "ContainerWindow"; + aArg.Value <<= xOwnWindow; + pArgs[0] <<= aArg; + + if ( xContFrame.is() ) + { + aArg.Name = "ParentFrame"; + aArg.Value <<= xContFrame; + pArgs[1] <<= aArg; + } + + // the call will create, initialize the frame, and register it in the parent + m_xFrame.set( xFrameFact->createInstanceWithArguments( aArgs ), uno::UNO_QUERY_THROW ); + + m_xHatchWindow = xHWindow; + m_xOwnWindow = xOwnWindow; + + if ( !SetFrameLMVisibility( m_xFrame, false ) ) + { + OSL_FAIL( "Can't deactivate LayoutManager!" ); + // TODO/LATER: error handling? + } + + // m_bIsInplace = sal_True; TODO: ? + + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + + // TODO: some listeners to the frame and the window ( resize for example ) + } + + if ( !m_xComponent ) + return false; + + if ( !LoadDocToFrame( true ) ) + { + CloseFrame(); + return false; + } + + uno::Reference< frame::XControllerBorder > xControllerBorder( m_xFrame->getController(), uno::UNO_QUERY ); + if ( xControllerBorder.is() ) + { + m_aBorderWidths = xControllerBorder->getBorder(); + xControllerBorder->addBorderResizeListener( static_cast<frame::XBorderResizeListener*>(this) ); + } + + PlaceFrame( aRectangleToShow ); + + if ( m_xHatchWindow.is() ) + m_xHatchWindow->setVisible( true ); + + return true; +} + + +uno::Reference< container::XIndexAccess > DocumentHolder::RetrieveOwnMenu_Impl() +{ + uno::Reference< container::XIndexAccess > xResult; + + uno::Reference< css::ui::XUIConfigurationManagerSupplier > xUIConfSupplier( + m_xComponent, + uno::UNO_QUERY ); + uno::Reference< css::ui::XUIConfigurationManager > xUIConfigManager; + if( xUIConfSupplier.is()) + { + xUIConfigManager.set( + xUIConfSupplier->getUIConfigurationManager(), + uno::UNO_SET_THROW ); + } + + try + { + if( xUIConfigManager.is()) + { + xResult = xUIConfigManager->getSettings( + "private:resource/menubar/menubar", + false ); + } + } + catch( const uno::Exception& ) + {} + + if ( !xResult.is() ) + { + // no internal document configuration, use the one from the module + uno::Reference< frame::XModuleManager2 > xModuleMan = frame::ModuleManager::create(m_xContext); + OUString aModuleIdent = + xModuleMan->identify( uno::Reference< uno::XInterface >( m_xComponent, uno::UNO_QUERY ) ); + + if ( !aModuleIdent.isEmpty() ) + { + uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModConfSupplier = + ui::theModuleUIConfigurationManagerSupplier::get(m_xContext); + uno::Reference< css::ui::XUIConfigurationManager > xModUIConfMan( + xModConfSupplier->getUIConfigurationManager( aModuleIdent ), + uno::UNO_SET_THROW ); + xResult = xModUIConfMan->getSettings( + "private:resource/menubar/menubar", + false ); + } + } + + if ( !xResult.is() ) + throw uno::RuntimeException(); + + return xResult; +} + + +void DocumentHolder::FindConnectPoints( + const uno::Reference< container::XIndexAccess >& xMenu, + sal_Int32 nConnectPoints[2] ) +{ + nConnectPoints[0] = -1; + nConnectPoints[1] = -1; + for ( sal_Int32 nInd = 0; nInd < xMenu->getCount(); nInd++ ) + { + uno::Sequence< beans::PropertyValue > aProps; + xMenu->getByIndex( nInd ) >>= aProps; + OUString aCommand; + for ( beans::PropertyValue const & prop : std::as_const(aProps) ) + if ( prop.Name == "CommandURL" ) + { + prop.Value >>= aCommand; + break; + } + + if ( aCommand.isEmpty() ) + throw uno::RuntimeException(); + + if ( aCommand == ".uno:PickList" ) + nConnectPoints[0] = nInd; + else if ( aCommand == ".uno:WindowList" ) + nConnectPoints[1] = nInd; + } +} + + +uno::Reference< container::XIndexAccess > DocumentHolder::MergeMenusForInplace( + const uno::Reference< container::XIndexAccess >& xContMenu, + const uno::Reference< frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName, + const uno::Reference< container::XIndexAccess >& xOwnMenu, + const uno::Reference< frame::XDispatchProvider >& xOwnDisp ) +{ + // TODO/LATER: use dispatch providers on merge + + sal_Int32 nContPoints[2]; + sal_Int32 nOwnPoints[2]; + + uno::Reference< lang::XSingleComponentFactory > xIndAccessFact( xContMenu, uno::UNO_QUERY_THROW ); + + uno::Reference< container::XIndexContainer > xMergedMenu( + xIndAccessFact->createInstanceWithContext( + comphelper::getProcessComponentContext() ), + uno::UNO_QUERY_THROW ); + + FindConnectPoints( xContMenu, nContPoints ); + FindConnectPoints( xOwnMenu, nOwnPoints ); + + for ( sal_Int32 nInd = 0; nInd < xOwnMenu->getCount(); nInd++ ) + { + if ( nOwnPoints[0] == nInd ) + { + if ( nContPoints[0] >= 0 && nContPoints[0] < xContMenu->getCount() ) + { + InsertMenu_Impl( xMergedMenu, nInd, xContMenu, nContPoints[0], aContModuleName, xContDisp ); + } + } + else if ( nOwnPoints[1] == nInd ) + { + if ( nContPoints[1] >= 0 && nContPoints[1] < xContMenu->getCount() ) + { + InsertMenu_Impl( xMergedMenu, nInd, xContMenu, nContPoints[1], aContModuleName, xContDisp ); + } + } + else + InsertMenu_Impl( xMergedMenu, nInd, xOwnMenu, nInd, OUString(), xOwnDisp ); + } + + return uno::Reference< container::XIndexAccess >( xMergedMenu, uno::UNO_QUERY_THROW ); +} + + +bool DocumentHolder::MergeMenus_Impl( const uno::Reference< css::frame::XLayoutManager >& xOwnLM, + const uno::Reference< css::frame::XLayoutManager >& xContLM, + const uno::Reference< frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName ) +{ + bool bMenuMerged = false; + try + { + uno::Reference< css::ui::XUIElementSettings > xUISettings( + xContLM->getElement( "private:resource/menubar/menubar" ), + uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xContMenu = xUISettings->getSettings( true ); + if ( !xContMenu.is() ) + throw uno::RuntimeException(); + + uno::Reference< container::XIndexAccess > xOwnMenu = RetrieveOwnMenu_Impl(); + uno::Reference< frame::XDispatchProvider > xOwnDisp( m_xFrame, uno::UNO_QUERY_THROW ); + + uno::Reference< container::XIndexAccess > xMergedMenu = MergeMenusForInplace( xContMenu, xContDisp, aContModuleName, xOwnMenu, xOwnDisp ); + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, + uno::UNO_QUERY_THROW ); + bMenuMerged = xMerge->setMergedMenuBar( xMergedMenu ); + } + catch( const uno::Exception& ) + {} + + return bMenuMerged; +} + +bool DocumentHolder::ShowUI( const uno::Reference< css::frame::XLayoutManager >& xContainerLM, + const uno::Reference< frame::XDispatchProvider >& xContainerDP, + const OUString& aContModuleName ) +{ + bool bResult = false; + if ( xContainerLM.is() ) + { + // the LM of the embedded frame and its current DockingAreaAcceptor + uno::Reference< css::frame::XLayoutManager > xOwnLM; + uno::Reference< css::ui::XDockingAreaAcceptor > xDocAreaAcc; + + try + { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + xDocAreaAcc = xContainerLM->getDockingAreaAcceptor(); + } + catch( const uno::Exception& ){} + + if ( xOwnLM.is() && xDocAreaAcc.is() ) + { + // make sure that lock state of LM is correct even if an exception is thrown in between + bool bUnlockContainerLM = false; + bool bLockOwnLM = false; + try + { + // take over the control over the containers window + // as long as the LM is invisible and locked an empty tool space will be used on resizing + xOwnLM->setDockingAreaAcceptor( xDocAreaAcc ); + + // try to merge menus; don't do anything else if it fails + if ( MergeMenus_Impl( xOwnLM, xContainerLM, xContainerDP, aContModuleName ) ) + { + // make sure that the container LM does not control the size of the containers window anymore + // this must be done after merging menus as we won't get the container menu otherwise + xContainerLM->setDockingAreaAcceptor( uno::Reference < ui::XDockingAreaAcceptor >() ); + + uno::Reference< lang::XServiceInfo> xServiceInfo(m_xComponent, uno::UNO_QUERY); + if (!xServiceInfo.is() || !xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + { + // prevent further changes at this LM + xContainerLM->setVisible(false); + xContainerLM->lock(); + bUnlockContainerLM = true; + } + + // by unlocking the LM each layout change will now resize the containers window; pending layouts will be processed now + xOwnLM->setVisible( true ); + + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( m_xFrame ); + + xOwnLM->unlock(); + bLockOwnLM = true; + bResult = true; + + // TODO/LATER: The following action should be done only if the window is not hidden + // otherwise the activation must fail, unfortunately currently it is not possible + // to detect whether the window is hidden using UNO API + m_xOwnWindow->setFocus(); + } + } + catch( const uno::Exception& ) + { + // activation failed; reestablish old state + try + { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( nullptr ); + + // remove control about containers window from own LM + if (bLockOwnLM) + xOwnLM->lock(); + xOwnLM->setVisible( false ); + xOwnLM->setDockingAreaAcceptor( uno::Reference< css::ui::XDockingAreaAcceptor >() ); + + // unmerge menu + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, uno::UNO_QUERY_THROW ); + xMerge->removeMergedMenuBar(); + } + catch( const uno::Exception& ) {} + + try + { + // reestablish control of containers window + xContainerLM->setDockingAreaAcceptor( xDocAreaAcc ); + xContainerLM->setVisible( true ); + if (bUnlockContainerLM) + xContainerLM->unlock(); + } + catch( const uno::Exception& ) {} + } + } + } + + return bResult; +} + + +bool DocumentHolder::HideUI( const uno::Reference< css::frame::XLayoutManager >& xContainerLM ) +{ + bool bResult = false; + + if ( xContainerLM.is() ) + { + uno::Reference< css::frame::XLayoutManager > xOwnLM; + + try { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + } catch( const uno::Exception& ) + {} + + if ( xOwnLM.is() ) + { + try { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( nullptr ); + + uno::Reference< css::ui::XDockingAreaAcceptor > xDocAreaAcc = xOwnLM->getDockingAreaAcceptor(); + + xOwnLM->setDockingAreaAcceptor( uno::Reference < ui::XDockingAreaAcceptor >() ); + xOwnLM->lock(); + xOwnLM->setVisible( false ); + + uno::Reference< css::frame::XMenuBarMergingAcceptor > xMerge( xOwnLM, uno::UNO_QUERY_THROW ); + xMerge->removeMergedMenuBar(); + + xContainerLM->setDockingAreaAcceptor( xDocAreaAcc ); + uno::Reference< lang::XServiceInfo> xServiceInfo(m_xComponent, uno::UNO_QUERY); + if (!xServiceInfo.is() || !xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + { + xContainerLM->setVisible(true); + xContainerLM->unlock(); + } + + xContainerLM->doLayout(); + bResult = true; + } + catch( const uno::Exception& ) + { + SetFrameLMVisibility( m_xFrame, true ); + } + } + } + + return bResult; +} + + +uno::Reference< frame::XFrame > const & DocumentHolder::GetDocFrame() +{ + // the frame for outplace activation + if ( !m_xFrame.is() ) + { + uno::Reference< lang::XSingleServiceFactory > xFrameFact = frame::TaskCreator::create(m_xContext); + + m_xFrame.set(xFrameFact->createInstanceWithArguments( m_aOutplaceFrameProps ), uno::UNO_QUERY_THROW); + + uno::Reference< frame::XDispatchProviderInterception > xInterception( m_xFrame, uno::UNO_QUERY ); + if ( xInterception.is() ) + { + if ( m_xInterceptor.is() ) + { + m_xInterceptor->DisconnectDocHolder(); + m_xInterceptor.clear(); + } + + m_xInterceptor = new Interceptor( this ); + + xInterception->registerDispatchProviderInterceptor( m_xInterceptor ); + + // register interceptor from outside + if ( m_xOutplaceInterceptor.is() ) + xInterception->registerDispatchProviderInterceptor( m_xOutplaceInterceptor ); + } + + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xFrame, uno::UNO_QUERY ); + if ( xCloseBroadcaster.is() ) + xCloseBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + } + + if ( m_xComponent.is() ) + { + uno::Reference< css::frame::XLayoutManager > xOwnLM; + try { + uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY_THROW ); + xPropSet->getPropertyValue("LayoutManager") >>= xOwnLM; + } catch( const uno::Exception& ) + {} + + if ( xOwnLM.is() ) + xOwnLM->lock(); + + // TODO/LATER: get it for the real aspect + awt::Size aSize; + LoadDocToFrame(false); + + if ( xOwnLM.is() ) + { + xOwnLM->unlock(); + xOwnLM->lock(); + } + + GetExtent(embed::Aspects::MSOLE_CONTENT, &aSize); + SetExtent(embed::Aspects::MSOLE_CONTENT, aSize); + + if ( xOwnLM.is() ) + xOwnLM->unlock(); + } + + try + { + uno::Reference< awt::XWindow > xHWindow = m_xFrame->getContainerWindow(); + + if( xHWindow.is() ) + { + sal_Int32 nDisplay = Application::GetDisplayBuiltInScreen(); + + tools::Rectangle aWorkRect = Application::GetScreenPosSizePixel( nDisplay ); + awt::Rectangle aWindowRect = xHWindow->getPosSize(); + + if (( aWindowRect.Width < aWorkRect.GetWidth()) && ( aWindowRect.Height < aWorkRect.GetHeight() )) + { + int OffsetX = ( aWorkRect.GetWidth() - aWindowRect.Width ) / 2 + aWorkRect.Left(); + int OffsetY = ( aWorkRect.GetHeight() - aWindowRect.Height ) /2 + aWorkRect.Top(); + xHWindow->setPosSize( OffsetX, OffsetY, aWindowRect.Width, aWindowRect.Height, awt::PosSize::POS ); + } + else + { + xHWindow->setPosSize( aWorkRect.Left(), aWorkRect.Top(), aWorkRect.GetWidth(), aWorkRect.GetHeight(), awt::PosSize::POSSIZE ); + } + + xHWindow->setVisible( true ); + } + } + catch ( const uno::Exception& ) + { + } + + return m_xFrame; +} + + +void DocumentHolder::SetComponent( const uno::Reference< util::XCloseable >& xDoc, bool bReadOnly ) +{ + if ( m_xComponent.is() ) + { + // May be should be improved + try { + CloseDocument( true, false ); + } catch( const uno::Exception& ) + {} + } + + m_xComponent = xDoc; + + m_bReadOnly = bReadOnly; + m_bAllowClosing = false; + + if ( m_xComponent.is() ) + m_xComponent->addCloseListener( static_cast<util::XCloseListener*>(this) ); + + uno::Reference< document::XEventBroadcaster > xEventBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xEventBroadcaster.is() ) + xEventBroadcaster->addEventListener( static_cast<document::XEventListener*>(this) ); + else + { + // the object does not support document::XEventBroadcaster interface + // use the workaround, register for modified events + uno::Reference< util::XModifyBroadcaster > xModifyBroadcaster( m_xComponent, uno::UNO_QUERY ); + if ( xModifyBroadcaster.is() ) + xModifyBroadcaster->addModifyListener( static_cast<util::XModifyListener*>(this) ); + } + + if ( m_xFrame.is() ) + LoadDocToFrame(false); +} + + +bool DocumentHolder::LoadDocToFrame( bool bInPlace ) +{ + if ( !m_xFrame || !m_xComponent ) + return true; + + uno::Reference < frame::XModel > xDoc( m_xComponent, uno::UNO_QUERY ); + if ( xDoc.is() ) + { + // load new document into the frame + uno::Reference< frame::XComponentLoader > xComponentLoader( m_xFrame, uno::UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aArgs; + aArgs.put( "Model", m_xComponent ); + aArgs.put( "ReadOnly", m_bReadOnly ); + + // set document title to show in the title bar + css::uno::Reference< css::frame::XTitle > xModelTitle( xDoc, css::uno::UNO_QUERY ); + if( xModelTitle.is() && m_pEmbedObj && !m_pEmbedObj->getContainerName().isEmpty() ) + { + std::locale aResLoc = Translate::Create("sfx"); + OUString sEmbedded = Translate::get(STR_EMBEDDED_TITLE, aResLoc); + xModelTitle->setTitle( m_pEmbedObj->getContainerName() + sEmbedded ); + m_aContainerName = m_pEmbedObj->getContainerName(); + // TODO: get real m_aDocumentNamePart + m_aDocumentNamePart = sEmbedded; + } + + if ( bInPlace ) + aArgs.put( "PluginMode", sal_Int16(1) ); + OUString sUrl; + uno::Reference< lang::XServiceInfo> xServiceInfo(xDoc,uno::UNO_QUERY); + if ( xServiceInfo.is() + && xServiceInfo->supportsService("com.sun.star.report.ReportDefinition") ) + { + sUrl = ".component:DB/ReportDesign"; + } + else if( xServiceInfo.is() + && xServiceInfo->supportsService("com.sun.star.chart2.ChartDocument")) + sUrl = "private:factory/schart"; + else + sUrl = "private:object"; + + xComponentLoader->loadComponentFromURL( sUrl, + "_self", + 0, + aArgs.getPropertyValues() ); + + return true; + } + else + { + uno::Reference < frame::XSynchronousFrameLoader > xLoader( m_xComponent, uno::UNO_QUERY ); + if ( xLoader.is() ) + return xLoader->load( uno::Sequence < beans::PropertyValue >(), m_xFrame ); + else + return false; + } + + return true; +} + + +void DocumentHolder::Show() +{ + if( m_xFrame.is() ) + { + m_xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( m_xFrame->getContainerWindow(), uno::UNO_QUERY ); + if( xTopWindow.is() ) + xTopWindow->toFront(); + } + else + GetDocFrame(); +} + + +bool DocumentHolder::SetExtent( sal_Int64 nAspect, const awt::Size& aSize ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( xDocVis.is() ) + { + try + { + xDocVis->setVisualAreaSize( nAspect, aSize ); + return true; + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return false; +} + + +bool DocumentHolder::GetExtent( sal_Int64 nAspect, awt::Size *pSize ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( pSize && xDocVis.is() ) + { + try + { + *pSize = xDocVis->getVisualAreaSize( nAspect ); + return true; + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return false; +} + + +sal_Int32 DocumentHolder::GetMapUnit( sal_Int64 nAspect ) +{ + uno::Reference< embed::XVisualObject > xDocVis( m_xComponent, uno::UNO_QUERY ); + if ( xDocVis.is() ) + { + try + { + return xDocVis->getMapUnit( nAspect ); + } + catch( const uno::Exception& ) + { + // TODO: Error handling + } + } + + return 0; +} + + +awt::Rectangle DocumentHolder::CalculateBorderedArea( const awt::Rectangle& aRect ) +{ + return awt::Rectangle( aRect.X + m_aBorderWidths.Left + HATCH_BORDER_WIDTH, + aRect.Y + m_aBorderWidths.Top + HATCH_BORDER_WIDTH, + aRect.Width - m_aBorderWidths.Left - m_aBorderWidths.Right - 2*HATCH_BORDER_WIDTH, + aRect.Height - m_aBorderWidths.Top - m_aBorderWidths.Bottom - 2*HATCH_BORDER_WIDTH ); +} + + +awt::Rectangle DocumentHolder::AddBorderToArea( const awt::Rectangle& aRect ) +{ + return awt::Rectangle( aRect.X - m_aBorderWidths.Left - HATCH_BORDER_WIDTH, + aRect.Y - m_aBorderWidths.Top - HATCH_BORDER_WIDTH, + aRect.Width + m_aBorderWidths.Left + m_aBorderWidths.Right + 2*HATCH_BORDER_WIDTH, + aRect.Height + m_aBorderWidths.Top + m_aBorderWidths.Bottom + 2*HATCH_BORDER_WIDTH ); +} + + +void SAL_CALL DocumentHolder::disposing( const css::lang::EventObject& aSource ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source ) + { + m_xComponent = nullptr; + if ( m_bWaitForClose ) + { + m_bWaitForClose = false; + FreeOffice(); + } + } + + if( m_xFrame.is() && m_xFrame == aSource.Source ) + { + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); + } +} + + +void SAL_CALL DocumentHolder::queryClosing( const lang::EventObject& aSource, sal_Bool /*bGetsOwnership*/ ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source && !m_bAllowClosing ) + throw util::CloseVetoException("To close an embedded document, close the document holder (document definition), not the document itself.", static_cast< ::cppu::OWeakObject*>(this)); +} + + +void SAL_CALL DocumentHolder::notifyClosing( const lang::EventObject& aSource ) +{ + if ( m_xComponent.is() && m_xComponent == aSource.Source ) + { + m_xComponent = nullptr; + if ( m_bWaitForClose ) + { + m_bWaitForClose = false; + FreeOffice(); + } + } + + if( m_xFrame.is() && m_xFrame == aSource.Source ) + { + m_xHatchWindow.clear(); + m_xOwnWindow.clear(); + m_xFrame.clear(); + } +} + + +void SAL_CALL DocumentHolder::queryTermination( const lang::EventObject& ) +{ + if ( m_bWaitForClose ) + throw frame::TerminationVetoException(); +} + + +void SAL_CALL DocumentHolder::notifyTermination( const lang::EventObject& aSource ) +{ + OSL_ENSURE( !m_xComponent.is(), "Just a disaster..." ); + + uno::Reference< frame::XDesktop > xDesktop( aSource.Source, uno::UNO_QUERY ); + m_bDesktopTerminated = true; + if ( xDesktop.is() ) + xDesktop->removeTerminateListener( static_cast<frame::XTerminateListener*>(this) ); +} + + +void SAL_CALL DocumentHolder::modified( const lang::EventObject& aEvent ) +{ + // if the component does not support document::XEventBroadcaster + // the modify notifications are used as workaround, but only for running state + if( aEvent.Source == m_xComponent && m_pEmbedObj && m_pEmbedObj->getCurrentState() == embed::EmbedStates::RUNNING ) + m_pEmbedObj->PostEvent_Impl( "OnVisAreaChanged" ); +} + + +void SAL_CALL DocumentHolder::notifyEvent( const document::EventObject& Event ) +{ + if( m_pEmbedObj && Event.Source == m_xComponent ) + { + // for now the ignored events are not forwarded, but sent by the object itself + if ( !Event.EventName.startsWith( "OnSave" ) + && !Event.EventName.startsWith( "OnSaveDone" ) + && !Event.EventName.startsWith( "OnSaveAs" ) + && !Event.EventName.startsWith( "OnSaveAsDone" ) + && !( Event.EventName.startsWith( "OnVisAreaChanged" ) && m_nNoResizeReact ) ) + m_pEmbedObj->PostEvent_Impl( Event.EventName ); + } +} + + +void SAL_CALL DocumentHolder::borderWidthsChanged( const uno::Reference< uno::XInterface >& aObject, + const frame::BorderWidths& aNewSize ) +{ + // TODO: may require mutex introduction ??? + if ( m_pEmbedObj && m_xFrame.is() && aObject == m_xFrame->getController() ) + { + if ( m_aBorderWidths.Left != aNewSize.Left + || m_aBorderWidths.Right != aNewSize.Right + || m_aBorderWidths.Top != aNewSize.Top + || m_aBorderWidths.Bottom != aNewSize.Bottom ) + { + m_aBorderWidths = aNewSize; + if ( !m_nNoBorderResizeReact ) + PlaceFrame( m_aObjRect ); + } + } +} + + +void SAL_CALL DocumentHolder::requestPositioning( const awt::Rectangle& aRect ) +{ + // TODO: may require mutex introduction ??? + if ( m_pEmbedObj ) + { + // borders should not be counted + awt::Rectangle aObjRect = CalculateBorderedArea( aRect ); + IntCounterGuard aGuard( m_nNoResizeReact ); + m_pEmbedObj->requestPositioning( aObjRect ); + } +} + + +awt::Rectangle SAL_CALL DocumentHolder::calcAdjustedRectangle( const awt::Rectangle& aRect ) +{ + // Solar mutex should be locked already since this is a call from HatchWindow with focus + awt::Rectangle aResult( aRect ); + + if ( m_xFrame.is() ) + { + // borders should not be counted + uno::Reference< frame::XControllerBorder > xControllerBorder( m_xFrame->getController(), uno::UNO_QUERY ); + if ( xControllerBorder.is() ) + { + awt::Rectangle aObjRect = CalculateBorderedArea( aRect ); + aObjRect = xControllerBorder->queryBorderedArea( aObjRect ); + aResult = AddBorderToArea( aObjRect ); + } + } + + awt::Rectangle aMinRectangle = AddBorderToArea( awt::Rectangle() ); + if ( aResult.Width < aMinRectangle.Width + 2 ) + aResult.Width = aMinRectangle.Width + 2; + if ( aResult.Height < aMinRectangle.Height + 2 ) + aResult.Height = aMinRectangle.Height + 2; + + return aResult; +} + +void SAL_CALL DocumentHolder::activated( ) +{ + if ( !(m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE) ) + return; + + if ( m_pEmbedObj->getCurrentState() != embed::EmbedStates::UI_ACTIVE && + !(m_pEmbedObj->getStatus(embed::Aspects::MSOLE_CONTENT)&embed::EmbedMisc::MS_EMBED_NOUIACTIVATE) ) + { + try + { + m_pEmbedObj->changeState( embed::EmbedStates::UI_ACTIVE ); + } + catch ( const css::embed::StateChangeInProgressException& ) + { + // must catch this exception because focus is grabbed while UI activation in doVerb() + } + catch ( const css::uno::Exception& ) + { + // no outgoing exceptions specified here + } + } + else + { + uno::Reference< frame::XFramesSupplier > xSupp = m_xFrame->getCreator(); + if ( xSupp.is() ) + xSupp->setActiveFrame( m_xFrame ); + } +} + +void DocumentHolder::ResizeHatchWindow() +{ + awt::Rectangle aHatchRect = AddBorderToArea( m_aObjRect ); + ResizeWindows_Impl( aHatchRect ); + uno::Reference< embed::XHatchWindow > xHatchWindow( m_xHatchWindow, uno::UNO_QUERY ); + xHatchWindow->setHatchBorderSize( awt::Size( HATCH_BORDER_WIDTH, HATCH_BORDER_WIDTH ) ); +} + +void SAL_CALL DocumentHolder::deactivated( ) +{ + // deactivation is too unspecific to be useful; usually we only trigger code from activation + // so UIDeactivation is actively triggered by the container +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/dummyobject.cxx b/embeddedobj/source/general/dummyobject.cxx new file mode 100644 index 000000000..4d2af144a --- /dev/null +++ b/embeddedobj/source/general/dummyobject.cxx @@ -0,0 +1,638 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XEmbeddedClient.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> + +#include <comphelper/multicontainer2.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include <dummyobject.hxx> + + +using namespace ::com::sun::star; + + +void ODummyEmbeddedObject::CheckInit_WrongState() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + +void ODummyEmbeddedObject::CheckInit_Runtime() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState == -1 ) + throw uno::RuntimeException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +} +void ODummyEmbeddedObject::PostEvent_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pIC = m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if( !pIC ) + return; + + document::EventObject aEvent; + aEvent.EventName = aEventName; + aEvent.Source.set( static_cast< ::cppu::OWeakObject* >( this ) ); + // For now all the events are sent as object events + // aEvent.Source = ( xSource.is() ? xSource + // : uno::Reference< uno::XInterface >( static_cast< ::cppu::OWeakObject* >( this ) ) ); + comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + try + { + static_cast<document::XEventListener *>(aIt.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + aIt.remove(); + } + + // the listener could dispose the object. + if ( m_bDisposed ) + return; + } +} + + +ODummyEmbeddedObject::~ODummyEmbeddedObject() +{ +} + + +void SAL_CALL ODummyEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( nNewState == embed::EmbedStates::LOADED ) + return; + + throw embed::UnreachableStateException(); +} + + +uno::Sequence< sal_Int32 > SAL_CALL ODummyEmbeddedObject::getReachableStates() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return { embed::EmbedStates::LOADED }; +} + + +sal_Int32 SAL_CALL ODummyEmbeddedObject::getCurrentState() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return m_nObjectState; +} + + +void SAL_CALL ODummyEmbeddedObject::doVerb( sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // no supported verbs +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL ODummyEmbeddedObject::getSupportedVerbs() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return uno::Sequence< embed::VerbDescriptor >(); +} + + +void SAL_CALL ODummyEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + m_xClientSite = xClient; +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL ODummyEmbeddedObject::getClientSite() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return m_xClientSite; +} + + +void SAL_CALL ODummyEmbeddedObject::update() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); +} + + +void SAL_CALL ODummyEmbeddedObject::setUpdateMode( sal_Int32 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); +} + + +sal_Int64 SAL_CALL ODummyEmbeddedObject::getStatus( sal_Int64 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + return 0; +} + + +void SAL_CALL ODummyEmbeddedObject::setContainerName( const OUString& ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); +} + + +void SAL_CALL ODummyEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_nCachedAspect = nAspect; + m_aCachedSize = aSize; + m_bHasCachedSize = true; +} + + +awt::Size SAL_CALL ODummyEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( !m_bHasCachedSize || m_nCachedAspect != nAspect ) + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aCachedSize; +} + + +sal_Int32 SAL_CALL ODummyEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + OSL_ENSURE( nAspect != embed::Aspects::MSOLE_ICON, "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + + +embed::VisualRepresentation SAL_CALL ODummyEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); +} + + +void SAL_CALL ODummyEmbeddedObject::setPersistentEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + throw embed::WrongStateException( + "Can't change persistent representation of activated object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + { + if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + saveCompleted( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + + } + + if ( nEntryConnectionMode != embed::EntryInitModes::DEFAULT_INIT + && nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( !xStorage->hasByName( sEntName ) ) + throw lang::IllegalArgumentException( "Wrong entry is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + m_xParentStorage = xStorage; + m_aEntryName = sEntName; + m_nObjectState = embed::EmbedStates::LOADED; +} + + +void SAL_CALL ODummyEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); +} + + +void SAL_CALL ODummyEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + PostEvent_Impl( "OnSaveAs" ); + + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + + m_bWaitSaveCompleted = true; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; +} + + +void SAL_CALL ODummyEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + OSL_ENSURE( m_bWaitSaveCompleted, "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + m_xParentStorage = m_xNewParentStorage; + m_aEntryName = m_aNewEntryName; + + PostEvent_Impl( "OnSaveAsDone" ); + } + + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_bWaitSaveCompleted = false; +} + + +sal_Bool SAL_CALL ODummyEmbeddedObject::hasEntry() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( !m_aEntryName.isEmpty() ) + return true; + + return false; +} + + +OUString SAL_CALL ODummyEmbeddedObject::getEntryName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL ODummyEmbeddedObject::storeOwn() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // the object can not be activated or changed +} + + +sal_Bool SAL_CALL ODummyEmbeddedObject::isReadonly() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // this object can not be changed + return true; +} + + +void SAL_CALL ODummyEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& /* lArguments */, + const uno::Sequence< beans::PropertyValue >& /* lObjArgs */ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_WrongState(); + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // nothing to reload +} + + +uno::Sequence< sal_Int8 > SAL_CALL ODummyEmbeddedObject::getClassID() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + // currently the class ID is empty + // TODO/LATER: should a special class ID be used in this case? + return uno::Sequence< sal_Int8 >(); +} + + +OUString SAL_CALL ODummyEmbeddedObject::getClassName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return OUString(); +} + + +void SAL_CALL ODummyEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& /*aClassID*/, const OUString& /*aClassName*/ ) +{ + throw lang::NoSupportException(); +} + + +uno::Reference< util::XCloseable > SAL_CALL ODummyEmbeddedObject::getComponent() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + CheckInit_Runtime(); + + return uno::Reference< util::XCloseable >(); +} + + +void SAL_CALL ODummyEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ) ); + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource ); + } + catch( const uno::RuntimeException& ) + { + pCloseIterator.remove(); + } + } + } + + m_pInterfaceContainer->disposeAndClear( aSource ); + } + + m_bDisposed = true; // the object is disposed now for outside +} + + +void SAL_CALL ODummyEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + return; + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL ODummyEmbeddedObject::removeEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +OUString SAL_CALL ODummyEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.ODummyEmbeddedObject"; +} + +sal_Bool SAL_CALL ODummyEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL ODummyEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.ODummyEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/intercept.cxx b/embeddedobj/source/general/intercept.cxx new file mode 100644 index 000000000..58a7ed49b --- /dev/null +++ b/embeddedobj/source/general/intercept.cxx @@ -0,0 +1,312 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EmbedStates.hpp> +#include <comphelper/multiinterfacecontainer3.hxx> + +#include <intercept.hxx> +#include <docholder.hxx> +#include <commonembobj.hxx> + +using namespace ::com::sun::star; + +constexpr OUStringLiteral IU0 = u".uno:Save"; +constexpr OUStringLiteral IU1 = u".uno:SaveAll"; +constexpr OUStringLiteral IU2 = u".uno:CloseDoc"; +constexpr OUStringLiteral IU3 = u".uno:CloseWin"; +constexpr OUStringLiteral IU4 = u".uno:CloseFrame"; +constexpr OUStringLiteral IU5 = u".uno:SaveAs"; +const uno::Sequence< OUString > Interceptor::m_aInterceptedURL{ IU0, IU1, IU2, IU3, IU4, IU5 }; + +class StatusChangeListenerContainer + : public comphelper::OMultiTypeInterfaceContainerHelperVar3<frame::XStatusListener, OUString> +{ +public: + explicit StatusChangeListenerContainer(osl::Mutex& aMutex) + : comphelper::OMultiTypeInterfaceContainerHelperVar3<frame::XStatusListener, OUString>(aMutex) + { + } +}; + +void Interceptor::DisconnectDocHolder() +{ + osl::MutexGuard aGuard( m_aMutex ); + m_pDocHolder = nullptr; +} + +Interceptor::Interceptor( DocumentHolder* pDocHolder ) + : m_pDocHolder( pDocHolder ) +{ +} + +Interceptor::~Interceptor() +{ +} + +//XDispatch +void SAL_CALL +Interceptor::dispatch( + const util::URL& URL, + const uno::Sequence< + beans::PropertyValue >& Arguments ) +{ + osl::MutexGuard aGuard(m_aMutex); + if( !m_pDocHolder ) + return; + + if(URL.Complete == m_aInterceptedURL[0]) + m_pDocHolder->GetEmbedObject()->SaveObject_Impl(); + else if(URL.Complete == m_aInterceptedURL[2] || + URL.Complete == m_aInterceptedURL[3] || + URL.Complete == m_aInterceptedURL[4]) + { + try { + m_pDocHolder->GetEmbedObject()->changeState( embed::EmbedStates::RUNNING ); + } + catch( const uno::Exception& ) + { + } + } + else if ( URL.Complete == m_aInterceptedURL[5] ) + { + uno::Sequence< beans::PropertyValue > aNewArgs = Arguments; + sal_Int32 nInd = 0; + + while( nInd < aNewArgs.getLength() ) + { + if ( aNewArgs[nInd].Name == "SaveTo" ) + { + aNewArgs.getArray()[nInd].Value <<= true; + break; + } + nInd++; + } + + if ( nInd == aNewArgs.getLength() ) + { + aNewArgs.realloc( nInd + 1 ); + auto pNewArgs = aNewArgs.getArray(); + pNewArgs[nInd].Name = "SaveTo"; + pNewArgs[nInd].Value <<= true; + } + + uno::Reference< frame::XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch( + URL, "_self", 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( URL, aNewArgs ); + } +} + +void SAL_CALL +Interceptor::addStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!Control.is()) + return; + + if(URL.Complete == m_aInterceptedURL[0]) + { // Save + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[0]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($1) " + m_pDocHolder->GetTitle(); + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + sal_Int32 i = 2; + if(URL.Complete == m_aInterceptedURL[i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[++i] ) + { // Close and return + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($2)" + m_pDocHolder->GetContainerName(); + Control->statusChanged(aStateEvent); + + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + if(URL.Complete != m_aInterceptedURL[5]) + return; + +// SaveAs + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[5]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= OUString("($3)"); + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset(new StatusChangeListenerContainer(m_aMutex)); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + +} + + +void SAL_CALL +Interceptor::removeStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!(Control.is() && m_pStatCL)) + return; + else { + m_pStatCL->removeInterface(URL.Complete,Control); + return; + } +} + + +//XInterceptorInfo +uno::Sequence< OUString > +SAL_CALL +Interceptor::getInterceptedURLs( ) +{ + // now implemented as update + + return m_aInterceptedURL; +} + + +// XDispatchProvider + +uno::Reference< frame::XDispatch > SAL_CALL +Interceptor::queryDispatch( + const util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) +{ + osl::MutexGuard aGuard(m_aMutex); + if(URL.Complete == m_aInterceptedURL[0]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[1]) + return nullptr ; + else if(URL.Complete == m_aInterceptedURL[2]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[3]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[4]) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[5]) + return static_cast<frame::XDispatch*>(this); + else { + if(m_xSlaveDispatchProvider.is()) + return m_xSlaveDispatchProvider->queryDispatch( + URL,TargetFrameName,SearchFlags); + else + return uno::Reference<frame::XDispatch>(nullptr); + } +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL +Interceptor::queryDispatches( + const uno::Sequence<frame::DispatchDescriptor >& Requests ) +{ + osl::MutexGuard aGuard(m_aMutex); + typedef uno::Sequence<uno::Reference<frame::XDispatch>> DispatchSeq; + DispatchSeq aRet = m_xSlaveDispatchProvider.is() + ? m_xSlaveDispatchProvider->queryDispatches(Requests) + : DispatchSeq(Requests.getLength()); + + auto aRetRange = asNonConstRange(aRet); + for(sal_Int32 i = 0; i < Requests.getLength(); ++i) + if(m_aInterceptedURL[0] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[1] == Requests[i].FeatureURL.Complete) + aRetRange[i] = nullptr; + else if(m_aInterceptedURL[2] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[3] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[4] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[5] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + + return aRet; +} + + +//XDispatchProviderInterceptor + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getSlaveDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xSlaveDispatchProvider; +} + +void SAL_CALL +Interceptor::setSlaveDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewDispatchProvider ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xSlaveDispatchProvider = NewDispatchProvider; +} + + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getMasterDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xMasterDispatchProvider; +} + + +void SAL_CALL +Interceptor::setMasterDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewSupplier ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xMasterDispatchProvider = NewSupplier; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/general/xcreator.cxx b/embeddedobj/source/general/xcreator.cxx new file mode 100644 index 000000000..c4a8635e4 --- /dev/null +++ b/embeddedobj/source/general/xcreator.cxx @@ -0,0 +1,413 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/XEmbedObjectFactory.hpp> +#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp> +#include <com/sun/star/embed/OLEEmbeddedObjectFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/documentconstants.hxx> +#include <officecfg/Office/Common.hxx> + +#include <xcreator.hxx> +#include <dummyobject.hxx> + + +using namespace ::com::sun::star; + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceInitNew( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + OUString aEmbedFactory = m_aConfigHelper.GetFactoryNameByClassID( aClassID ); + if ( aEmbedFactory.isEmpty() ) + { + // use system fallback + // TODO: in future users factories can be tested + aEmbedFactory = "com.sun.star.embed.OLEEmbeddedObjectFactory"; + } + + uno::Reference < uno::XInterface > xFact( m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext) ); + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY ); + if ( xEmbCreator.is() ) + return xEmbCreator->createInstanceInitNew( aClassID, aClassName, xStorage, sEntName, lObjArgs ); + + uno::Reference < embed::XEmbedObjectFactory > xEmbFact( xFact, uno::UNO_QUERY_THROW ); + return xEmbFact->createInstanceUserInit( aClassID, aClassName, xStorage, sEntName, embed::EntryInitModes::TRUNCATE_INIT, uno::Sequence < beans::PropertyValue >(), lObjArgs); +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMedDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + if ( !xNameAccess->hasByName( sEntName ) ) + throw container::NoSuchElementException(); + + OUString aMediaType; + OUString aEmbedFactory; + if ( xStorage->isStorageElement( sEntName ) ) + { + // the object must be based on storage + uno::Reference< embed::XStorage > xSubStorage = + xStorage->openStorageElement( sEntName, embed::ElementModes::READ ); + + uno::Reference< beans::XPropertySet > xPropSet( xSubStorage, uno::UNO_QUERY_THROW ); + + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + } + catch ( const uno::Exception& ) + { + } + + try { + if ( xSubStorage.is() ) + xSubStorage->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + else + { + // the object must be based on stream + // it means for now that this is an OLE object + + // the object will be created as embedded object + // after it is loaded it can detect that it is a link + + uno::Reference< io::XStream > xSubStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READ ); + + uno::Reference< beans::XPropertySet > xPropSet( xSubStream, uno::UNO_QUERY_THROW ); + + try { + uno::Any aAny = xPropSet->getPropertyValue("MediaType"); + aAny >>= aMediaType; + if ( aMediaType == "application/vnd.sun.star.oleobject" ) + aEmbedFactory = "com.sun.star.embed.OLEEmbeddedObjectFactory"; + } + catch ( const uno::Exception& ) + { + } + + try { + uno::Reference< lang::XComponent > xComp( xSubStream, uno::UNO_QUERY ); + if ( xComp.is() ) + xComp->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + OSL_ENSURE( !aMediaType.isEmpty(), "No media type is specified for the object!" ); + if ( !aMediaType.isEmpty() && aEmbedFactory.isEmpty() ) + { + aEmbedFactory = m_aConfigHelper.GetFactoryNameByMediaType( aMediaType ); + + // If no factory is found, fall back to the FileFormatVersion=6200 filter, Base only has that. + if (aEmbedFactory.isEmpty() && aMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) + aEmbedFactory = m_aConfigHelper.GetFactoryNameByMediaType(MIMETYPE_VND_SUN_XML_BASE_ASCII); + } + + if ( !aEmbedFactory.isEmpty() ) + { + uno::Reference< uno::XInterface > xFact = m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext); + + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY ); + if ( xEmbCreator.is() ) + return xEmbCreator->createInstanceInitFromEntry( xStorage, sEntName, aMedDescr, lObjArgs ); + + uno::Reference < embed::XEmbedObjectFactory > xEmbFact( xFact, uno::UNO_QUERY ); + if ( xEmbFact.is() ) + return xEmbFact->createInstanceUserInit( uno::Sequence< sal_Int8 >(), OUString(), xStorage, sEntName, embed::EntryInitModes::DEFAULT_INIT, aMedDescr, lObjArgs); + } + + // the default object should be created, it will allow to store the contents on the next saving + uno::Reference< uno::XInterface > xResult( static_cast< cppu::OWeakObject* >( new ODummyEmbeddedObject() ) ); + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, sEntName, embed::EntryInitModes::DEFAULT_INIT, aMedDescr, lObjArgs ); + return xResult; +} + +/** + * Decides if rFilter should be used to load data into a doc model or real OLE embedding should + * happen. Empty return value means the later. + */ +static OUString HandleFilter(const OUString& rFilter) +{ + OUString aRet = rFilter; + + if (!officecfg::Office::Common::Filter::Microsoft::Import::WinWordToWriter::get()) + { + if (rFilter == "MS Word 97" || rFilter == "MS Word 2007 XML") + { + aRet.clear(); + } + } + + if (!officecfg::Office::Common::Filter::Microsoft::Import::ExcelToCalc::get()) + { + if (rFilter == "MS Excel 97" || rFilter == "Calc MS Excel 2007 XML") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Microsoft::Import::PowerPointToImpress::get()) + { + if (rFilter == "MS PowerPoint 97" || rFilter == "Impress MS PowerPoint 2007 XML") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Microsoft::Import::VisioToDraw::get()) + { + if (rFilter == "Visio Document") + { + aRet.clear(); + } + } + if (!officecfg::Office::Common::Filter::Adobe::Import::PDFToDraw::get()) + { + if (rFilter == "draw_pdf_import") + { + aRet.clear(); + } + } + + return aRet; +} + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // TODO: use lObjArgs + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult; + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is FilterName + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + aFilterName = HandleFilter(aFilterName); + + if ( !aFilterName.isEmpty() ) + { + // the object can be loaded by one of the office application + uno::Reference< embed::XEmbeddedObjectCreator > xOOoEmbCreator = + embed::OOoEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOOoEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, + sEntName, + aTempMedDescr, + lObjArgs ); + } + else + { + // must be an OLE object + + // TODO: in future, when more object types are possible this place seems + // to be a weak one, probably configuration must provide a type detection service + // for every factory, so any file could go through services until it is recognized + // or there is no more services + // Or for example the typename can be used to detect object type if typedetection + // was also extended. + + uno::Reference< embed::XEmbeddedObjectCreator > xOleEmbCreator = + embed::OLEEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOleEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, sEntName, aTempMedDescr, lObjArgs ); + } + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& sClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& aArgs, + const uno::Sequence< beans::PropertyValue >& aObjectArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + OUString aEmbedFactory = m_aConfigHelper.GetFactoryNameByClassID( aClassID ); + uno::Reference< embed::XEmbedObjectFactory > xEmbFactory( + m_xContext->getServiceManager()->createInstanceWithContext(aEmbedFactory, m_xContext), + uno::UNO_QUERY_THROW ); + + return xEmbFactory->createInstanceUserInit( aClassID, + sClassName, + xStorage, + sEntName, + nEntryConnectionMode, + aArgs, + aObjectArgs ); +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOEmbeddedObjectCreator::createInstanceLink( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + uno::Reference< uno::XInterface > xResult; + + uno::Sequence< beans::PropertyValue > aTempMedDescr( aMediaDescr ); + + // check if there is URL, URL must exist + OUString aURL; + for ( beans::PropertyValue const & prop : std::as_const(aTempMedDescr) ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( "No URL for the link is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + OUString aFilterName = m_aConfigHelper.UpdateMediaDescriptorWithFilterName( aTempMedDescr, false ); + + if ( !aFilterName.isEmpty() ) + { + // the object can be loaded by one of the office application + uno::Reference< embed::XEmbeddedObjectCreator > xOOoLinkCreator = + embed::OOoEmbeddedObjectFactory::create( m_xContext ); + + xResult = xOOoLinkCreator->createInstanceLink( xStorage, + sEntName, + aTempMedDescr, + lObjArgs ); + } + else + { + // must be an OLE link + + // TODO: in future, when more object types are possible this place seems + // to be a weak one, probably configuration must provide a type detection service + // for every factory, so any file could go through services until it is recognized + // or there is no more services + // Or for example the typename can be used to detect object type if typedetection + // was also extended. + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + uno::Reference< embed::XEmbeddedObjectCreator > xLinkCreator = + embed::OLEEmbeddedObjectFactory::create( m_xContext); + + xResult = xLinkCreator->createInstanceLink( xStorage, sEntName, aTempMedDescr, lObjArgs ); + } + + return xResult; +} + +OUString SAL_CALL UNOEmbeddedObjectCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.EmbeddedObjectCreator"; +} + +sal_Bool SAL_CALL UNOEmbeddedObjectCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL UNOEmbeddedObjectCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.EmbeddedObjectCreator", "com.sun.star.comp.embed.EmbeddedObjectCreator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_UNOEmbeddedObjectCreator_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UNOEmbeddedObjectCreator(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/closepreventer.hxx b/embeddedobj/source/inc/closepreventer.hxx new file mode 100644 index 000000000..fa8e548b8 --- /dev/null +++ b/embeddedobj/source/inc/closepreventer.hxx @@ -0,0 +1,33 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/util/XCloseListener.hpp> +#include <cppuhelper/implbase.hxx> + +class OClosePreventer : public ::cppu::WeakImplHelper < css::util::XCloseListener > +{ + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/commonembobj.hxx b/embeddedobj/source/inc/commonembobj.hxx new file mode 100644 index 000000000..f683b0acc --- /dev/null +++ b/embeddedobj/source/inc/commonembobj.hxx @@ -0,0 +1,442 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist2.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/embed/XInplaceObject.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/chart2/XDefaultSizeTransmitter.hpp> +#include <com/sun/star/io/XTempFile.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/weak.hxx> +#include <embeddedobj/embeddedupdate.hxx> +#include <rtl/ref.hxx> +#include <map> +#include <memory> +#include <svtools/filechangedchecker.hxx> + +namespace com::sun::star { + namespace embed { + class XStorage; + } + namespace lang { + class XMultiServiceFactory; + } + namespace util { + class XCloseListener; + } + namespace beans { + struct PropertyValue; + struct NamedValue; + } +} + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +namespace comphelper { + class NamedValueCollection; +} + +#define NUM_SUPPORTED_STATES 5 +// #define NUM_SUPPORTED_VERBS 5 + +#include "docholder.hxx" + +class Interceptor; + +/** + * Represents an OLE object that has native data and we loaded that data into a + * document model successfully. + */ +class OCommonEmbeddedObject : public css::embed::XEmbeddedObject + , public css::embed::EmbeddedUpdate + , public css::embed::XEmbedPersist2 + , public css::embed::XLinkageSupport + , public css::embed::XInplaceObject + , public css::container::XChild + , public css::chart2::XDefaultSizeTransmitter + , public css::lang::XServiceInfo + , public css::lang::XInitialization + , public css::lang::XTypeProvider + , public ::cppu::OWeakObject +{ +protected: + ::osl::Mutex m_aMutex; + + rtl::Reference<DocumentHolder> m_xDocHolder; + + std::unique_ptr<::comphelper::OMultiTypeInterfaceContainerHelper2> m_pInterfaceContainer; + + bool m_bReadOnly; + + bool m_bDisposed; + bool m_bClosed; + + sal_Int32 m_nObjectState; + sal_Int32 m_nTargetState; // should be -1 except during state changing + sal_Int32 m_nUpdateMode; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Sequence< css::beans::PropertyValue > m_aDocMediaDescriptor; + + css::uno::Sequence< sal_Int8 > m_aClassID; + OUString m_aClassName; + + OUString m_aDocServiceName; + OUString m_aPresetFilterName; + + sal_Int64 m_nMiscStatus; + + css::uno::Sequence< css::embed::VerbDescriptor > m_aObjectVerbs; + + std::map< sal_Int32, sal_Int32 > m_aVerbTable; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + OUString m_aContainerName; + OUString m_aDefaultParentBaseURL; + OUString m_aModuleName; + bool m_bEmbeddedScriptSupport; + bool m_bDocumentRecoverySupport; + + // following information will be used between SaveAs and SaveCompleted + bool m_bWaitSaveCompleted; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + css::uno::Reference< css::embed::XStorage > m_xNewObjectStorage; + css::uno::Sequence< css::beans::PropertyValue > m_aNewDocMediaDescriptor; + + css::uno::Reference< css::awt::XWindow > m_xClientWindow; // ??? + css::awt::Rectangle m_aOwnRectangle; + css::awt::Rectangle m_aClipRectangle; + + bool m_bIsLinkURL; + bool m_bLinkTempFileChanged; + ::std::unique_ptr< FileChangedChecker > m_pLinkFile; + bool m_bOleUpdate; + bool m_bInHndFunc; + + // embedded object related stuff + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + css::uno::Reference< css::embed::XStorage > m_xObjectStorage; + css::uno::Reference< css::embed::XStorage > m_xRecoveryStorage; + + // link related stuff + OUString m_aLinkURL; + OUString m_aLinkFilterName; + bool m_bLinkHasPassword; + OUString m_aLinkPassword; + + // tdf#141529 hold a cc of a linked OLE + css::uno::Reference < css::io::XTempFile > m_aLinkTempFile; + + css::uno::Reference< css::uno::XInterface > m_xParent; + + bool m_bHasClonedSize; // the object has cached size + css::awt::Size m_aClonedSize; + sal_Int32 m_nClonedMapUnit; + css::awt::Size m_aDefaultSizeForChart_In_100TH_MM;//#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this member + +private: + void CommonInit_Impl( const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + void LinkInit_Impl( const css::uno::Sequence< css::beans::NamedValue >& aObjectProps, + const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, + const css::uno::Sequence< css::beans::PropertyValue >& aObjectDescr ); + + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const css::uno::Reference< css::embed::XStorage >& xNewObjectStorage, + const OUString& aNewName ); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const OUString& aNewName ); + + const OUString& GetDocumentServiceName() const { return m_aDocServiceName; } + const OUString& GetPresetFilterName() const { return m_aPresetFilterName; } + + css::uno::Reference< css::io::XInputStream > + StoreDocumentToTempStream_Impl( sal_Int32 nStorageFormat, + const OUString& aBaseURL, + const OUString& aHierarchName ); + + sal_Int32 ConvertVerbToState_Impl( sal_Int32 nVerb ); + + void Deactivate(); + + // when State = CopyTempToLink -> the user pressed the save button + // when change in embedded part then copy to the linked-file + // CopyLinkToTemp -> the user pressed the refresh button + // when change in linked-file then copy to the embedded part (temp-file) + // CopyLinkToTempInit -> create the temp file + // CopyLinkToTempRefresh -> when save and Link change but not temp then update temp + enum class CopyBackToOLELink {NoCopy, CopyTempToLink, CopyLinkToTemp, CopyLinkToTempInit, CopyLinkToTempRefresh}; + + void handleLinkedOLE( CopyBackToOLELink eState ); + + void StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState,::osl::ResettableMutexGuard& _rGuard ); + + void SwitchStateTo_Impl( sal_Int32 nNextState ); + + css::uno::Sequence< sal_Int32 > const & GetIntermediateStatesSequence_Impl( sal_Int32 nNewState ); + + OUString GetFilterName( sal_Int32 nVersion ) const; + css::uno::Reference< css::util::XCloseable > LoadDocumentFromStorage_Impl(); + + css::uno::Reference< css::util::XCloseable > LoadLink_Impl(); + + css::uno::Reference< css::util::XCloseable > InitNewDocument_Impl(); + + void StoreDocToStorage_Impl( + const css::uno::Reference<css::embed::XStorage>& xStorage, + const css::uno::Sequence<css::beans::PropertyValue>& rMediaArgs, + const css::uno::Sequence<css::beans::PropertyValue>& rObjArgs, + sal_Int32 nStorageVersion, + const OUString& aHierarchName, + bool bAttachToStorage ); + + void SwitchDocToStorage_Impl( + const css::uno::Reference< css::document::XStorageBasedDocument >& xDoc, + const css::uno::Reference< css::embed::XStorage >& xStorage ); + + void FillDefaultLoadArgs_Impl( + const css::uno::Reference< css::embed::XStorage >& i_rxStorage, + ::comphelper::NamedValueCollection& o_rLoadArgs + ) const; + + void EmbedAndReparentDoc_Impl( + const css::uno::Reference< css::util::XCloseable >& i_rxDocument + ) const; + + css::uno::Reference< css::util::XCloseable > CreateDocFromMediaDescr_Impl( + const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr ); + + css::uno::Reference< css::util::XCloseable > CreateTempDocFromLink_Impl(); + + OUString GetBaseURL_Impl() const; + static OUString GetBaseURLFrom_Impl( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ); + +protected: + void SetInplaceActiveState(); + +public: + OCommonEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + // no persistence for linked objects, so the descriptors are provided in constructor + OCommonEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps, + const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, + const css::uno::Sequence< css::beans::PropertyValue >& aObjectDescr ); + + virtual ~OCommonEmbeddedObject() override; + + void SaveObject_Impl(); + + void requestPositioning( const css::awt::Rectangle& aRect ); + + // not a real listener and should not be + void PostEvent_Impl( const OUString& aEventName ); + + OUString const & getContainerName() const { return m_aContainerName; } +// XInterface + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override ; + + virtual void SAL_CALL acquire() + noexcept override; + + virtual void SAL_CALL release() + noexcept override; + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + +// EmbeddedUpdate + + virtual void SetOleState(bool bIsOleUpdate) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeToEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeAsEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + +// XLinkageSupport + + virtual void SAL_CALL breakLink( const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName ) override; + + virtual sal_Bool SAL_CALL isLink() override; + + virtual OUString SAL_CALL getLinkURL() override; + + +// XCommonEmbedPersist + + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + +// XEmbedPersist2 + + virtual sal_Bool SAL_CALL isStored() override; + +// XInplaceObject + + virtual void SAL_CALL setObjectRectangles( const css::awt::Rectangle& aPosRect, + const css::awt::Rectangle& aClipRect ) override; + + virtual void SAL_CALL enableModeless( sal_Bool bEnable ) override; + + virtual void SAL_CALL translateAccelerators( + const css::uno::Sequence< css::awt::KeyEvent >& aKeys ) override; + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + // XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XDefaultSizeTransmitter + //#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this method + virtual void SAL_CALL setDefaultSize( const css::awt::Size& rSize_100TH_MM ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInitialization + void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override; + css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/docholder.hxx b/embeddedobj/source/inc/docholder.hxx new file mode 100644 index 000000000..303fb4c97 --- /dev/null +++ b/embeddedobj/source/inc/docholder.hxx @@ -0,0 +1,200 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XBorderResizeListener.hpp> +#include <com/sun/star/frame/BorderWidths.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/embed/XHatchWindowController.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +class OCommonEmbeddedObject; +class Interceptor; + +class DocumentHolder : + public ::cppu::WeakImplHelper< + css::util::XCloseListener, + css::frame::XTerminateListener, + css::util::XModifyListener, + css::document::XEventListener, + css::frame::XBorderResizeListener, + css::embed::XHatchWindowController > +{ +private: + + OCommonEmbeddedObject* m_pEmbedObj; + + rtl::Reference<Interceptor> m_xInterceptor; + css::uno::Reference< css::frame::XDispatchProviderInterceptor > m_xOutplaceInterceptor; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Reference< css::util::XCloseable > m_xComponent; + + css::uno::Reference< css::frame::XFrame > m_xFrame; + css::uno::Reference< css::awt::XWindow > m_xOwnWindow; // set for inplace objects + css::uno::Reference< css::awt::XWindow > m_xHatchWindow; // set for inplace objects + + css::awt::Rectangle m_aObjRect; + css::frame::BorderWidths m_aBorderWidths; + + OUString m_aContainerName; + OUString m_aDocumentNamePart; + + bool m_bReadOnly; + + bool m_bWaitForClose; + bool m_bAllowClosing; + bool m_bDesktopTerminated; + + sal_Int32 m_nNoBorderResizeReact; + sal_Int32 m_nNoResizeReact; + + css::uno::Sequence< css::uno::Any > m_aOutplaceFrameProps; + + + css::uno::Reference< css::frame::XFrame > const & GetDocFrame(); + bool LoadDocToFrame( bool ); + + css::awt::Rectangle CalculateBorderedArea( const css::awt::Rectangle& aRect ); + css::awt::Rectangle AddBorderToArea( const css::awt::Rectangle& aRect ); + + void ResizeWindows_Impl( const css::awt::Rectangle& aHatchRect ); + + css::uno::Reference< css::container::XIndexAccess > RetrieveOwnMenu_Impl(); + bool MergeMenus_Impl( + const css::uno::Reference< css::frame::XLayoutManager >& xOwnLM, + const css::uno::Reference< css::frame::XLayoutManager >& xContLM, + const css::uno::Reference< css::frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName ); + +public: + /// @throws css::uno::Exception + static void FindConnectPoints( + const css::uno::Reference< css::container::XIndexAccess >& xMenu, + sal_Int32 nConnectPoints[2] ); + + /// @throws css::uno::Exception + static css::uno::Reference< css::container::XIndexAccess > MergeMenusForInplace( + const css::uno::Reference< css::container::XIndexAccess >& xContMenu, + const css::uno::Reference< css::frame::XDispatchProvider >& xContDisp, + const OUString& aContModuleName, + const css::uno::Reference< css::container::XIndexAccess >& xOwnMenu, + const css::uno::Reference< css::frame::XDispatchProvider >& xOwnDisp ); + + + DocumentHolder( const css::uno::Reference< css::uno::XComponentContext >& xContext, + OCommonEmbeddedObject* pEmbObj ); + virtual ~DocumentHolder() override; + + OCommonEmbeddedObject* GetEmbedObject() { return m_pEmbedObj; } + + void SetComponent( const css::uno::Reference< css::util::XCloseable >& xDoc, bool bReadOnly ); + void ResizeHatchWindow(); + void FreeOffice(); + + void CloseDocument( bool bDeliverOwnership, bool bWaitForClose ); + void CloseFrame(); + + OUString GetTitle() const + { + return m_aContainerName + ( m_aDocumentNamePart.isEmpty() ? OUString() : ( " - " + m_aDocumentNamePart ) ); + } + + OUString const & GetContainerName() const + { + return m_aContainerName; + } + + void SetOutplaceFrameProperties( const css::uno::Sequence< css::uno::Any >& aProps ) + { m_aOutplaceFrameProps = aProps; } + + void PlaceFrame( const css::awt::Rectangle& aNewRect ); + + static bool SetFrameLMVisibility( const css::uno::Reference< css::frame::XFrame >& xFrame, + bool bVisible ); + + bool ShowInplace( const css::uno::Reference< css::awt::XWindowPeer >& xParent, + const css::awt::Rectangle& aRectangleToShow, + const css::uno::Reference< css::frame::XDispatchProvider >& xContainerDP ); + + bool ShowUI( + const css::uno::Reference< css::frame::XLayoutManager >& xContainerLM, + const css::uno::Reference< css::frame::XDispatchProvider >& xContainerDP, + const OUString& aContModuleName ); + bool HideUI( + const css::uno::Reference< css::frame::XLayoutManager >& xContainerLM ); + + void Show(); + + bool SetExtent( sal_Int64 nAspect, const css::awt::Size& aSize ); + bool GetExtent( sal_Int64 nAspect, css::awt::Size *pSize ); + + sal_Int32 GetMapUnit( sal_Int64 nAspect ); + + void SetOutplaceDispatchInterceptor( + const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& + xOutplaceInterceptor ) + { + m_xOutplaceInterceptor = xOutplaceInterceptor; + } + + const css::uno::Reference< css::util::XCloseable >& GetComponent() const { return m_xComponent; } + +// XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +// XCloseListener + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + +// XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& Event ) override; + +// XModifyListener + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; + +// XEventListener + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + +// XBorderResizeListener + virtual void SAL_CALL borderWidthsChanged( const css::uno::Reference< css::uno::XInterface >& aObject, const css::frame::BorderWidths& aNewSize ) override; + +// XHatchWindowController + virtual void SAL_CALL requestPositioning( const css::awt::Rectangle& aRect ) override; + virtual css::awt::Rectangle SAL_CALL calcAdjustedRectangle( const css::awt::Rectangle& aRect ) override; + virtual void SAL_CALL activated( ) override; + virtual void SAL_CALL deactivated( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/dummyobject.hxx b/embeddedobj/source/inc/dummyobject.hxx new file mode 100644 index 000000000..64ee7deeb --- /dev/null +++ b/embeddedobj/source/inc/dummyobject.hxx @@ -0,0 +1,205 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/multicontainer2.hxx> + +namespace com::sun::star { + namespace embed { + class XStorage; + } + namespace util { + class XCloseListener; + } + namespace beans { + struct PropertyValue; + struct NamedValue; + } +} + +/** + * Represents an OLE object that has native data (next to the replacement + * image), but we don't understand that data. + */ +class ODummyEmbeddedObject : public ::cppu::WeakImplHelper + < css::embed::XEmbeddedObject + , css::embed::XEmbedPersist + , css::lang::XServiceInfo > +{ + ::osl::Mutex m_aMutex; + std::unique_ptr<comphelper::OMultiTypeInterfaceContainerHelper2> + m_pInterfaceContainer; + bool m_bDisposed; + + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + sal_Int32 m_nObjectState; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + sal_Int64 m_nCachedAspect; + css::awt::Size m_aCachedSize; + bool m_bHasCachedSize; + + // following information will be used between SaveAs and SaveCompleted + bool m_bWaitSaveCompleted; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + +protected: + void CheckInit_WrongState(); //throw WrongStateException on m_nObjectState == -1 + void CheckInit_Runtime(); //throw RuntimeException on m_nObjectState == -1 + void PostEvent_Impl( const OUString& aEventName ); + +public: + + ODummyEmbeddedObject() + : m_bDisposed( false ) + , m_nObjectState( -1 ) + , m_nCachedAspect( 0 ) + , m_bHasCachedSize( false ) + , m_bWaitSaveCompleted( false ) + {} + + virtual ~ODummyEmbeddedObject() override; + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeToEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeAsEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + + +// XCommonEmbedPersist + + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/intercept.hxx b/embeddedobj/source/inc/intercept.hxx new file mode 100644 index 000000000..2dc14738a --- /dev/null +++ b/embeddedobj/source/inc/intercept.hxx @@ -0,0 +1,117 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/mutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <memory> + + +class StatusChangeListenerContainer; +class DocumentHolder; + +class Interceptor : public ::cppu::WeakImplHelper< css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::frame::XDispatch> +{ +public: + + Interceptor( DocumentHolder* pDocHolder ); + virtual ~Interceptor() override; + + void DisconnectDocHolder(); + + //XDispatch + virtual void SAL_CALL + dispatch( + const css::util::URL& URL, + const css::uno::Sequence< + css::beans::PropertyValue >& Arguments ) override; + + virtual void SAL_CALL + addStatusListener( + const css::uno::Reference< + css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + virtual void SAL_CALL + removeStatusListener( + const css::uno::Reference< + css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + //XInterceptorInfo + virtual css::uno::Sequence< OUString > + SAL_CALL getInterceptedURLs( ) override; + + //XDispatchProvider ( inherited by XDispatchProviderInterceptor ) + virtual css::uno::Reference< + css::frame::XDispatch > SAL_CALL + queryDispatch( + const css::util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) override; + + virtual css::uno::Sequence< + css::uno::Reference< + css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence< + css::frame::DispatchDescriptor >& Requests ) override; + + + //XDispatchProviderInterceptor + virtual css::uno::Reference< + css::frame::XDispatchProvider > SAL_CALL + getSlaveDispatchProvider( ) override; + + virtual void SAL_CALL + setSlaveDispatchProvider( + const css::uno::Reference< + css::frame::XDispatchProvider >& NewDispatchProvider ) override; + + virtual css::uno::Reference< + css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider( ) override; + + virtual void SAL_CALL + setMasterDispatchProvider( + const css::uno::Reference< + css::frame::XDispatchProvider >& NewSupplier ) override; + + +private: + + osl::Mutex m_aMutex; + + DocumentHolder* m_pDocHolder; + + css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatchProvider; + css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatchProvider; + + static const css::uno::Sequence< OUString > m_aInterceptedURL; + + std::unique_ptr<StatusChangeListenerContainer> m_pStatCL; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/oleembobj.hxx b/embeddedobj/source/inc/oleembobj.hxx new file mode 100644 index 000000000..e456f2fc3 --- /dev/null +++ b/embeddedobj/source/inc/oleembobj.hxx @@ -0,0 +1,456 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbeddedOleObject.hpp> +#include <com/sun/star/embed/XInplaceObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +#include <osl/thread.h> +#include <memory> + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +class VerbExecutionController +{ + // the following mutex is allowed to be locked only for variables initialization, so no deadlock can be caused + ::osl::Mutex m_aVerbExecutionMutex; + + sal_Int32 m_nNotificationLock; + +#ifdef _WIN32 + bool m_bWasEverActive; + bool m_bVerbExecutionInProgress; + oslThreadIdentifier m_nVerbExecutionThreadIdentifier; + bool m_bChangedOnVerbExecution; +#endif + +public: + + VerbExecutionController() + : m_nNotificationLock( 0 ) +#ifdef _WIN32 + , m_bWasEverActive( false ) + , m_bVerbExecutionInProgress( false ) + , m_nVerbExecutionThreadIdentifier( 0 ) + , m_bChangedOnVerbExecution( false ) +#endif + {} + +#ifdef _WIN32 + void StartControlExecution(); + bool EndControlExecution_WasModified(); + void ModificationNotificationIsDone(); + // no need to lock anything to check the value of the numeric members + bool CanDoNotification() { return ( !m_bVerbExecutionInProgress && !m_bWasEverActive && !m_nNotificationLock ); } + // ... or to change it + void ObjectIsActive() { m_bWasEverActive = true; } +#endif + void LockNotification(); + void UnlockNotification(); +}; + +class VerbExecutionControllerGuard +{ + VerbExecutionController& m_rController; +public: + + VerbExecutionControllerGuard( VerbExecutionController& rController ) + : m_rController( rController ) + { + m_rController.LockNotification(); + } + + ~VerbExecutionControllerGuard() + { + m_rController.UnlockNotification(); + } +}; + + +class OleComponent; +class OwnView_Impl; +/** + * Represents an OLE object that has native data and we try to let an external + * application handle that data. + */ +class OleEmbeddedObject : public ::cppu::WeakImplHelper + < css::embed::XEmbeddedObject + , css::embed::XEmbeddedOleObject + , css::embed::XEmbedPersist + , css::embed::XLinkageSupport + , css::embed::XInplaceObject + , css::container::XChild + , css::io::XActiveDataStreamer + , css::lang::XInitialization + , css::lang::XServiceInfo > +{ + friend class OleComponent; + + ::osl::Mutex m_aMutex; + + rtl::Reference<OleComponent> m_pOleComponent; + + std::unique_ptr<::comphelper::OMultiTypeInterfaceContainerHelper2> m_pInterfaceContainer; + + bool m_bReadOnly; + + bool m_bDisposed; + sal_Int32 m_nObjectState; + sal_Int32 m_nTargetState; + sal_Int32 m_nUpdateMode; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + css::uno::Sequence< sal_Int8 > m_aClassID; + OUString m_aClassName; + + css::uno::Reference< css::embed::XEmbeddedClient > m_xClientSite; + + OUString m_aContainerName; + + css::uno::Reference< css::util::XCloseListener > m_xClosePreventer; + + bool m_bWaitSaveCompleted; + bool m_bNewVisReplInStream; + css::uno::Reference< css::io::XStream > m_xNewCachedVisRepl; + OUString m_aNewEntryName; + css::uno::Reference< css::embed::XStorage > m_xNewParentStorage; + css::uno::Reference< css::io::XStream > m_xNewObjectStream; + bool m_bStoreLoaded; + + css::uno::Reference< css::io::XStream > m_xCachedVisualRepresentation; + bool m_bVisReplInitialized; + bool m_bVisReplInStream; + bool m_bStoreVisRepl; + + bool m_bIsLink; + + // TODO/LATER: may need to cache more than one aspect in future + bool m_bHasCachedSize; // the object has cached size + css::awt::Size m_aCachedSize; + sal_Int64 m_nCachedAspect; + + bool m_bHasSizeToSet; // the object has cached size that should be set to OLE component + css::awt::Size m_aSizeToSet; // this size might be different from the cached one ( scaling is applied ) + sal_Int64 m_nAspectToSet; + + + // cache the status of the object + // TODO/LATER: may need to cache more than one aspect in future + bool m_bGotStatus; + sal_Int64 m_nStatus; + sal_Int64 m_nStatusAspect; + + // embedded object related stuff + OUString m_aEntryName; + css::uno::Reference< css::embed::XStorage > m_xParentStorage; + css::uno::Reference< css::io::XStream > m_xObjectStream; + + // link related stuff + OUString m_aLinkURL; // ??? + + // points to own view provider if the object has no server + rtl::Reference<OwnView_Impl> m_xOwnView; + + // whether the object should be initialized from clipboard in case of default initialization + bool m_bFromClipboard; + + OUString m_aTempURL; + + OUString m_aTempDumpURL; + + // STAMPIT solution + // the following member is used during verb execution to detect whether the verb execution modifies the object + VerbExecutionController m_aVerbExecutionController; + + // if the following member is set, the object works in wrapper mode + css::uno::Reference< css::embed::XEmbeddedObject > m_xWrappedObject; + bool m_bTriedConversion; + OUString m_aFilterName; // if m_bTriedConversion, then the filter detected by that + + css::uno::Reference< css::uno::XInterface > m_xParent; + + /// If it is allowed to modify entries in the stream of the OLE storage. + bool m_bStreamReadOnly = false; + +protected: + /// @throws css::uno::Exception + css::uno::Reference< css::io::XStream > TryToGetAcceptableFormat_Impl( + const css::uno::Reference< css::io::XStream >& xStream ); + + /// @throws css::io::IOException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::io::XStream > GetNewFilledTempStream_Impl( + const css::uno::Reference< css::io::XInputStream >& xInStream ); +#ifdef _WIN32 + void SwitchComponentToRunningState_Impl(); +#endif + void MakeEventListenerNotification_Impl( const OUString& aEventName ); +#ifdef _WIN32 + void StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ); + css::uno::Reference< css::io::XOutputStream > GetStreamForSaving(); + + + css::uno::Sequence< sal_Int32 > GetIntermediateVerbsSequence_Impl( sal_Int32 nNewState ); + + static css::uno::Sequence< sal_Int32 > GetReachableStatesList_Impl( + const css::uno::Sequence< css::embed::VerbDescriptor >& aVerbList ); +#endif + + void Dispose(); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const css::uno::Reference< css::io::XStream >& xNewObjectStream, + const OUString& aNewName ); + + void SwitchOwnPersistence( + const css::uno::Reference< css::embed::XStorage >& xNewParentStorage, + const OUString& aNewName ); + + void GetRidOfComponent(); + + /// @throws css::uno::Exception + void StoreToLocation_Impl( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs, + bool bSaveAs ); +#ifdef _WIN32 + /// @throws css::uno::Exception + void StoreObjectToStream( css::uno::Reference< css::io::XOutputStream > const & xOutStream ); +#endif + /// @throws css::uno::Exception + void InsertVisualCache_Impl( + const css::uno::Reference< css::io::XStream >& xTargetStream, + const css::uno::Reference< css::io::XStream >& xCachedVisualRepresentation ); + + /// @throws css::uno::Exception + void RemoveVisualCache_Impl( const css::uno::Reference< css::io::XStream >& xTargetStream ); + + void SetVisReplInStream( bool bExists ); + bool HasVisReplInStream(); + + /// @throws css::uno::Exception + css::embed::VisualRepresentation GetVisualRepresentationInNativeFormat_Impl( + const css::uno::Reference< css::io::XStream >& xCachedVisRepr ); + + css::uno::Reference< css::io::XStream > TryToRetrieveCachedVisualRepresentation_Impl( + const css::uno::Reference< css::io::XStream >& xStream, + bool bAllowRepair50 = false ) + noexcept; +#ifdef _WIN32 + bool SaveObject_Impl(); + bool OnShowWindow_Impl( bool bShow ); + void CreateOleComponent_Impl( rtl::Reference<OleComponent> const & pOleComponent = {} ); + void CreateOleComponentAndLoad_Impl( rtl::Reference<OleComponent> const & pOleComponent = {} ); + void CreateOleComponentFromClipboard_Impl( OleComponent* pOleComponent = nullptr ); + OUString CreateTempURLEmpty_Impl(); + OUString GetTempURL_Impl(); + void SetObjectIsLink_Impl( bool bIsLink ) { m_bIsLink = bIsLink; } +#endif + + // the following 4 methods are related to switch to wrapping mode + void MoveListeners(); + css::uno::Reference< css::embed::XStorage > CreateTemporarySubstorage( OUString& o_aStorageName ); + OUString MoveToTemporarySubstream(); + bool TryToConvertToOOo( const css::uno::Reference< css::io::XStream >& xStream ); + +public: + // in case a new object must be created the class ID must be specified + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName ); + + // in case object will be loaded from a persistent entry or from a file the class ID will be detected on loading + // factory can do it for OOo objects, but for OLE objects OS dependent code is required + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext, + bool bLink ); +#ifdef _WIN32 + // this constructor let object be initialized from clipboard + OleEmbeddedObject( const css::uno::Reference< css::uno::XComponentContext >& xContext ); +#endif + + virtual ~OleEmbeddedObject() override; + +#ifdef _WIN32 + static void OnIconChanged_Impl(); + void OnViewChanged_Impl(); + void OnClosed_Impl(); +#endif + +// XEmbeddedObject + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getReachableStates() override; + + virtual sal_Int32 SAL_CALL getCurrentState() override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + + virtual css::uno::Sequence< css::embed::VerbDescriptor > SAL_CALL getSupportedVerbs() override; + + virtual void SAL_CALL setClientSite( + const css::uno::Reference< css::embed::XEmbeddedClient >& xClient ) override; + + virtual css::uno::Reference< css::embed::XEmbeddedClient > SAL_CALL getClientSite() override; + + virtual void SAL_CALL update() override; + + virtual void SAL_CALL setUpdateMode( sal_Int32 nMode ) override; + + virtual sal_Int64 SAL_CALL getStatus( sal_Int64 nAspect ) override; + + virtual void SAL_CALL setContainerName( const OUString& sName ) override; + + +// XVisualObject + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + + +// XEmbedPersist + + virtual void SAL_CALL setPersistentEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeToEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL storeAsEntry( + const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + virtual void SAL_CALL saveCompleted( sal_Bool bUseNew ) override; + + virtual sal_Bool SAL_CALL hasEntry() override; + + virtual OUString SAL_CALL getEntryName() override; + +// XLinkageSupport + + virtual void SAL_CALL breakLink( const css::uno::Reference< css::embed::XStorage >& xStorage, + const OUString& sEntName ) override; + + virtual sal_Bool SAL_CALL isLink() override; + + virtual OUString SAL_CALL getLinkURL() override; + +// XCommonEmbedPersist + virtual void SAL_CALL storeOwn() override; + + virtual sal_Bool SAL_CALL isReadonly() override; + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + +// XClassifiedObject + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getClassID() override; + + virtual OUString SAL_CALL getClassName() override; + + virtual void SAL_CALL setClassInfo( + const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) override; + +// XStateChangeBroadcaster + virtual void SAL_CALL addStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + virtual void SAL_CALL removeStateChangeListener( const css::uno::Reference< css::embed::XStateChangeListener >& xListener ) override; + + +// XComponentSupplier + + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent() override; + +// XCloseable + + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + + virtual void SAL_CALL addCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + virtual void SAL_CALL removeCloseListener( + const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + +// XEventBroadcaster + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::document::XEventListener >& Listener ) override; + +// XInplaceObject ( only for wrapping scenario here ) + + virtual void SAL_CALL setObjectRectangles( const css::awt::Rectangle& aPosRect, + const css::awt::Rectangle& aClipRect ) override; + + virtual void SAL_CALL enableModeless( sal_Bool bEnable ) override; + + virtual void SAL_CALL translateAccelerators( + const css::uno::Sequence< css::awt::KeyEvent >& aKeys ) override; + + // XChild ( only for wrapping scenario here ) + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XActiveDataStreamer + void SAL_CALL setStream(const css::uno::Reference<css::io::XStream>& xStream) override; + css::uno::Reference<css::io::XStream> SAL_CALL getStream() override; + + // XInitialization + void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/specialobject.hxx b/embeddedobj/source/inc/specialobject.hxx new file mode 100644 index 000000000..9ec0d7d86 --- /dev/null +++ b/embeddedobj/source/inc/specialobject.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/awt/Size.hpp> + +#include "commonembobj.hxx" + +class OSpecialEmbeddedObject : public OCommonEmbeddedObject +{ +private: + css::awt::Size maSize; +public: + OSpecialEmbeddedObject( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Sequence< css::beans::NamedValue >& aObjectProps ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override ; + + // XVisualObject + virtual css::embed::VisualRepresentation SAL_CALL getPreferredVisualRepresentation( ::sal_Int64 nAspect ) override; + + virtual void SAL_CALL setVisualAreaSize( sal_Int64 nAspect, const css::awt::Size& aSize ) override; + + virtual css::awt::Size SAL_CALL getVisualAreaSize( sal_Int64 nAspect ) override; + + virtual sal_Int32 SAL_CALL getMapUnit( sal_Int64 nAspect ) override; + + virtual void SAL_CALL changeState( sal_Int32 nNewState ) override; + + virtual void SAL_CALL doVerb( sal_Int32 nVerbID ) override; + +// XCommonEmbedPersist + + virtual void SAL_CALL reload( + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/strings.hrc b/embeddedobj/source/inc/strings.hrc new file mode 100644 index 000000000..a3b323c54 --- /dev/null +++ b/embeddedobj/source/inc/strings.hrc @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String)) + +#define BTN_OVERWRITE_TEXT NC_("BTN_OVERWRITE_TEXT", "Overwrite") +#define STR_OVERWRITE_LINK NC_("STR_OVERWRITE_LINK", "You have made changes to the %{filename}, saving will overwrite the data from the inserted object.\n\nDo you still want to overwrite this data?") +#define STR_OVERWRITE_TEMP NC_("STR_OVERWRITE_TEMP", "You have changed the data in the inserted object which will be overwritten by updating the %{filename}.\n\nDo you still want to overwrite this data?") + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/embeddedobj/source/inc/targetstatecontrol.hxx b/embeddedobj/source/inc/targetstatecontrol.hxx new file mode 100644 index 000000000..15900f318 --- /dev/null +++ b/embeddedobj/source/inc/targetstatecontrol.hxx @@ -0,0 +1,42 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/types.h> +#include <osl/diagnose.h> + +class TargetStateControl_Impl +{ + sal_Int32& m_nTargetStateVariable; +public: + TargetStateControl_Impl( sal_Int32& nVariable, sal_Int32 nValue ) + : m_nTargetStateVariable( nVariable ) + { + OSL_ENSURE( m_nTargetStateVariable == -1, "The target state variable is not initialized properly!" ); + m_nTargetStateVariable = nValue; + } + + ~TargetStateControl_Impl() + { + m_nTargetStateVariable = -1; + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/inc/xcreator.hxx b/embeddedobj/source/inc/xcreator.hxx new file mode 100644 index 000000000..41b946aff --- /dev/null +++ b/embeddedobj/source/inc/xcreator.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObjectCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <comphelper/mimeconfighelper.hxx> + +class UNOEmbeddedObjectCreator : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + ::comphelper::MimeConfigurationHelper m_aConfigHelper; +public: + UNOEmbeddedObjectCreator( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_aConfigHelper( rxContext ) + { + OSL_ENSURE( rxContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitNew( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromMediaDescriptor( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& sClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& aArgs, const css::uno::Sequence< css::beans::PropertyValue >& aObjectArgs ) override; + + // XLinkCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLink( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/advisesink.cxx b/embeddedobj/source/msole/advisesink.cxx new file mode 100644 index 000000000..5601c8d9e --- /dev/null +++ b/embeddedobj/source/msole/advisesink.cxx @@ -0,0 +1,124 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include "advisesink.hxx" +#include "olecomponent.hxx" + +#include <rtl/ref.hxx> + +OleWrapperAdviseSink::OleWrapperAdviseSink( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperAdviseSink::~OleWrapperAdviseSink() +{ +} + +STDMETHODIMP OleWrapperAdviseSink::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IAdviseSink ) + *ppv = static_cast<IAdviseSink*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperAdviseSink::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperAdviseSink::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnDataChange(FORMATETC *, STGMEDIUM *) +{ + // Unused for now ( no registration for IDataObject events ) +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnViewChange(DWORD dwAspect, LONG) +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnViewChange_Impl( dwAspect ); +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnRename(IMoniker *) +{ + // handled by default inprocess handler +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnSave() +{ + // TODO: ??? + // The object already knows about document saving, since it controls it as ClientSide + // other interested listeners must be registered for the object +} + +STDMETHODIMP_(void) OleWrapperAdviseSink::OnClose() +{ + ::rtl::Reference< OleComponent > xLockComponent; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + xLockComponent = m_pOleComp; + } + + if ( xLockComponent.is() ) + xLockComponent->OnClose_Impl(); + + // TODO: sometimes it can be necessary to simulate OnShowWindow( False ) here +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/advisesink.hxx b/embeddedobj/source/msole/advisesink.hxx new file mode 100644 index 000000000..cd1bc3892 --- /dev/null +++ b/embeddedobj/source/msole/advisesink.hxx @@ -0,0 +1,51 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperAdviseSink : public IAdviseSink +{ +protected: + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + OleWrapperAdviseSink(OleComponent* pOleComp); + OleWrapperAdviseSink(); + virtual ~OleWrapperAdviseSink(); + + void disconnectOleComponent(); + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP_(void) OnDataChange(FORMATETC*, STGMEDIUM*) override; + STDMETHODIMP_(void) OnViewChange(DWORD, LONG) override; + STDMETHODIMP_(void) OnRename(IMoniker*) override; + STDMETHODIMP_(void) OnSave() override; + STDMETHODIMP_(void) OnClose() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/closepreventer.cxx b/embeddedobj/source/msole/closepreventer.cxx new file mode 100644 index 000000000..466975898 --- /dev/null +++ b/embeddedobj/source/msole/closepreventer.cxx @@ -0,0 +1,42 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <closepreventer.hxx> + +#include <com/sun/star/util/CloseVetoException.hpp> +#include <osl/diagnose.h> + +void SAL_CALL OClosePreventer::queryClosing(const css::lang::EventObject&, sal_Bool) +{ + throw css::util::CloseVetoException(); +} + +void SAL_CALL OClosePreventer::notifyClosing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +void SAL_CALL OClosePreventer::disposing(const css::lang::EventObject&) +{ + // just a disaster + OSL_FAIL("The object can not be prevented from closing!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/emboleobj.component b/embeddedobj/source/msole/emboleobj.component new file mode 100644 index 000000000..ac09d3a6d --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/emboleobj.windows.component b/embeddedobj/source/msole/emboleobj.windows.component new file mode 100644 index 000000000..89b644fc7 --- /dev/null +++ b/embeddedobj/source/msole/emboleobj.windows.component @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.MSOLEObjectSystemCreator" + constructor="embeddedobj_MSOLEDialogObjectCreator_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.MSOLEObjectSystemCreator"/> + <service name="com.sun.star.embed.MSOLEObjectSystemCreator"/> + </implementation> + <implementation name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory" + constructor="embeddedobj_OleEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OLEEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OLEEmbeddedObjectFactory"/> + </implementation> +</component> diff --git a/embeddedobj/source/msole/graphconvert.cxx b/embeddedobj/source/msole/graphconvert.cxx new file mode 100644 index 000000000..f853f03dc --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.cxx @@ -0,0 +1,132 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/seqstream.hxx> +#include <tools/stream.hxx> +#include <vcl/graphicfilter.hxx> + +#include "graphconvert.hxx" +#include "mtnotification.hxx" +#include <oleembobj.hxx> + + +using namespace ::com::sun::star; + + +bool ConvertBufferToFormat( void* pBuf, + sal_uInt32 nBufSize, + const OUString& aMimeType, + uno::Any& aResult ) +{ + // produces sequence with data in requested format and returns it in aResult + if ( pBuf ) + { + // First, in case the buffer is already in the requested format, then avoid a conversion. + SvMemoryStream aMemoryStream(pBuf, nBufSize, StreamMode::READ); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + sal_uInt16 nRetFormat = 0; + if (rFilter.CanImportGraphic(u"", aMemoryStream, GRFILTER_FORMAT_DONTKNOW, &nRetFormat) == ERRCODE_NONE && + rFilter.GetImportFormatMediaType(nRetFormat) == aMimeType) + { + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemoryStream.GetData() ), aMemoryStream.TellEnd() ); + return true; + } + + uno::Sequence < sal_Int8 > aData( static_cast<sal_Int8*>(pBuf), nBufSize ); + uno::Reference < io::XInputStream > xIn = new comphelper::SequenceInputStream( aData ); + try + { + uno::Reference < graphic::XGraphicProvider > xGraphicProvider( graphic::GraphicProvider::create(comphelper::getProcessComponentContext())); + uno::Sequence< beans::PropertyValue > aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xIn) }; + uno::Reference< graphic::XGraphic > xGraphic( xGraphicProvider->queryGraphic( aMediaProperties ) ); + if( xGraphic.is() ) + { + SvMemoryStream aNewStream( 65535, 65535 ); + uno::Reference < io::XStream > xOut = new utl::OStreamWrapper( aNewStream ); + uno::Sequence< beans::PropertyValue > aOutMediaProperties{ + comphelper::makePropertyValue("OutputStream", xOut), + comphelper::makePropertyValue("MimeType", aMimeType) + }; + + xGraphicProvider->storeGraphic( xGraphic, aOutMediaProperties ); + aResult <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aNewStream.GetData() ), aNewStream.TellEnd() ); + return true; + } + } + catch (const uno::Exception&) + {} + } + + return false; +} + + +// MainThreadNotificationRequest + +MainThreadNotificationRequest::MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect ) +: m_pObject( xObj.get() ) +, m_xObject( static_cast< embed::XEmbeddedObject* >( xObj.get() ) ) +, m_nNotificationType( nNotificationType ) +, m_nAspect( nAspect ) +{} + +void SAL_CALL MainThreadNotificationRequest::notify (const uno::Any& ) +{ + if ( m_pObject ) + { + try + { + uno::Reference< uno::XInterface > xLock = m_xObject.get(); + if ( xLock.is() ) + { + // this is the main thread, the solar mutex must be locked + if ( m_nNotificationType == OLECOMP_ONCLOSE ) + m_pObject->OnClosed_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_CONTENT ) + m_pObject->OnViewChanged_Impl(); + else if ( m_nAspect == embed::Aspects::MSOLE_ICON ) + OleEmbeddedObject::OnIconChanged_Impl(); + } + } + catch( const uno::Exception& ) + { + // ignore all the errors + } + } +} + +MainThreadNotificationRequest::~MainThreadNotificationRequest() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/graphconvert.hxx b/embeddedobj/source/msole/graphconvert.hxx new file mode 100644 index 000000000..cc5066de5 --- /dev/null +++ b/embeddedobj/source/msole/graphconvert.hxx @@ -0,0 +1,33 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class Any; } + +bool ConvertBufferToFormat( + void * pBuf, sal_uInt32 nBufSize, OUString const & aFormatShortName, + css::uno::Any & aResult); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/mtnotification.hxx b/embeddedobj/source/msole/mtnotification.hxx new file mode 100644 index 000000000..09888d04a --- /dev/null +++ b/embeddedobj/source/msole/mtnotification.hxx @@ -0,0 +1,48 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/awt/XCallback.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <rtl/ref.hxx> + +class OleEmbeddedObject; + +#define OLECOMP_ONVIEWCHANGE 1 +#define OLECOMP_ONCLOSE 2 + +class MainThreadNotificationRequest : public cppu::WeakImplHelper< css::awt::XCallback > +{ + OleEmbeddedObject* m_pObject; + css::uno::WeakReference< css::embed::XEmbeddedObject > m_xObject; + + sal_uInt16 m_nNotificationType; + sal_uInt32 m_nAspect; + +public: + virtual void SAL_CALL notify (const css::uno::Any& rUserData) override; + MainThreadNotificationRequest( const ::rtl::Reference< OleEmbeddedObject >& xObj, sal_uInt16 nNotificationType, sal_uInt32 nAspect = 0 ); + ~MainThreadNotificationRequest() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.cxx b/embeddedobj/source/msole/olecomponent.cxx new file mode 100644 index 000000000..f31113023 --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.cxx @@ -0,0 +1,1672 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/awt/XRequestCallback.hpp> + +#include "platform.h" +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <osl/file.hxx> +#include <rtl/ref.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <o3tl/unit_conversion.hxx> +#include <systools/win32/comtools.hxx> +#include <vcl/threadex.hxx> + +#include "graphconvert.hxx" +#include "olecomponent.hxx" +#include "olepersist.hxx" +#include "olewrapclient.hxx" +#include "advisesink.hxx" +#include <oleembobj.hxx> +#include "mtnotification.hxx" +#include <memory> +#include <string> + +using namespace ::com::sun::star; +using namespace ::comphelper; +#define MAX_ENUM_ELE 20 +#define FORMATS_NUM 3 + +typedef std::vector< FORMATETC* > FormatEtcList; + +FORMATETC const pFormatTemplates[FORMATS_NUM] = { + { CF_ENHMETAFILE, nullptr, 0, -1, TYMED_ENHMF }, + { CF_METAFILEPICT, nullptr, 0, -1, TYMED_MFPICT }, + { CF_BITMAP, nullptr, 0, -1, TYMED_GDI } }; + + +struct OleComponentNative_Impl { + sal::systools::COMReference< IUnknown > m_pObj; + sal::systools::COMReference< IOleObject > m_pOleObject; + sal::systools::COMReference< IViewObject2 > m_pViewObject2; + sal::systools::COMReference< IStorage > m_pIStorage; + FormatEtcList m_aFormatsList; + uno::Sequence< datatransfer::DataFlavor > m_aSupportedGraphFormats; + + OleComponentNative_Impl() + { + // TODO: Extend format list + m_aSupportedGraphFormats = { + + datatransfer::DataFlavor( + "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", + "Windows Enhanced Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "image/png", + "PNG", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ), + + datatransfer::DataFlavor( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", + "GDIMetafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + }; + } + + bool ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ); + + bool GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ); + + uno::Sequence< datatransfer::DataFlavor > GetFlavorsForAspects( sal_uInt32 nSupportedAspects ); +}; + + +static DWORD GetAspectFromFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + if ( aFlavor.MimeType.indexOf( ";Aspect=THUMBNAIL" ) != -1 ) + return DVASPECT_THUMBNAIL; + else if ( aFlavor.MimeType.indexOf( ";Aspect=ICON" ) != -1 ) + return DVASPECT_ICON; + else if ( aFlavor.MimeType.indexOf( ";Aspect=DOCPRINT" ) != -1 ) + return DVASPECT_DOCPRINT; + else + return DVASPECT_CONTENT; +} + + +static OUString GetFlavorSuffixFromAspect( DWORD nAsp ) +{ + OUString aResult; + + if ( nAsp == DVASPECT_THUMBNAIL ) + aResult = ";Aspect=THUMBNAIL"; + else if ( nAsp == DVASPECT_ICON ) + aResult = ";Aspect=ICON"; + else if ( nAsp == DVASPECT_DOCPRINT ) + aResult = ";Aspect=DOCPRINT"; + + // no suffix for DVASPECT_CONTENT + + return aResult; +} + + +static HRESULT OpenIStorageFromURL_Impl( const OUString& aURL, IStorage** ppIStorage ) +{ + OSL_ENSURE( ppIStorage, "The pointer must not be empty!" ); + + OUString aFilePath; + if ( !ppIStorage || ::osl::FileBase::getSystemPathFromFileURL( aURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + return StgOpenStorage( o3tl::toW(aFilePath.getStr()), + nullptr, + STGM_READWRITE | STGM_TRANSACTED, // | STGM_DELETEONRELEASE, + nullptr, + 0, + ppIStorage ); +} + + +bool OleComponentNative_Impl::ConvertDataForFlavor( const STGMEDIUM& aMedium, + const datatransfer::DataFlavor& aFlavor, + uno::Any& aResult ) +{ + bool bAnyIsReady = false; + + // try to convert data from Medium format to specified Flavor format + if ( aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + // first the GDI-metafile must be generated + + std::unique_ptr<sal_Int8[]> pBuf; + sal_uInt32 nBufSize = 0; + OUString aFormat; + + if ( aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + aFormat = "image/x-wmf"; + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ) + 22; + pBuf.reset(new sal_Int8[nBufSize]); + + + // TODO/LATER: the unit size must be calculated correctly + *reinterpret_cast<long*>( pBuf.get() ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf.get()+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf.get()+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf.get()+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf.get()+14 ) = USHORT(2540); + + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize - 22, pBuf.get() + 22 ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + + GlobalUnlock( aMedium.hMetaFilePict ); + } + } + else if ( aMedium.tymed == TYMED_ENHMF ) // Enh Metafile + { + aFormat = "image/x-emf"; + nBufSize = GetEnhMetaFileBits( aMedium.hEnhMetaFile, 0, nullptr ); + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == GetEnhMetaFileBits( aMedium.hEnhMetaFile, nBufSize, reinterpret_cast<LPBYTE>(pBuf.get()) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-emf;windows_formatname=\"Image EMF\"", 57 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + else if ( aMedium.tymed == TYMED_GDI ) // Bitmap + { + aFormat = "image/x-MS-bmp"; + + // Find out size of buffer: deprecated GetBitmapBits does not have a mode to return + // required buffer size + BITMAP aBmp; + GetObjectW(aMedium.hBitmap, sizeof(aBmp), &aBmp); + nBufSize = aBmp.bmWidthBytes * aBmp.bmHeight; + + pBuf.reset(new sal_Int8[nBufSize]); + if ( nBufSize && nBufSize == sal::static_int_cast< ULONG >( GetBitmapBits( aMedium.hBitmap, nBufSize, pBuf.get() ) ) ) + { + if ( aFlavor.MimeType.matchAsciiL( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", 54 ) ) + { + aResult <<= uno::Sequence< sal_Int8 >( pBuf.get(), nBufSize ); + bAnyIsReady = true; + } + } + } + + if ( pBuf && !bAnyIsReady ) + { + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType + && aFlavor.DataType == cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) + { + bAnyIsReady = ConvertBufferToFormat( pBuf.get(), nBufSize, aFormat, aResult ); + break; + } + } + } + + return bAnyIsReady; +} + + +bool OleComponentNative_Impl::GraphicalFlavor( const datatransfer::DataFlavor& aFlavor ) +{ + // Actually all the required graphical formats must be supported + for ( auto const & supportedFormat : std::as_const(m_aSupportedGraphFormats) ) + if ( aFlavor.MimeType.match( supportedFormat.MimeType ) + && aFlavor.DataType == supportedFormat.DataType ) + return true; + + return false; +} + + +static bool GetClassIDFromSequence_Impl( uno::Sequence< sal_Int8 > const & aSeq, CLSID& aResult ) +{ + if ( aSeq.getLength() == 16 ) + { + aResult.Data1 = ( ( ( ( ( static_cast<sal_uInt8>(aSeq[0]) << 8 ) + static_cast<sal_uInt8>(aSeq[1]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[2]) ) << 8 ) + static_cast<sal_uInt8>(aSeq[3]); + aResult.Data2 = ( static_cast<sal_uInt8>(aSeq[4]) << 8 ) + static_cast<sal_uInt8>(aSeq[5]); + aResult.Data3 = ( static_cast<sal_uInt8>(aSeq[6]) << 8 ) + static_cast<sal_uInt8>(aSeq[7]); + for( int nInd = 0; nInd < 8; nInd++ ) + aResult.Data4[nInd] = static_cast<sal_uInt8>(aSeq[nInd+8]); + + return true; + } + + return false; +} + + +static OUString WinAccToVcl_Impl( const sal_Unicode* pStr ) +{ + OUString aResult; + + if( pStr ) + { + while ( *pStr ) + { + if ( *pStr == '&' ) + { + aResult += "~"; + while( *( ++pStr ) == '&' ); + } + else + { + aResult += OUStringChar( *pStr ); + pStr++; + } + } + } + + return aResult; +} + + +OleComponent::OleComponent( const uno::Reference< uno::XComponentContext >& xContext, OleEmbeddedObject* pUnoOleObject ) +: m_pInterfaceContainer( nullptr ) +, m_bDisposed( false ) +, m_bModified( false ) +, m_pNativeImpl( new OleComponentNative_Impl() ) +, m_pUnoOleObject( pUnoOleObject ) +, m_pOleWrapClientSite( nullptr ) +, m_pImplAdviseSink( nullptr ) +, m_nOLEMiscFlags( 0 ) +, m_nAdvConn( 0 ) +, m_xContext( xContext ) +, m_bOleInitialized( false ) +, m_bWorkaroundActive( false ) +{ + OSL_ENSURE( m_pUnoOleObject, "No owner object is provided!" ); + + HRESULT hr = OleInitialize( nullptr ); + if ( hr == S_OK || hr == S_FALSE ) + m_bOleInitialized = true; + else + { + SAL_WARN("embeddedobj.ole", "OleComponent ctor: OleInitialize() failed with 0x" + << OUString::number(static_cast<sal_uInt32>(hr), 16) << ": " + << WindowsErrorStringFromHRESULT(hr)); + } + + m_pOleWrapClientSite = new OleWrapperClientSite( this ); + m_pOleWrapClientSite->AddRef(); + + m_pImplAdviseSink = new OleWrapperAdviseSink( this ); + m_pImplAdviseSink->AddRef(); + +} + + +OleComponent::~OleComponent() +{ + OSL_ENSURE( !m_pOleWrapClientSite && !m_pImplAdviseSink && !m_pInterfaceContainer && !m_bOleInitialized, + "The object was not closed successfully! DISASTER is possible!" ); + + if ( m_pOleWrapClientSite || m_pImplAdviseSink || m_pInterfaceContainer || m_bOleInitialized ) + { + osl_atomic_increment(&m_refCount); + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + for (auto const& format : m_pNativeImpl->m_aFormatsList) + { + delete format; + } + m_pNativeImpl->m_aFormatsList.clear(); + + delete m_pNativeImpl; +} + +void OleComponent::Dispose() +{ + if ( m_bDisposed ) + return; + + // Call CloseObject() without m_aMutex locked, since it will call + // IOleObject::Close(), which can call event listeners, which can run on a + // different thread. + CloseObject(); + + osl::MutexGuard aGuard(m_aMutex); + if ( m_pOleWrapClientSite ) + { + m_pOleWrapClientSite->disconnectOleComponent(); + m_pOleWrapClientSite->Release(); + m_pOleWrapClientSite = nullptr; + } + + if ( m_pImplAdviseSink ) + { + m_pImplAdviseSink->disconnectOleComponent(); + m_pImplAdviseSink->Release(); + m_pImplAdviseSink = nullptr; + } + + if ( m_pInterfaceContainer ) + { + lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aEvent ); + + delete m_pInterfaceContainer; + m_pInterfaceContainer = nullptr; + } + + if ( m_bOleInitialized ) + { + // since the disposing can happen not only from main thread but also from a clipboard + // the deinitialization might lead to a disaster, SO7 does not deinitialize OLE at all + // so currently the same approach is selected as workaround + // OleUninitialize(); + m_bOleInitialized = false; + } + + m_bDisposed = true; +} + + +void OleComponent::disconnectEmbeddedObject() +{ + // must not be called from destructor of UNO OLE object!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pUnoOleObject = nullptr; +} + + +void OleComponent::CreateNewIStorage_Impl() +{ + // TODO: in future a global memory could be used instead of file. + + // write the stream to the temporary file + OUString aTempURL; + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + aTempURL = m_pUnoOleObject->CreateTempURLEmpty_Impl(); + else + aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + if ( !aTempURL.getLength() ) + throw uno::RuntimeException(); // TODO + + // open an IStorage based on the temporary file + OUString aTempFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aTempURL, aTempFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = StgCreateDocfile( o3tl::toW(aTempFilePath.getStr()), STGM_CREATE | STGM_READWRITE | STGM_TRANSACTED | STGM_DELETEONRELEASE, 0, &m_pNativeImpl->m_pIStorage ); + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? +} + + +uno::Sequence< datatransfer::DataFlavor > OleComponentNative_Impl::GetFlavorsForAspects( sal_uInt32 nSupportedAspects ) +{ + uno::Sequence< datatransfer::DataFlavor > aResult; + for ( sal_uInt32 nAsp = 1; nAsp <= 8; nAsp *= 2 ) + if ( ( nSupportedAspects & nAsp ) == nAsp ) + { + OUString aAspectSuffix = GetFlavorSuffixFromAspect( nAsp ); + + sal_Int32 nLength = aResult.getLength(); + aResult.realloc( nLength + m_aSupportedGraphFormats.getLength() ); + auto pResult = aResult.getArray(); + + for ( sal_Int32 nInd = 0; nInd < m_aSupportedGraphFormats.getLength(); nInd++ ) + { + pResult[nLength + nInd].MimeType = m_aSupportedGraphFormats[nInd].MimeType + aAspectSuffix; + pResult[nLength + nInd].HumanPresentableName = m_aSupportedGraphFormats[nInd].HumanPresentableName; + pResult[nLength + nInd].DataType = m_aSupportedGraphFormats[nInd].DataType; + } + } + + return aResult; +} + + +void OleComponent::RetrieveObjectDataFlavors_Impl() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + sal::systools::COMReference< IEnumFORMATETC > pFormatEnum; + HRESULT hr = pDataObject->EnumFormatEtc( DATADIR_GET, &pFormatEnum ); + if ( SUCCEEDED( hr ) && pFormatEnum ) + { + FORMATETC pElem[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + + // if it is possible to retrieve at least one supported graphical format for an aspect + // this format can be converted to other supported formats + sal_uInt32 nSupportedAspects = 0; + do + { + HRESULT hr2 = pFormatEnum->Next( MAX_ENUM_ELE, pElem, &nNum ); + if( hr2 == S_OK || hr2 == S_FALSE ) + { + for( sal_uInt32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + if ( pElem[nInd].cfFormat == pFormatTemplates[nInd].cfFormat + && pElem[nInd].tymed == pFormatTemplates[nInd].tymed ) + nSupportedAspects |= pElem[nInd].dwAspect; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + + m_aDataFlavors = m_pNativeImpl->GetFlavorsForAspects( nSupportedAspects ); + } + } + + if ( !m_aDataFlavors.getLength() ) + { + // TODO: + // for any reason the object could not provide this information + // try to get access to the cached representation + } + } +} + + +bool OleComponent::InitializeObject_Impl() +// There will be no static objects! +{ + if ( !m_pNativeImpl->m_pObj ) + return false; + + // the linked object will be detected here + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( m_pUnoOleObject ) + m_pUnoOleObject->SetObjectIsLink_Impl( m_pNativeImpl->m_pObj.QueryInterface<IOleLink>(sal::systools::COM_QUERY).is() ); + + if ( !m_pNativeImpl->m_pViewObject2.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; + + // remove all the caches + if ( sal::systools::COMReference< IOleCache > pIOleCache{ m_pNativeImpl->m_pObj, sal::systools::COM_QUERY } ) + { + IEnumSTATDATA* pEnumSD = nullptr; + HRESULT hr2 = pIOleCache->EnumCache( &pEnumSD ); + + if ( SUCCEEDED( hr2 ) && pEnumSD ) + { + pEnumSD->Reset(); + STATDATA aSD; + DWORD nNum; + while( SUCCEEDED( pEnumSD->Next( 1, &aSD, &nNum ) ) && nNum == 1 ) + hr2 = pIOleCache->Uncache( aSD.dwConnection ); + } + + // No IDataObject implementation, caching must be used instead + DWORD nConn; + FORMATETC aFormat = { 0, nullptr, DVASPECT_CONTENT, -1, TYMED_MFPICT }; + hr2 = pIOleCache->Cache( &aFormat, ADVFCACHE_ONSAVE, &nConn ); + } + + if ( !m_pNativeImpl->m_pOleObject.set(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY) ) + return false; // Static objects are not supported, they should be inserted as graphics + + m_pNativeImpl->m_pOleObject->GetMiscStatus( DVASPECT_CONTENT, reinterpret_cast<DWORD*>(&m_nOLEMiscFlags) ); + // TODO: use other misc flags also + // the object should have drawable aspect even in case it supports only iconic representation + // if ( m_nOLEMiscFlags & OLEMISC_ONLYICONIC ) + + m_pNativeImpl->m_pOleObject->SetClientSite( m_pOleWrapClientSite ); + + // the only need in this registration is workaround for close notification + m_pNativeImpl->m_pOleObject->Advise( m_pImplAdviseSink, reinterpret_cast<DWORD*>(&m_nAdvConn) ); + m_pNativeImpl->m_pViewObject2->SetAdvise( DVASPECT_CONTENT, 0, m_pImplAdviseSink ); + + OleSetContainedObject( m_pNativeImpl->m_pOleObject, TRUE ); + + return true; +} + +namespace +{ + HRESULT OleLoadSeh(LPSTORAGE pIStorage, LPVOID* ppObj) + { + HRESULT hr = E_FAIL; + // tdf#119039: there is a nasty bug in OleLoad, that may call an unpaired + // IUnknown::Release on pIStorage on STG_E_FILENOTFOUND: see + // https://developercommunity.visualstudio.com/t/10144795 + // Workaround it here to avoid crash in smart COM pointer destructor that + // would try to release already released object. Since we don't know if + // the bug appears each time STG_E_FILENOTFOUND is returned, this might + // potentially leak the storge object. + if (pIStorage) + pIStorage->AddRef(); + + __try { + hr = OleLoad(pIStorage, IID_IUnknown, nullptr, ppObj); + } __except( EXCEPTION_EXECUTE_HANDLER ) { + hr = E_FAIL; + } + if (pIStorage && hr != STG_E_FILENOTFOUND) + pIStorage->Release(); + + return hr; + } +} + +void OleComponent::LoadEmbeddedObject( const OUString& aTempURL ) +{ + if ( !aTempURL.getLength() ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO the object is already initialized or wrong initialization is done + + // open an IStorage based on the temporary file + HRESULT hr = OpenIStorageFromURL_Impl( aTempURL, &m_pNativeImpl->m_pIStorage ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO: transport error code? + + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + { + throw uno::RuntimeException(); + } + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateObjectFromClipboard() +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + IDataObject * pDO = nullptr; + HRESULT hr = OleGetClipboard( &pDO ); + if( SUCCEEDED( hr ) && pDO ) + { + hr = OleQueryCreateFromData( pDO ); + if( S_OK == GetScode( hr ) ) + { + hr = OleCreateFromData( pDO, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + else + { + // Static objects are not supported + pDO->Release(); + } + } + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateNewEmbeddedObject( const uno::Sequence< sal_Int8 >& aSeqCLSID ) +{ + CLSID aClsID; + + if ( !GetClassIDFromSequence_Impl( aSeqCLSID, aClsID ) ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO + + // FORMATETC aFormat = { CF_METAFILEPICT, NULL, nAspect, -1, TYMED_MFPICT }; // for OLE..._DRAW should be NULL + + HRESULT hr = OleCreate( aClsID, + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, // &aFormat, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO + + // TODO: getExtent??? +} + + +void OleComponent::CreateObjectFromData( const uno::Reference< datatransfer::XTransferable >& ) +// Static objects are not supported, they should be inserted as graphics +{ + // TODO: May be this call is useless since there are no static objects + // and nonstatic objects will be created based on OLEstorage ( stream ). + // ??? + + // OleQueryCreateFromData... +} + + +void OleComponent::CreateObjectFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateFromFile( CLSID_NULL, + o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::CreateLinkFromFile( const OUString& aFileURL ) +{ + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OUString aFilePath; + if ( ::osl::FileBase::getSystemPathFromFileURL( aFileURL, aFilePath ) != ::osl::FileBase::E_None ) + throw uno::RuntimeException(); // TODO: something dangerous happened + + HRESULT hr = OleCreateLinkToFile( o3tl::toW(aFilePath.getStr()), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + + if ( FAILED( hr ) || !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ) +{ + if ( !pOleLinkComponent || !pOleLinkComponent->m_pNativeImpl->m_pObj ) + throw lang::IllegalArgumentException(); // TODO + + if ( m_pNativeImpl->m_pIStorage ) + throw io::IOException(); // TODO:the object is already initialized + + sal::systools::COMReference< IDataObject > pDataObject(pOleLinkComponent->m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject && SUCCEEDED( OleQueryCreateFromData( pDataObject ) ) ) + { + // the object must be already disconnected from the temporary URL + CreateNewIStorage_Impl(); + if ( !m_pNativeImpl->m_pIStorage ) + throw uno::RuntimeException(); // TODO: + + OleCreateFromData( pDataObject, + IID_IUnknown, + OLERENDER_DRAW, + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IOleLink > pOleLink(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pOleLink ) + throw io::IOException(); // TODO: the object doesn't support IOleLink + + sal::systools::COMReference< IMoniker > pMoniker; + HRESULT hr = pOleLink->GetSourceMoniker( &pMoniker ); + if ( FAILED( hr ) || !pMoniker ) + throw io::IOException(); // TODO: can not retrieve moniker + + // In case of file moniker life is easy : ) + DWORD aMonType = 0; + hr = pMoniker->IsSystemMoniker( &aMonType ); + if ( SUCCEEDED( hr ) && aMonType == MKSYS_FILEMONIKER ) + { + sal::systools::COMReference< IMalloc > pMalloc; + hr = CoGetMalloc( 1, &pMalloc ); // if fails there will be a memory leak + OSL_ENSURE(SUCCEEDED(hr) && pMalloc, "CoGetMalloc() failed!"); + + LPOLESTR pOleStr = nullptr; + hr = pOleLink->GetSourceDisplayName( &pOleStr ); + if ( SUCCEEDED( hr ) && pOleStr ) + { + std::wstring aFilePath( pOleStr ); + if ( pMalloc ) + pMalloc->Free( pOleStr ); + + hr = OleCreateFromFile( CLSID_NULL, + aFilePath.c_str(), + IID_IUnknown, + OLERENDER_DRAW, // OLERENDER_FORMAT + nullptr, + nullptr, + m_pNativeImpl->m_pIStorage, + reinterpret_cast<void**>(&m_pNativeImpl->m_pObj) ); + } + } + + // in case of other moniker types the only way is to get storage + if ( !m_pNativeImpl->m_pObj ) + { + sal::systools::COMReference< IBindCtx > pBindCtx; + hr = CreateBindCtx( 0, &pBindCtx ); + if ( SUCCEEDED( hr ) && pBindCtx ) + { + sal::systools::COMReference< IStorage > pObjectStorage; + hr = pMoniker->BindToStorage( pBindCtx, nullptr, IID_IStorage, reinterpret_cast<void**>(&pObjectStorage) ); + if ( SUCCEEDED( hr ) && pObjectStorage ) + { + hr = pObjectStorage->CopyTo( 0, nullptr, nullptr, m_pNativeImpl->m_pIStorage ); + if ( SUCCEEDED( hr ) ) + hr = OleLoadSeh(m_pNativeImpl->m_pIStorage, reinterpret_cast<void**>(&m_pNativeImpl->m_pObj)); + } + } + } + } + + // If object could not be created the only way is to use graphical representation + if ( !m_pNativeImpl->m_pObj ) + throw uno::RuntimeException(); // TODO + + if ( !InitializeObject_Impl() ) + throw uno::RuntimeException(); // TODO +} + + +void OleComponent::RunObject() +{ + OSL_ENSURE( m_pNativeImpl->m_pOleObject, "The pointer can not be set to NULL here!" ); + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + { + HRESULT hr = OleRun( m_pNativeImpl->m_pObj ); + + if ( FAILED( hr ) ) + { + if ( hr == REGDB_E_CLASSNOTREG ) + throw embed::UnreachableStateException(); // the object server is not installed + else + throw io::IOException(); + } + } +} + + +awt::Size OleComponent::CalculateWithFactor( const awt::Size& aSize, + const awt::Size& aMultiplier, + const awt::Size& aDivisor ) +{ + awt::Size aResult; + + sal_Int64 nWidth = static_cast<sal_Int64>(aSize.Width) * static_cast<sal_Int64>(aMultiplier.Width) / static_cast<sal_Int64>(aDivisor.Width); + sal_Int64 nHeight = static_cast<sal_Int64>(aSize.Height) * static_cast<sal_Int64>(aMultiplier.Height) / static_cast<sal_Int64>(aDivisor.Height); + OSL_ENSURE( nWidth < SAL_MAX_INT32 && nWidth > SAL_MIN_INT32 + && nHeight < SAL_MAX_INT32 && nHeight > SAL_MIN_INT32, + "Unacceptable result size!" ); + + aResult.Width = static_cast<sal_Int32>(nWidth); + aResult.Height = static_cast<sal_Int32>(nHeight); + + return aResult; +} + + +void OleComponent::CloseObject() +{ + if ( m_pNativeImpl->m_pOleObject && OleIsRunning( m_pNativeImpl->m_pOleObject ) ) + m_pNativeImpl->m_pOleObject->Close( OLECLOSE_NOSAVE ); // must be saved before +} + + +uno::Sequence< embed::VerbDescriptor > OleComponent::GetVerbList() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if( !m_aVerbList.getLength() ) + { + sal::systools::COMReference< IEnumOLEVERB > pEnum; + if( SUCCEEDED( m_pNativeImpl->m_pOleObject->EnumVerbs( &pEnum ) ) ) + { + OLEVERB szEle[ MAX_ENUM_ELE ]; + ULONG nNum = 0; + sal_Int32 nSeqSize = 0; + + do + { + HRESULT hr = pEnum->Next( MAX_ENUM_ELE, szEle, &nNum ); + if( hr == S_OK || hr == S_FALSE ) + { + m_aVerbList.realloc( nSeqSize += nNum ); + auto pVerbList = m_aVerbList.getArray(); + for( sal_uInt32 nInd = 0; nInd < nNum; nInd++ ) + { + pVerbList[nSeqSize-nNum+nInd].VerbID = szEle[ nInd ].lVerb; + pVerbList[nSeqSize-nNum+nInd].VerbName = WinAccToVcl_Impl( o3tl::toU(szEle[ nInd ].lpszVerbName) ); + pVerbList[nSeqSize-nNum+nInd].VerbFlags = szEle[ nInd ].fuFlags; + pVerbList[nSeqSize-nNum+nInd].VerbAttributes = szEle[ nInd ].grfAttribs; + } + } + else + break; + } + while( nNum == MAX_ENUM_ELE ); + } + } + + return m_aVerbList; +} + + +void OleComponent::ExecuteVerb( sal_Int32 nVerbID ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO + + HRESULT hr = OleRun( m_pNativeImpl->m_pOleObject ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: a specific exception that transport error code can be thrown here + + // TODO: probably extents should be set here and stored in aRect + // TODO: probably the parent window also should be set + hr = m_pNativeImpl->m_pOleObject->DoVerb( nVerbID, nullptr, m_pOleWrapClientSite, 0, nullptr, nullptr ); + + if ( FAILED( hr ) ) + throw io::IOException(); // TODO +} + + +void OleComponent::SetHostName( const OUString& aEmbDocName ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + m_pNativeImpl->m_pOleObject->SetHostNames( L"app name", o3tl::toW( aEmbDocName.getStr() ) ); +} + + +void OleComponent::SetExtent( const awt::Size& aVisAreaSize, sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + + SIZEL aSize = { aVisAreaSize.Width, aVisAreaSize.Height }; + HRESULT hr = m_pNativeImpl->m_pOleObject->SetExtent( nMSAspect, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? In future user code probably should be ready for the exception. + // if the object is running but not activated, RPC_E_SERVER_DIED error code is returned by OLE package + // in this case just do nothing + // Also Visio returns E_FAIL on resize if it is in running state + // if ( hr != RPC_E_SERVER_DIED ) + throw io::IOException(); // TODO + } +} + + +awt::Size OleComponent::GetExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + awt::Size aSize; + bool bGotSize = false; + + if ( nMSAspect == DVASPECT_CONTENT ) + { + // Try to get the size from the replacement image first + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( pDataObject ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[1]; // use windows metafile format + aFormat.dwAspect = nMSAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + + if (hr == RPC_E_WRONG_THREAD) + { + // Assume that the OLE object was loaded on the main thread. + vcl::solarthread::syncExecute([this, &hr, &pDataObject, &aFormat, &aMedium]() { + // Make sure that the current state is embed::EmbedStates::RUNNING. + RunObject(); + // Now try again on the correct thread. + hr = pDataObject->GetData(&aFormat, &aMedium); + }); + } + + if ( SUCCEEDED( hr ) && aMedium.tymed == TYMED_MFPICT ) // Win Metafile + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( aMedium.hMetaFilePict )); + if ( pMF ) + { + // the object uses 0.01 mm as unit, so the metafile size should be converted to object unit + o3tl::Length eFrom = o3tl::Length::mm100; + switch( pMF->mm ) + { + case MM_HIENGLISH: + eFrom = o3tl::Length::in1000; + break; + + case MM_LOENGLISH: + eFrom = o3tl::Length::in100; + break; + + case MM_LOMETRIC: + eFrom = o3tl::Length::mm10; + break; + + case MM_TWIPS: + eFrom = o3tl::Length::twip; + break; + + case MM_ISOTROPIC: + case MM_ANISOTROPIC: + case MM_HIMETRIC: + // do nothing + break; + } + + sal_Int64 nX = o3tl::convert(abs( pMF->xExt ), eFrom, o3tl::Length::mm100); + sal_Int64 nY = o3tl::convert(abs( pMF->yExt ), eFrom, o3tl::Length::mm100); + if ( nX < SAL_MAX_INT32 && nY < SAL_MAX_INT32 ) + { + aSize.Width = static_cast<sal_Int32>(nX); + aSize.Height = static_cast<sal_Int32>(nY); + bGotSize = true; + } + else + OSL_FAIL( "Unexpected size is provided!" ); + } + } + else if (!SUCCEEDED(hr)) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetExtent: GetData() failed"); + } + // i113605, to release storage medium + if ( SUCCEEDED( hr ) ) + ::ReleaseStgMedium(&aMedium); + } + } + + if ( !bGotSize ) + throw lang::IllegalArgumentException(); + + return aSize; +} + + +awt::Size OleComponent::GetCachedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + + HRESULT hr = m_pNativeImpl->m_pViewObject2->GetExtent( nMSAspect, -1, nullptr, &aSize ); + + if ( FAILED( hr ) ) + { + // TODO/LATER: is it correct? + // if there is no appropriate cache for the aspect, OLE_E_BLANK error code is returned + // if ( hr == OLE_E_BLANK ) + // throw lang::IllegalArgumentException(); + //else + // throw io::IOException(); // TODO + + SAL_WARN("embeddedobj.ole", " OleComponent::GetCachedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +awt::Size OleComponent::GetRecommendedExtent( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nMSAspect = static_cast<DWORD>(nAspect); // first 32 bits are for MS aspects + SIZEL aSize; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetExtent( nMSAspect, &aSize ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", " OleComponent::GetRecommendedExtent: GetExtent() failed"); + throw lang::IllegalArgumentException(); + } + + return awt::Size( aSize.cx, aSize.cy ); +} + + +sal_Int64 OleComponent::GetMiscStatus( sal_Int64 nAspect ) +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + DWORD nResult; + m_pNativeImpl->m_pOleObject->GetMiscStatus( static_cast<DWORD>(nAspect), &nResult ); + return static_cast<sal_Int64>(nResult); // first 32 bits are for MS flags +} + + +uno::Sequence< sal_Int8 > OleComponent::GetCLSID() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + GUID aCLSID; + HRESULT hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO: + + return MimeConfigurationHelper::GetSequenceClassID( aCLSID.Data1, aCLSID.Data2, aCLSID.Data3, + aCLSID.Data4[0], aCLSID.Data4[1], + aCLSID.Data4[2], aCLSID.Data4[3], + aCLSID.Data4[4], aCLSID.Data4[5], + aCLSID.Data4[6], aCLSID.Data4[7] ); +} + + +bool OleComponent::IsDirty() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( IsWorkaroundActive() ) + return true; + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + HRESULT hr = pPersistStorage->IsDirty(); + return ( hr != S_FALSE ); +} + + +void OleComponent::StoreOwnTmpIfNecessary() +{ + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + sal::systools::COMReference< IPersistStorage > pPersistStorage(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pPersistStorage ) + throw io::IOException(); // TODO + + if ( m_bWorkaroundActive || pPersistStorage->IsDirty() != S_FALSE ) + { + HRESULT hr = OleSave( pPersistStorage, m_pNativeImpl->m_pIStorage, TRUE ); + if ( FAILED( hr ) ) + { + // Till now was required only for AcrobatReader7.0.8 + GUID aCLSID; + hr = m_pNativeImpl->m_pOleObject->GetUserClassID( &aCLSID ); + if ( FAILED( hr ) ) + { + SAL_WARN("embeddedobj.ole", "OleComponent::StoreOwnTmpIfNecessary: GetUserClassID() failed"); + throw io::IOException(); // TODO + } + + hr = WriteClassStg( m_pNativeImpl->m_pIStorage, aCLSID ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + // the result of the following call is not checked because some objects, for example AcrobatReader7.0.8 + // return error even in case the saving was done correctly + hr = pPersistStorage->Save( m_pNativeImpl->m_pIStorage, TRUE ); + + // another workaround for AcrobatReader7.0.8 object, this object might think that it is not changed + // when it has been created from file, although it must be saved + m_bWorkaroundActive = true; + } + + hr = m_pNativeImpl->m_pIStorage->Commit( STGC_DEFAULT ); + if ( FAILED( hr ) ) + throw io::IOException(); // TODO + + hr = pPersistStorage->SaveCompleted( nullptr ); + if ( FAILED( hr ) && hr != E_UNEXPECTED ) + throw io::IOException(); // TODO + + } +} + + +bool OleComponent::SaveObject_Impl() +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->SaveObject_Impl(); + pLockObject->release(); + } + + return bResult; +} + + +bool OleComponent::OnShowWindow_Impl( bool bShow ) +{ + bool bResult = false; + OleEmbeddedObject* pLockObject = nullptr; + + { + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pUnoOleObject ) + { + pLockObject = m_pUnoOleObject; + pLockObject->acquire(); + } + } + + if ( pLockObject ) + { + bResult = pLockObject->OnShowWindow_Impl( bShow ); + pLockObject->release(); + } + + return bResult; +} + + +void OleComponent::OnViewChange_Impl( sal_uInt32 dwAspect ) +{ + // TODO: check if it is enough or may be saving notifications are required for Visio2000 + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONVIEWCHANGE, dwAspect ), uno::Any() ); + } +} + + +void OleComponent::OnClose_Impl() +{ + ::rtl::Reference< OleEmbeddedObject > xLockObject; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pUnoOleObject ) + xLockObject = m_pUnoOleObject; + } + + if ( xLockObject.is() ) + { + uno::Reference < awt::XRequestCallback > xRequestCallback( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.AsyncCallback", m_xContext), + uno::UNO_QUERY ); + xRequestCallback->addCallback( new MainThreadNotificationRequest( xLockObject, OLECOMP_ONCLOSE ), uno::Any() ); + } +} + +// XCloseable + +void SAL_CALL OleComponent::close( sal_Bool bDeliverOwnership ) +{ + uno::Reference< uno::XInterface > xSelfHold; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw lang::DisposedException(); // TODO + + xSelfHold.set(static_cast<::cppu::OWeakObject*>(this)); + lang::EventObject aSource(static_cast<::cppu::OWeakObject*>(this)); + + if (m_pInterfaceContainer) + { + comphelper::OInterfaceContainerHelper2* pContainer + = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get()); + if (pContainer != nullptr) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next()) + ->queryClosing(aSource, bDeliverOwnership); + } + catch (const uno::RuntimeException&) + { + pIterator.remove(); + } + } + } + + pContainer + = m_pInterfaceContainer->getContainer(cppu::UnoType<util::XCloseListener>::get()); + if (pContainer != nullptr) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next()) + ->notifyClosing(aSource); + } + catch (const uno::RuntimeException&) + { + pCloseIterator.remove(); + } + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleComponent::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + +// XTransferable + +uno::Any SAL_CALL OleComponent::getTransferData( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + uno::Any aResult; + bool bSupportedFlavor = false; + + if ( m_pNativeImpl->GraphicalFlavor( aFlavor ) ) + { + DWORD nRequestedAspect = GetAspectFromFlavor( aFlavor ); + // if own icon is set and icon aspect is requested the own icon can be returned directly + + sal::systools::COMReference< IDataObject > pDataObject(m_pNativeImpl->m_pObj, sal::systools::COM_QUERY); + if ( !pDataObject ) + throw io::IOException(); // TODO: transport error code + + // The following optimization does not make much sense currently just because + // only one aspect is supported, and only three formats for the aspect are supported + // and moreover it is not guaranteed that the once returned format will be supported further + // example - i52106 + // TODO/LATER: bring the optimization back when other aspects are supported + + // FORMATETC* pFormatEtc = m_pNativeImpl->GetSupportedFormatForAspect( nRequestedAspect ); + // if ( pFormatEtc ) + // { + // STGMEDIUM aMedium; + // hr = pDataObject->GetData( pFormatEtc, &aMedium ); + // if ( SUCCEEDED( hr ) ) + // bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + // } + // else + { + // the supported format of the application is still not found, find one + for ( sal_Int32 nInd = 0; nInd < FORMATS_NUM; nInd++ ) + { + STGMEDIUM aMedium; + FORMATETC aFormat = pFormatTemplates[nInd]; + aFormat.dwAspect = nRequestedAspect; + + HRESULT hr = pDataObject->GetData( &aFormat, &aMedium ); + if ( SUCCEEDED( hr ) ) + { + bSupportedFlavor = m_pNativeImpl->ConvertDataForFlavor( aMedium, aFlavor, aResult ); + ::ReleaseStgMedium(&aMedium); // i113605, to release storage medium + if ( bSupportedFlavor ) + { + // TODO/LATER: bring the optimization back when other aspects are supported + // m_pNativeImpl->AddSupportedFormat( aFormat ); + break; + } + } + } + } + + // If the replacement could not be retrieved, the cached representation should be used + // currently it is not necessary to retrieve it here, so it is implemented in the object itself + } + // TODO: Investigate if there is already the format name + // and whether this format is really required + else if ( aFlavor.DataType == cppu::UnoType<io::XInputStream>::get() + && aFlavor.MimeType == "application/x-openoffice-contentstream" ) + { + // allow to retrieve stream-representation of the object persistence + bSupportedFlavor = true; + uno::Reference < io::XStream > xTempFileStream( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFileStream->getOutputStream(); + uno::Reference< io::XInputStream > xTempInStream = xTempFileStream->getInputStream(); + if ( !(xTempOutStream.is() && xTempInStream.is()) ) + throw io::IOException(); // TODO: + + OSL_ENSURE( m_pUnoOleObject, "Unexpected object absence!" ); + if ( !m_pUnoOleObject ) + throw uno::RuntimeException(); + + m_pUnoOleObject->StoreObjectToStream( xTempOutStream ); + + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + + aResult <<= xTempInStream; + } + + if ( !bSupportedFlavor ) + throw datatransfer::UnsupportedFlavorException(); + + return aResult; +} + + +uno::Sequence< datatransfer::DataFlavor > SAL_CALL OleComponent::getTransferDataFlavors() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + RetrieveObjectDataFlavors_Impl(); + + return m_aDataFlavors; +} + + +sal_Bool SAL_CALL OleComponent::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pNativeImpl->m_pOleObject ) + throw embed::WrongStateException(); // TODO: the object is in wrong state + + if ( !m_aDataFlavors.getLength() ) + { + RetrieveObjectDataFlavors_Impl(); + } + + for ( auto const & supportedFormat : std::as_const(m_aDataFlavors) ) + if ( supportedFormat.MimeType.equals( aFlavor.MimeType ) && supportedFormat.DataType == aFlavor.DataType ) + return true; + + return false; +} + +void SAL_CALL OleComponent::dispose() +{ + try + { + close( true ); + } + catch ( const uno::Exception& ) + { + } +} + +void SAL_CALL OleComponent::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OleComponent::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<lang::XEventListener>::get(), + xListener ); +} + +sal_Int64 SAL_CALL OleComponent::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) +{ + try + { + uno::Sequence < sal_Int8 > aCLSID = GetCLSID(); + if ( MimeConfigurationHelper::ClassIDsEqual( aIdentifier, aCLSID ) ) + return comphelper::getSomething_cast(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + + // compatibility hack for old versions: CLSID was used in wrong order (SvGlobalName order) + sal_Int32 nLength = aIdentifier.getLength(); + if ( nLength == 16 ) + { + for ( sal_Int32 n=8; n<16; n++ ) + if ( aIdentifier[n] != aCLSID[n] ) + return 0; + if ( aIdentifier[7] == aCLSID[6] && + aIdentifier[6] == aCLSID[7] && + aIdentifier[5] == aCLSID[4] && + aIdentifier[4] == aCLSID[5] && + aIdentifier[3] == aCLSID[0] && + aIdentifier[2] == aCLSID[1] && + aIdentifier[1] == aCLSID[2] && + aIdentifier[0] == aCLSID[3] ) + return reinterpret_cast<sal_Int64>(static_cast<IUnknown*>(m_pNativeImpl->m_pObj)); + } + } + catch ( const uno::Exception& ) + { + } + + return 0; +} + +sal_Bool SAL_CALL OleComponent::isModified() +{ + return m_bModified; +} + +void SAL_CALL OleComponent::setModified( sal_Bool bModified ) +{ + m_bModified = bModified; + + if ( bModified && m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XModifyListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + lang::EventObject aEvent( static_cast<util::XModifiable*>(this) ); + static_cast<util::XModifyListener*>(pIterator.next())->modified( aEvent ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } +} + +void SAL_CALL OleComponent::addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer = new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex ); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XModifyListener>::get(), xListener ); +} + +void SAL_CALL OleComponent::removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XModifyListener>::get(), + xListener ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olecomponent.hxx b/embeddedobj/source/msole/olecomponent.hxx new file mode 100644 index 000000000..5a96b64fc --- /dev/null +++ b/embeddedobj/source/msole/olecomponent.hxx @@ -0,0 +1,161 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/embed/VerbDescriptor.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <rtl/ref.hxx> + +namespace comphelper { + class OMultiTypeInterfaceContainerHelper2; +} + +class OleWrapperClientSite; +class OleWrapperAdviseSink; +class OleEmbeddedObject; +struct OleComponentNative_Impl; + +class OleComponent : public ::cppu::WeakImplHelper< css::util::XCloseable, css::lang::XComponent, + css::lang::XUnoTunnel, css::util::XModifiable, + css::datatransfer::XTransferable > +{ + ::osl::Mutex m_aMutex; + comphelper::OMultiTypeInterfaceContainerHelper2* m_pInterfaceContainer; + + bool m_bDisposed; + bool m_bModified; + OleComponentNative_Impl* m_pNativeImpl; + + OleEmbeddedObject* m_pUnoOleObject; + OleWrapperClientSite* m_pOleWrapClientSite; + OleWrapperAdviseSink* m_pImplAdviseSink; + + sal_Int32 m_nOLEMiscFlags; + sal_Int32 m_nAdvConn; + + css::uno::Sequence< css::embed::VerbDescriptor > m_aVerbList; + css::uno::Sequence< css::datatransfer::DataFlavor > m_aDataFlavors; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + bool m_bOleInitialized; + + // specifies whether the workaround for some rare embedded objects is activated ( f.e. AcrobatReader 7.0.8 object ) + // such objects report the dirty state wrongly sometimes and do not allow to store them any time + bool m_bWorkaroundActive; + + bool InitializeObject_Impl(); + + void CreateNewIStorage_Impl(); + void RetrieveObjectDataFlavors_Impl(); + void Dispose(); + + +public: + OleComponent( const css::uno::Reference< css::uno::XComponentContext >& xContext, + OleEmbeddedObject* pOleObj ); + + virtual ~OleComponent() override; + + OleComponent* createEmbeddedCopyOfLink(); + + void disconnectEmbeddedObject(); + + static css::awt::Size CalculateWithFactor( const css::awt::Size& aSize, + const css::awt::Size& aMultiplier, + const css::awt::Size& aDivisor ); + + css::awt::Size CalculateTheRealSize( const css::awt::Size& aContSize, bool bUpdate ); + + // ==== Initialization ================================================== + void LoadEmbeddedObject( const OUString& aTempURL ); + void CreateObjectFromClipboard(); + void CreateNewEmbeddedObject( const css::uno::Sequence< sal_Int8 >& aSeqCLSID ); + static void CreateObjectFromData( + const css::uno::Reference< css::datatransfer::XTransferable >& xTransfer ); + void CreateObjectFromFile( const OUString& aFileName ); + void CreateLinkFromFile( const OUString& aFileName ); + void InitEmbeddedCopyOfLink( rtl::Reference<OleComponent> const & pOleLinkComponent ); + + + void RunObject(); // switch OLE object to running state + void CloseObject(); // switch OLE object to loaded state + + css::uno::Sequence< css::embed::VerbDescriptor > GetVerbList(); + + void ExecuteVerb( sal_Int32 nVerbID ); + void SetHostName( const OUString& aEmbDocName ); + void SetExtent( const css::awt::Size& aVisAreaSize, sal_Int64 nAspect ); + + css::awt::Size GetExtent( sal_Int64 nAspect ); + css::awt::Size GetCachedExtent( sal_Int64 nAspect ); + css::awt::Size GetRecommendedExtent( sal_Int64 nAspect ); + + sal_Int64 GetMiscStatus( sal_Int64 nAspect ); + + css::uno::Sequence< sal_Int8 > GetCLSID(); + + bool IsWorkaroundActive() const { return m_bWorkaroundActive; } + bool IsDirty(); + + void StoreOwnTmpIfNecessary(); + + bool SaveObject_Impl(); + bool OnShowWindow_Impl( bool bShow ); + void OnViewChange_Impl( sal_uInt32 dwAspect ); + void OnClose_Impl(); + + // XCloseable + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + virtual void SAL_CALL addCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + virtual void SAL_CALL removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + // XTransferable + virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override; + virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override; + virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override; + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + virtual void SAL_CALL removeEventListener(const css::uno::Reference < css::lang::XEventListener >& aListener) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // XModifiable + virtual sal_Bool SAL_CALL isModified() override; + virtual void SAL_CALL setModified( sal_Bool bModified ) override; + virtual void SAL_CALL addModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference < css::util::XModifyListener >& xListener) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/oleembed.cxx b/embeddedobj/source/msole/oleembed.cxx new file mode 100644 index 000000000..6a3f4e926 --- /dev/null +++ b/embeddedobj/source/msole/oleembed.cxx @@ -0,0 +1,1179 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <oleembobj.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/UnreachableStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/NeedsRunningStateException.hpp> +#include <com/sun/star/embed/StateChangeInProgressException.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbedObjectCreator.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/propertyvalue.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + + +#include <targetstatecontrol.hxx> + +#include "ownview.hxx" + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; + +#ifdef _WIN32 + +void OleEmbeddedObject::SwitchComponentToRunningState_Impl() +{ + if ( !m_pOleComponent ) + { + throw embed::UnreachableStateException(); + } + try + { + m_pOleComponent->RunObject(); + } + catch( const embed::UnreachableStateException& ) + { + GetRidOfComponent(); + throw; + } + catch( const embed::WrongStateException& ) + { + GetRidOfComponent(); + throw; + } +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetReachableStatesList_Impl( + const uno::Sequence< embed::VerbDescriptor >& aVerbList ) +{ + uno::Sequence< sal_Int32 > aStates { embed::EmbedStates::LOADED, embed::EmbedStates::RUNNING }; + for ( embed::VerbDescriptor const & vd : aVerbList ) + if ( vd.VerbID == embed::EmbedVerbs::MS_OLEVERB_OPEN ) + { + aStates.realloc(3); + aStates.getArray()[2] = embed::EmbedStates::ACTIVE; + break; + } + + return aStates; +} + + +uno::Sequence< sal_Int32 > OleEmbeddedObject::GetIntermediateVerbsSequence_Impl( sal_Int32 nNewState ) +{ + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "Loaded object is switched to running state without verbs using!" ); + + // actually there will be only one verb + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + return { embed::EmbedVerbs::MS_OLEVERB_OPEN }; + } + + return uno::Sequence< sal_Int32 >(); +} +#endif + +void OleEmbeddedObject::MoveListeners() +{ + if ( !m_pInterfaceContainer ) + return; + + // move state change listeners + { + comphelper::OInterfaceContainerHelper2* pStateChangeContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pStateChangeContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pStateChangeContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addStateChangeListener( static_cast<embed::XStateChangeListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move event listeners + { + comphelper::OInterfaceContainerHelper2* pEventContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<document::XEventListener>::get()); + if ( pEventContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pEventContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addEventListener( static_cast<document::XEventListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + // move close listeners + { + comphelper::OInterfaceContainerHelper2* pCloseContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pCloseContainer != nullptr ) + { + if ( m_xWrappedObject.is() ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pCloseContainer ); + while ( pIterator.hasMoreElements() ) + { + try + { + m_xWrappedObject->addCloseListener( static_cast<util::XCloseListener*>(pIterator.next()) ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + } + } + + m_pInterfaceContainer.reset(); +} + + +uno::Reference< embed::XStorage > OleEmbeddedObject::CreateTemporarySubstorage( OUString& o_aStorageName ) +{ + uno::Reference< embed::XStorage > xResult; + + for ( sal_Int32 nInd = 0; nInd < 32000 && !xResult.is(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTOR" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + xResult = m_xParentStorage->openStorageElement( aName, embed::ElementModes::READWRITE ); + o_aStorageName = aName; + } + } + + if ( !xResult.is() ) + { + o_aStorageName.clear(); + throw uno::RuntimeException(); + } + + return xResult; +} + + +OUString OleEmbeddedObject::MoveToTemporarySubstream() +{ + OUString aResult; + for ( sal_Int32 nInd = 0; nInd < 32000 && aResult.isEmpty(); nInd++ ) + { + OUString aName = OUString::number( nInd ) + "TMPSTREAM" + m_aEntryName; + if ( !m_xParentStorage->hasByName( aName ) ) + { + m_xParentStorage->renameElement( m_aEntryName, aName ); + aResult = aName; + } + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException(); + + return aResult; +} + + +bool OleEmbeddedObject::TryToConvertToOOo( const uno::Reference< io::XStream >& xStream ) +{ + bool bResult = false; + + OUString aStorageName; + OUString aTmpStreamName; + sal_Int32 nStep = 0; + + if ( m_pOleComponent || m_bReadOnly ) + return false; + + try + { + changeState( embed::EmbedStates::LOADED ); + + // the stream must be seekable + uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + m_aFilterName = OwnView_Impl::GetFilterNameFromExtentionAndInStream( m_xContext, std::u16string_view(), xStream->getInputStream() ); + + if ( !m_aFilterName.isEmpty() + && ( m_aFilterName == "Calc MS Excel 2007 XML" || m_aFilterName == "Impress MS PowerPoint 2007 XML" || m_aFilterName == "MS Word 2007 XML" + || m_aFilterName == "MS Excel 97 Vorlage/Template" || m_aFilterName == "MS Word 97 Vorlage" ) ) + { + uno::Reference< container::XNameAccess > xFilterFactory( + m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext), + uno::UNO_QUERY_THROW ); + + OUString aDocServiceName; + uno::Any aFilterAnyData = xFilterFactory->getByName( m_aFilterName ); + uno::Sequence< beans::PropertyValue > aFilterData; + if ( aFilterAnyData >>= aFilterData ) + { + for ( beans::PropertyValue const & prop : std::as_const(aFilterData) ) + if ( prop.Name == "DocumentService" ) + prop.Value >>= aDocServiceName; + } + + if ( !aDocServiceName.isEmpty() ) + { + // create the model + uno::Sequence< uno::Any > aArguments{ uno::Any( + beans::NamedValue( "EmbeddedObject", uno::Any( true ))) }; + + uno::Reference< util::XCloseable > xDocument( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( aDocServiceName, aArguments, m_xContext ), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XLoadable > xLoadable( xDocument, uno::UNO_QUERY_THROW ); + uno::Reference< document::XStorageBasedDocument > xStorDoc( xDocument, uno::UNO_QUERY_THROW ); + + // let the model behave as embedded one + uno::Reference< frame::XModel > xModel( xDocument, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue( + "SetEmbedded", true) }; + xModel->attachResource( OUString(), aSeq ); + + // load the model from the stream + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue("HierarchicalDocumentName", m_aEntryName), + comphelper::makePropertyValue("ReadOnly", true), + comphelper::makePropertyValue("FilterName", m_aFilterName), + comphelper::makePropertyValue("URL", OUString( "private:stream" )), + comphelper::makePropertyValue("InputStream", xStream->getInputStream()) + }; + + xSeekable->seek( 0 ); + xLoadable->load( aArgs ); + + // the model is successfully loaded, create a new storage and store the model to the storage + uno::Reference< embed::XStorage > xTmpStorage = CreateTemporarySubstorage( aStorageName ); + xStorDoc->storeToStorage( xTmpStorage, uno::Sequence< beans::PropertyValue >() ); + xDocument->close( true ); + uno::Reference< beans::XPropertySet > xStorProps( xTmpStorage, uno::UNO_QUERY_THROW ); + OUString aMediaType; + xStorProps->getPropertyValue("MediaType") >>= aMediaType; + xTmpStorage->dispose(); + + // look for the related embedded object factory + ::comphelper::MimeConfigurationHelper aConfigHelper( m_xContext ); + OUString aEmbedFactory; + if ( !aMediaType.isEmpty() ) + aEmbedFactory = aConfigHelper.GetFactoryNameByMediaType( aMediaType ); + + if ( aEmbedFactory.isEmpty() ) + throw uno::RuntimeException(); + + uno::Reference< uno::XInterface > xFact = m_xContext->getServiceManager()->createInstanceWithContext( aEmbedFactory, m_xContext ); + + uno::Reference< embed::XEmbedObjectCreator > xEmbCreator( xFact, uno::UNO_QUERY_THROW ); + + // now the object should be adjusted to become the wrapper + nStep = 1; + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY_THROW ); + xComp->dispose(); + m_xObjectStream.clear(); + m_nObjectState = -1; + + nStep = 2; + aTmpStreamName = MoveToTemporarySubstream(); + + nStep = 3; + m_xParentStorage->renameElement( aStorageName, m_aEntryName ); + + nStep = 4; + m_xWrappedObject.set( xEmbCreator->createInstanceInitFromEntry( m_xParentStorage, m_aEntryName, uno::Sequence< beans::PropertyValue >(), uno::Sequence< beans::PropertyValue >() ), uno::UNO_QUERY_THROW ); + + // remember parent document name to show in the title bar + m_xWrappedObject->setContainerName( m_aContainerName ); + + bResult = true; // the change is no more revertable + try + { + m_xParentStorage->removeElement( aTmpStreamName ); + } + catch( const uno::Exception& ) + { + // the success of the removing is not so important + } + } + } + } + catch( const uno::Exception& ) + { + // repair the object if necessary + switch( nStep ) + { + case 4: + case 3: + if ( !aTmpStreamName.isEmpty() && aTmpStreamName != m_aEntryName ) + try + { + if ( m_xParentStorage->hasByName( m_aEntryName ) ) + m_xParentStorage->removeElement( m_aEntryName ); + m_xParentStorage->renameElement( aTmpStreamName, m_aEntryName ); + } + catch ( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + m_xParentStorage->dispose(); // ??? the storage has information loss, it should be closed without committing! + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + case 2: + try + { + m_xObjectStream = m_xParentStorage->openStreamElement( m_aEntryName, m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE ); + m_nObjectState = embed::EmbedStates::LOADED; + } + catch( const uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + try { + close( true ); + } catch( const uno::Exception& ) {} + + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // the repairing is not possible + } + [[fallthrough]]; + + case 1: + case 0: + if ( !aStorageName.isEmpty() ) + try { + m_xParentStorage->removeElement( aStorageName ); + } catch( const uno::Exception& ) { SAL_WARN( "embeddedobj.ole", "Can not remove temporary storage!" ); } + break; + } + } + + if ( bResult ) + { + // the conversion was done successfully, now the additional initializations should happen + + MoveListeners(); + m_xWrappedObject->setClientSite( m_xClientSite ); + if ( m_xParent.is() ) + { + uno::Reference< container::XChild > xChild( m_xWrappedObject, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( m_xParent ); + } + + } + + return bResult; +} + + +void SAL_CALL OleEmbeddedObject::changeState( sal_Int32 nNewState ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->changeState( nNewState ); + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // in case the object is already in requested state + if ( m_nObjectState == nNewState ) + return; + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nTargetState != -1 ) + { + // means that the object is currently trying to reach the target state + throw embed::StateChangeInProgressException( OUString(), + uno::Reference< uno::XInterface >(), + m_nTargetState ); + } + + TargetStateControl_Impl aControl( m_nTargetState, nNewState ); + + // TODO: additional verbs can be a problem, since nobody knows how the object + // will behave after activation + + sal_Int32 nOldState = m_nObjectState; + aGuard.clear(); + StateChangeNotification_Impl( true, nOldState, nNewState ); + aGuard.reset(); + + try + { + if ( nNewState == embed::EmbedStates::LOADED ) + { + // This means just closing of the current object + // If component can not be closed the object stays in loaded state + // and it holds reference to "incomplete" component + // If the object is switched to running state later + // the component will become "complete" + + // the loaded state must be set before, because of notifications! + m_nObjectState = nNewState; + + { + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + m_pOleComponent->CloseObject(); + } + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + } + else if ( nNewState == embed::EmbedStates::RUNNING || nNewState == embed::EmbedStates::ACTIVE ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state and a different state is specified + // as a new one the object first must be switched to running state. + + // the component can exist already in nonrunning state + // it can be created during loading to detect type of object + CreateOleComponentAndLoad_Impl( m_pOleComponent ); + + SwitchComponentToRunningState_Impl(); + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + aGuard.reset(); + + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) {} + aGuard.reset(); + } + + if ( m_nObjectState == nNewState ) + return; + } + + // so now the object is either switched from Active to Running state or viceversa + // the notification about object state change will be done asynchronously + if ( m_nObjectState == embed::EmbedStates::RUNNING && nNewState == embed::EmbedStates::ACTIVE ) + { + // execute OPEN verb, if object does not reach active state it is an object's problem + aGuard.clear(); + m_pOleComponent->ExecuteVerb( embed::EmbedVerbs::MS_OLEVERB_OPEN ); + aGuard.reset(); + + // some objects do not allow to set the size even in running state + if ( m_pOleComponent && m_bHasSizeToSet ) + { + aGuard.clear(); + try { + m_pOleComponent->SetExtent( m_aSizeToSet, m_nAspectToSet ); + m_bHasSizeToSet = false; + } + catch( uno::Exception& ) {} + aGuard.reset(); + } + + m_nObjectState = nNewState; + } + else if ( m_nObjectState == embed::EmbedStates::ACTIVE && nNewState == embed::EmbedStates::RUNNING ) + { + aGuard.clear(); + m_pOleComponent->CloseObject(); + m_pOleComponent->RunObject(); // Should not fail, the object already was active + aGuard.reset(); + m_nObjectState = nNewState; + } + else + { + throw embed::UnreachableStateException(); + } + } + else + throw embed::UnreachableStateException(); + } + catch( uno::Exception& ) + { + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + } + else +#endif + { + throw embed::UnreachableStateException(); + } +} + + +uno::Sequence< sal_Int32 > SAL_CALL OleEmbeddedObject::getReachableStates() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getReachableStates(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // the list of supported verbs can be retrieved only when object is in running state + throw embed::NeedsRunningStateException(); // TODO: + } + + // the list of states can only be guessed based on standard verbs, + // since there is no way to detect what additional verbs do + return GetReachableStatesList_Impl( m_pOleComponent->GetVerbList() ); + } + else +#endif + { + return uno::Sequence< sal_Int32 >(); + } +} + + +sal_Int32 SAL_CALL OleEmbeddedObject::getCurrentState() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getCurrentState(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: Shouldn't we ask object? ( I guess no ) + return m_nObjectState; +} + +namespace +{ + bool lcl_CopyStream(const uno::Reference<io::XInputStream>& xIn, const uno::Reference<io::XOutputStream>& xOut, sal_Int32 nMaxCopy = SAL_MAX_INT32) + { + if (nMaxCopy <= 0) + return false; + + const sal_Int32 nChunkSize = 4096; + uno::Sequence< sal_Int8 > aData(nChunkSize); + sal_Int32 nTotalRead = 0; + sal_Int32 nRead; + do + { + if (nTotalRead + aData.getLength() > nMaxCopy) + { + aData.realloc(nMaxCopy - nTotalRead); + } + nRead = xIn->readBytes(aData, aData.getLength()); + nTotalRead += nRead; + xOut->writeBytes(aData); + } while (nRead == nChunkSize && nTotalRead <= nMaxCopy); + return nTotalRead != 0; + } + + uno::Reference < io::XStream > lcl_GetExtractedStream( OUString& rUrl, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + uno::Reference <io::XTempFile> xNativeTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW); + uno::Reference < io::XStream > xStream(xNativeTempFile); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xObjectStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, xContext ), uno::UNO_QUERY_THROW ); + + //various stream names that can contain the real document contents for + //this object in a straightforward direct way + static const std::u16string_view aStreamNames[] = + { + u"CONTENTS", + u"Package", + u"EmbeddedOdf", + u"WordDocument", + u"Workbook", + u"PowerPoint Document" + }; + + bool bCopied = false; + for (size_t i = 0; i < SAL_N_ELEMENTS(aStreamNames) && !bCopied; ++i) + { + uno::Reference<io::XStream> xEmbeddedFile; + try + { + xNameContainer->getByName(OUString(aStreamNames[i])) >>= xEmbeddedFile; + } + catch (const container::NoSuchElementException&) + { + // ignore + } + bCopied = xEmbeddedFile.is() && lcl_CopyStream(xEmbeddedFile->getInputStream(), xStream->getOutputStream()); + } + + if (!bCopied) + { + uno::Reference< io::XStream > xOle10Native; + try + { + xNameContainer->getByName("\1Ole10Native") >>= xOle10Native; + } + catch (container::NoSuchElementException const&) + { + // ignore + } + if (xOle10Native.is()) + { + const uno::Reference<io::XInputStream> xIn = xOle10Native->getInputStream(); + xIn->skipBytes(4); //size of the entire stream minus 4 bytes + xIn->skipBytes(2); //word that represent the directory type + uno::Sequence< sal_Int8 > aData(1); + sal_Int32 nRead; + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // file name plus extension of the attachment null terminated + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Fully Qualified File name with extension + xIn->skipBytes(1); //single byte + xIn->skipBytes(1); //single byte + xIn->skipBytes(2); //Word that represent the directory type + xIn->skipBytes(4); //len of string + do + { + nRead = xIn->readBytes(aData, 1); + } while (nRead == 1 && aData[0] != 0); // Actual string representing the file path + uno::Sequence< sal_Int8 > aLenData(4); + xIn->readBytes(aLenData, 4); //len of attachment + sal_uInt32 nLen = static_cast<sal_uInt32>( + (aLenData[0] & 0xFF) | + ((aLenData[1] & 0xFF) << 8) | + ((aLenData[2] & 0xFF) << 16) | + ((aLenData[3] & 0xFF) << 24)); + + bCopied = lcl_CopyStream(xIn, xStream->getOutputStream(), nLen); + } + } + + uno::Reference< io::XSeekable > xSeekableStor(xObjectStream, uno::UNO_QUERY); + if (xSeekableStor.is()) + xSeekableStor->seek(0); + + if (!bCopied) + bCopied = lcl_CopyStream(xObjectStream->getInputStream(), xStream->getOutputStream()); + + if (bCopied) + { + xNativeTempFile->setRemoveFile(false); + rUrl = xNativeTempFile->getUri(); + + xNativeTempFile.clear(); + + uno::Reference < ucb::XSimpleFileAccess3 > xSimpleFileAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xSimpleFileAccess->setReadOnly(rUrl, true); + } + else + { + xNativeTempFile->setRemoveFile(true); + } + + return xStream; + } + + //Dump the objects content to a tempfile, just the "CONTENTS" stream if + //there is one for non-compound documents, otherwise the whole content. + //On success a file is returned which must be removed by the caller + OUString lcl_ExtractObject(const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream) + { + OUString sUrl; + + // the solution is only active for Unix systems +#ifndef _WIN32 + lcl_GetExtractedStream(sUrl, xContext, xObjectStream); +#else + (void) xContext; + (void) xObjectStream; +#endif + return sUrl; + } + + uno::Reference < io::XStream > lcl_ExtractObjectStream( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xObjectStream ) + { + OUString sUrl; + return lcl_GetExtractedStream( sUrl, xContext, xObjectStream ); + } +} + + +void SAL_CALL OleEmbeddedObject::doVerb( sal_Int32 nVerbID ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->doVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); // open content in the window not in-place + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + if ( m_pOleComponent ) + { + sal_Int32 nOldState = m_nObjectState; + + // TODO/LATER detect target state here and do a notification + // StateChangeNotification_Impl( sal_True, nOldState, nNewState ); + if ( m_nObjectState == embed::EmbedStates::LOADED ) + { + // if the target object is in loaded state + // it must be switched to running state to execute verb + aGuard.clear(); + changeState( embed::EmbedStates::RUNNING ); + aGuard.reset(); + } + + try { + if ( !m_pOleComponent ) + throw uno::RuntimeException(); + + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.StartControlExecution(); + + + m_pOleComponent->ExecuteVerb( nVerbID ); + m_pOleComponent->SetHostName( m_aContainerName ); + + // ==== the STAMPIT related solution ============================= + bool bModifiedOnExecution = m_aVerbExecutionController.EndControlExecution_WasModified(); + + // this workaround is implemented for STAMPIT object + // if object was modified during verb execution it is saved here + if ( bModifiedOnExecution && m_pOleComponent->IsDirty() ) + SaveObject_Impl(); + + } + catch( uno::Exception& ) + { + // ==== the STAMPIT related solution ============================= + m_aVerbExecutionController.EndControlExecution_WasModified(); + + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + throw; + } + + } + else +#endif + { + if ( nVerbID != -9 ) + { + + throw embed::UnreachableStateException(); + } + + // the workaround verb to show the object in case no server is available + + // if it is possible, the object will be converted to OOo format + if ( !m_bTriedConversion ) + { + m_bTriedConversion = true; + if ( TryToConvertToOOo( m_xObjectStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName != "Text" ) + { + try { + uno::Reference< io::XSeekable > xSeekable( m_xObjectStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + xSeekable->seek( 0 ); + + m_xOwnView = new OwnView_Impl( m_xContext, m_xObjectStream->getInputStream() ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::doVerb: -9 fallback path:"); + } + } + + // it may be the OLE Storage, try to extract stream + if ( !m_xOwnView.is() && m_xObjectStream.is() && m_aFilterName == "Text" ) + { + uno::Reference< io::XStream > xStream = lcl_ExtractObjectStream( m_xContext, m_xObjectStream ); + + if ( TryToConvertToOOo( xStream ) ) + { + changeState( embed::EmbedStates::ACTIVE ); + return; + } + } + + if (!m_xOwnView.is() || !m_xOwnView->Open()) + { + //Make a RO copy and see if the OS can find something to at + //least display the content for us + if (m_aTempDumpURL.isEmpty()) + m_aTempDumpURL = lcl_ExtractObject(m_xContext, m_xObjectStream); + + if (m_aTempDumpURL.isEmpty()) + throw embed::UnreachableStateException(); + + uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute( + css::system::SystemShellExecute::create( m_xContext ) ); + xSystemShellExecute->execute(m_aTempDumpURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + + } + + } +} + + +uno::Sequence< embed::VerbDescriptor > SAL_CALL OleEmbeddedObject::getSupportedVerbs() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getSupportedVerbs(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); +#ifdef _WIN32 + if ( m_pOleComponent ) + { + // registry could be used in this case + // if ( m_nObjectState == embed::EmbedStates::LOADED ) + // { + // // the list of supported verbs can be retrieved only when object is in running state + // throw embed::NeedsRunningStateException(); // TODO: + // } + + return m_pOleComponent->GetVerbList(); + } + else +#endif + { + // tdf#140079 Claim support for the OleEmbeddedObject::doVerb -9 fallback. + // So in SfxViewFrame::GetState_Impl in case SID_OBJECT hasVerbs is not + // empty, so that the doVerb attempt with -9 fallback is attempted + uno::Sequence<embed::VerbDescriptor> aRet(1); + aRet.getArray()[0].VerbID = -9; + return aRet; + } +} + + +void SAL_CALL OleEmbeddedObject::setClientSite( + const uno::Reference< embed::XEmbeddedClient >& xClient ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClientSite( xClient ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_xClientSite != xClient) + { + if ( m_nObjectState != embed::EmbedStates::LOADED && m_nObjectState != embed::EmbedStates::RUNNING ) + throw embed::WrongStateException( + "The client site can not be set currently!", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xClientSite = xClient; + } +} + + +uno::Reference< embed::XEmbeddedClient > SAL_CALL OleEmbeddedObject::getClientSite() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClientSite(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_xClientSite; +} + + +void SAL_CALL OleEmbeddedObject::update() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->update(); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nUpdateMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE ) + { + // TODO: update view representation + } + else + { + // the object must be up to date + SAL_WARN_IF( m_nUpdateMode != embed::EmbedUpdateModes::ALWAYS_UPDATE, "embeddedobj.ole", "Unknown update mode!" ); + } +} + + +void SAL_CALL OleEmbeddedObject::setUpdateMode( sal_Int32 nMode ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setUpdateMode( nMode ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object has no persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( nMode == embed::EmbedUpdateModes::ALWAYS_UPDATE + || nMode == embed::EmbedUpdateModes::EXPLICIT_UPDATE, + "Unknown update mode!" ); + m_nUpdateMode = nMode; +} + + +sal_Int64 SAL_CALL OleEmbeddedObject::getStatus( sal_Int64 + nAspect +) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getStatus( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object must be in running state!", + static_cast< ::cppu::OWeakObject* >(this) ); + + sal_Int64 nResult = 0; + +#ifdef _WIN32 + if ( m_bGotStatus && m_nStatusAspect == nAspect ) + nResult = m_nStatus; + else if ( m_pOleComponent ) + { + + m_nStatus = m_pOleComponent->GetMiscStatus( nAspect ); + m_nStatusAspect = nAspect; + m_bGotStatus = true; + nResult = m_nStatus; + } +#endif + + // this implementation needs size to be provided after object loading/creating to work in optimal way + return ( nResult | embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD ); +} + + +void SAL_CALL OleEmbeddedObject::setContainerName( const OUString& sName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setContainerName( sName ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + m_aContainerName = sName; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olemisc.cxx b/embeddedobj/source/msole/olemisc.cxx new file mode 100644 index 000000000..e1007fbb1 --- /dev/null +++ b/embeddedobj/source/msole/olemisc.cxx @@ -0,0 +1,700 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <comphelper/multicontainer2.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> + +#include <oleembobj.hxx> +#include "olepersist.hxx" + +#include "ownview.hxx" + +#include "olecomponent.hxx" + +using namespace ::com::sun::star; + + +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode ( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_aClassID( aClassID ) +, m_aClassName( aClassName ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} + + +// In case of loading from persistent entry the classID of the object +// will be retrieved from the entry, during construction it is unknown +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext, bool bLink ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( bLink ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( false ) +, m_bTriedConversion( false ) +{ +} +#ifdef _WIN32 + +// this constructor let object be initialized from clipboard +OleEmbeddedObject::OleEmbeddedObject( const uno::Reference< uno::XComponentContext >& xContext ) +: m_bReadOnly( false ) +, m_bDisposed( false ) +, m_nObjectState( -1 ) +, m_nTargetState( -1 ) +, m_nUpdateMode( embed::EmbedUpdateModes::ALWAYS_UPDATE ) +, m_xContext( xContext ) +, m_bWaitSaveCompleted( false ) +, m_bNewVisReplInStream( true ) +, m_bStoreLoaded( false ) +, m_bVisReplInitialized( false ) +, m_bVisReplInStream( false ) +, m_bStoreVisRepl( false ) +, m_bIsLink( false ) +, m_bHasCachedSize( false ) +, m_nCachedAspect( 0 ) +, m_bHasSizeToSet( false ) +, m_nAspectToSet( 0 ) +, m_bGotStatus( false ) +, m_nStatus( 0 ) +, m_nStatusAspect( 0 ) +, m_bFromClipboard( true ) +, m_bTriedConversion( false ) +{ +} +#endif + +OleEmbeddedObject::~OleEmbeddedObject() +{ + OSL_ENSURE( !m_pInterfaceContainer && !m_pOleComponent && !m_xObjectStream.is(), + "The object is not closed! DISASTER is possible!" ); + + if ( m_pOleComponent || m_pInterfaceContainer || m_xObjectStream.is() ) + { + // the component must be cleaned during closing + osl_atomic_increment(&m_refCount); // to avoid crash + try { + Dispose(); + } catch( const uno::Exception& ) {} + } + + if ( !m_aTempURL.isEmpty() ) + KillFile_Impl( m_aTempURL, m_xContext ); + + if ( !m_aTempDumpURL.isEmpty() ) + KillFile_Impl( m_aTempDumpURL, m_xContext ); +} + + +void OleEmbeddedObject::MakeEventListenerNotification_Impl( const OUString& aEventName ) +{ + if ( !m_pInterfaceContainer ) + return; + + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( + cppu::UnoType<document::XEventListener>::get()); + if ( pContainer == nullptr ) + return; + + document::EventObject aEvent( static_cast< ::cppu::OWeakObject* >( this ), aEventName ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<document::XEventListener*>(pIterator.next())->notifyEvent( aEvent ); + } + catch( const uno::RuntimeException& ) + { + } + } +} +#ifdef _WIN32 + +void OleEmbeddedObject::StateChangeNotification_Impl( bool bBeforeChange, sal_Int32 nOldState, sal_Int32 nNewState ) +{ + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<embed::XStateChangeListener>::get()); + if ( pContainer != nullptr ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + + while (pIterator.hasMoreElements()) + { + if ( bBeforeChange ) + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->changingState( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // even if the listener complains ignore it for now + } + } + else + { + try + { + static_cast<embed::XStateChangeListener*>(pIterator.next())->stateChanged( aSource, nOldState, nNewState ); + } + catch( const uno::Exception& ) + { + // if anything happened it is problem of listener, ignore it + } + } + } + } + } +} +#endif + +void OleEmbeddedObject::GetRidOfComponent() +{ +#ifdef _WIN32 + if ( m_pOleComponent ) + { + if ( m_nObjectState != -1 && m_nObjectState != embed::EmbedStates::LOADED ) + SaveObject_Impl(); + + m_pOleComponent->removeCloseListener( m_xClosePreventer ); + try + { + m_pOleComponent->close( false ); + } + catch( const uno::Exception& ) + { + // TODO: there should be a special listener to wait for component closing + // and to notify object, may be object itself can be such a listener + m_pOleComponent->addCloseListener( m_xClosePreventer ); + throw; + } + + m_pOleComponent->disconnectEmbeddedObject(); + m_pOleComponent.clear(); + } +#endif +} + + +void OleEmbeddedObject::Dispose() +{ + if ( m_pInterfaceContainer ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + m_pInterfaceContainer.reset(); + } + + if ( m_xOwnView.is() ) + { + m_xOwnView->Close(); + m_xOwnView.clear(); + } + + if ( m_pOleComponent ) + try { + GetRidOfComponent(); + } catch( const uno::Exception& ) + { + m_bDisposed = true; + throw; // TODO: there should be a special listener that will close object when + // component is finally closed + } + + if ( m_xObjectStream.is() ) + { + uno::Reference< lang::XComponent > xComp( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( xComp.is(), "Storage stream doesn't support XComponent!" ); + + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) {} + } + m_xObjectStream.clear(); + } + + m_xParentStorage.clear(); + + m_bDisposed = true; +} + + +uno::Sequence< sal_Int8 > SAL_CALL OleEmbeddedObject::getClassID() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassID(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassID; +} + + +OUString SAL_CALL OleEmbeddedObject::getClassName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getClassName(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_aClassName; +} + + +void SAL_CALL OleEmbeddedObject::setClassInfo( + const uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setClassInfo( aClassID, aClassName ); + return; + } + // end wrapping related part ==================== + + // the object class info can not be changed explicitly + throw lang::NoSupportException(); //TODO: +} + + +uno::Reference< util::XCloseable > SAL_CALL OleEmbeddedObject::getComponent() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getComponent(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) // || m_nObjectState == embed::EmbedStates::LOADED ) + { + // the object is still not running + throw uno::RuntimeException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + +#if defined(_WIN32) + if (m_pOleComponent.is()) + { + return uno::Reference< util::XCloseable >( m_pOleComponent ); + } +#endif + + assert(!m_pOleComponent.is()); + // TODO/LATER: Is it correct??? + return uno::Reference< util::XCloseable >(); + // throw uno::RuntimeException(); // TODO +} + + +void SAL_CALL OleEmbeddedObject::addStateChangeListener( const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->addStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeStateChangeListener( + const uno::Reference< embed::XStateChangeListener >& xListener ) +{ + // begin wrapping related part ==================== + if ( m_xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + m_xWrappedObject->removeStateChangeListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<embed::XStateChangeListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::close( sal_Bool bDeliverOwnership ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->close( bDeliverOwnership ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >( this ) ); + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >( this ) ); + + if ( m_pInterfaceContainer ) + { + comphelper::OInterfaceContainerHelper2* pContainer = + m_pInterfaceContainer->getContainer( cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pIterator.next())->queryClosing( aSource, bDeliverOwnership ); + } + catch( const uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + pContainer = m_pInterfaceContainer->getContainer( + cppu::UnoType<util::XCloseListener>::get()); + if ( pContainer != nullptr ) + { + comphelper::OInterfaceIteratorHelper2 pCloseIterator(*pContainer); + while (pCloseIterator.hasMoreElements()) + { + try + { + static_cast<util::XCloseListener*>(pCloseIterator.next())->notifyClosing( aSource ); + } + catch( const uno::RuntimeException& ) + { + pCloseIterator.remove(); + } + } + } + } + + Dispose(); +} + + +void SAL_CALL OleEmbeddedObject::addCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeCloseListener( const uno::Reference< util::XCloseListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeCloseListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<util::XCloseListener>::get(), + xListener ); +} + + +void SAL_CALL OleEmbeddedObject::addEventListener( const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->addEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset(new comphelper::OMultiTypeInterfaceContainerHelper2( m_aMutex )); + + m_pInterfaceContainer->addInterface( cppu::UnoType<document::XEventListener>::get(), xListener ); +} + + +void SAL_CALL OleEmbeddedObject::removeEventListener( + const uno::Reference< document::XEventListener >& xListener ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->removeEventListener( xListener ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( cppu::UnoType<document::XEventListener>::get(), + xListener ); +} + +// XInplaceObject ( wrapper related implementation ) + +void SAL_CALL OleEmbeddedObject::setObjectRectangles( const awt::Rectangle& aPosRect, + const awt::Rectangle& aClipRect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setObjectRectangles( aPosRect, aClipRect ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::enableModeless( sal_Bool bEnable ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->enableModeless( bEnable ); + return; + } + // end wrapping related part ==================== + + throw embed::WrongStateException(); +} + + +void SAL_CALL OleEmbeddedObject::translateAccelerators( + const uno::Sequence< awt::KeyEvent >& aKeys ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XInplaceObject > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->translateAccelerators( aKeys ); + return; + } + // end wrapping related part ==================== + +} + +// XChild + +css::uno::Reference< css::uno::XInterface > SAL_CALL OleEmbeddedObject::getParent() +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getParent(); + } + // end wrapping related part ==================== + + return m_xParent; +} + + +void SAL_CALL OleEmbeddedObject::setParent( const css::uno::Reference< css::uno::XInterface >& xParent ) +{ + // begin wrapping related part ==================== + uno::Reference< container::XChild > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setParent( xParent ); + return; + } + // end wrapping related part ==================== + + m_xParent = xParent; +} + +void OleEmbeddedObject::setStream(const css::uno::Reference<css::io::XStream>& xStream) +{ + m_xObjectStream = xStream; +} + +css::uno::Reference<css::io::XStream> OleEmbeddedObject::getStream() +{ + return m_xObjectStream; +} + +void OleEmbeddedObject::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + if (!rArguments.hasElements()) + return; + + comphelper::SequenceAsHashMap aValues(rArguments[0]); + auto it = aValues.find("StreamReadOnly"); + if (it != aValues.end()) + it->second >>= m_bStreamReadOnly; +} + +OUString SAL_CALL OleEmbeddedObject::getImplementationName() +{ + return "com.sun.star.comp.embed.OleEmbeddedObject"; +} + +sal_Bool SAL_CALL OleEmbeddedObject::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence<OUString> SAL_CALL OleEmbeddedObject::getSupportedServiceNames() +{ + return { "com.sun.star.comp.embed.OleEmbeddedObject" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.cxx b/embeddedobj/source/msole/olepersist.cxx new file mode 100644 index 000000000..86403f41b --- /dev/null +++ b/embeddedobj/source/msole/olepersist.cxx @@ -0,0 +1,2019 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <oleembobj.hxx> +#include "olepersist.hxx" +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/EmbedUpdateModes.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/IOException.hpp> + +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> + +#include <closepreventer.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + + +bool KillFile_Impl( const OUString& aURL, const uno::Reference< uno::XComponentContext >& xContext ) +{ + if ( !xContext.is() ) + return false; + + bool bRet = false; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + xAccess->kill( aURL ); + bRet = true; + } + catch( const uno::Exception& ) + { + } + + return bRet; +} + + +OUString GetNewTempFileURL_Impl( const uno::Reference< uno::XComponentContext >& xContext ) +{ + SAL_WARN_IF( !xContext.is(), "embeddedobj.ole", "No factory is provided!" ); + + OUString aResult; + + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + try { + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch ( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw uno::RuntimeException("Cannot create tempfile."); + + return aResult; +} + + +OUString GetNewFilledTempFile_Impl( const uno::Reference< io::XInputStream >& xInStream, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + OSL_ENSURE( xInStream.is() && xContext.is(), "Wrong parameters are provided!" ); + + OUString aResult = GetNewTempFileURL_Impl( xContext ); + + if ( !aResult.isEmpty() ) + { + try { + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( xContext ) ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempAccess->openFileWrite( aResult ); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + // copy stream contents to the file + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + } + catch( const packages::WrongPasswordException& ) + { + KillFile_Impl( aResult, xContext ); + throw io::IOException(); //TODO: + } + catch( const io::IOException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::RuntimeException& ) + { + KillFile_Impl( aResult, xContext ); + throw; + } + catch( const uno::Exception& ) + { + KillFile_Impl( aResult, xContext ); + aResult.clear(); + } + } + + return aResult; +} +#ifdef _WIN32 +/// @throws io::IOException +/// @throws uno::RuntimeException +static OUString GetNewFilledTempFile_Impl( const uno::Reference< embed::XOptimizedStorage >& xParentStorage, const OUString& aEntryName, const uno::Reference< uno::XComponentContext >& xContext ) +{ + OUString aResult; + + try + { + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(xContext), + uno::UNO_SET_THROW ); + + xParentStorage->copyStreamElementData( aEntryName, xTempFile ); + + xTempFile->setRemoveFile( false ); + aResult = xTempFile->getUri(); + } + catch( const uno::RuntimeException& ) + { + throw; + } + catch( const uno::Exception& ) + { + } + + if ( aResult.isEmpty() ) + throw io::IOException(); + + return aResult; +} + + +static void SetStreamMediaType_Impl( const uno::Reference< io::XStream >& xStream, const OUString& aMediaType ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("MediaType", uno::Any( aMediaType ) ); +} +#endif + +static void LetCommonStoragePassBeUsed_Impl( const uno::Reference< io::XStream >& xStream ) +{ + uno::Reference< beans::XPropertySet > xPropSet( xStream, uno::UNO_QUERY_THROW ); + xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption", + uno::Any( true ) ); +} +#ifdef _WIN32 + +void VerbExecutionController::StartControlExecution() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + // the class is used to detect STAMPIT object, that can never be active + if ( !m_bVerbExecutionInProgress && !m_bWasEverActive ) + { + m_bVerbExecutionInProgress = true; + m_nVerbExecutionThreadIdentifier = osl::Thread::getCurrentIdentifier(); + m_bChangedOnVerbExecution = false; + } +} + + +bool VerbExecutionController::EndControlExecution_WasModified() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + bool bResult = false; + if ( m_bVerbExecutionInProgress && m_nVerbExecutionThreadIdentifier == osl::Thread::getCurrentIdentifier() ) + { + bResult = m_bChangedOnVerbExecution; + m_bVerbExecutionInProgress = false; + } + + return bResult; +} + + +void VerbExecutionController::ModificationNotificationIsDone() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + + if ( m_bVerbExecutionInProgress && osl::Thread::getCurrentIdentifier() == m_nVerbExecutionThreadIdentifier ) + m_bChangedOnVerbExecution = true; +} +#endif + +void VerbExecutionController::LockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock < SAL_MAX_INT32 ) + m_nNotificationLock++; +} + + +void VerbExecutionController::UnlockNotification() +{ + osl::MutexGuard aGuard( m_aVerbExecutionMutex ); + if ( m_nNotificationLock > 0 ) + m_nNotificationLock--; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::GetNewFilledTempStream_Impl( const uno::Reference< io::XInputStream >& xInStream ) +{ + SAL_WARN_IF( !xInStream.is(), "embeddedobj.ole", "Wrong parameter is provided!" ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOutStream ); + xTempOutStream->flush(); + return xTempFile; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToGetAcceptableFormat_Impl( const uno::Reference< io::XStream >& xStream ) +{ + // TODO/LATER: Actually this should be done by a centralized component ( may be a graphical filter ) + if ( !m_xContext.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XInputStream > xInStream = xStream->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XSeekable > xSeek( xStream, uno::UNO_QUERY_THROW ); + xSeek->seek( 0 ); + + uno::Sequence< sal_Int8 > aData( 8 ); + sal_Int32 nRead = xInStream->readBytes( aData, 8 ); + xSeek->seek( 0 ); + + if ( ( nRead >= 2 && aData[0] == 'B' && aData[1] == 'M' ) + || ( nRead >= 4 && aData[0] == 1 && aData[1] == 0 && aData[2] == 9 && aData[3] == 0 ) ) + { + // it should be a bitmap or a Metafile + return xStream; + } + + + sal_uInt32 nHeaderOffset = 0; + if ( ( nRead >= 8 && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 ) + && ( aData[4] == 2 || aData[4] == 3 || aData[4] == 14 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + nHeaderOffset = 40; + xSeek->seek( 8 ); + + // TargetDevice might be used in future, currently the cache has specified NULL + uno::Sequence< sal_Int8 > aHeadData( 4 ); + nRead = xInStream->readBytes( aHeadData, 4 ); + sal_uInt32 nLen = 0; + if ( nRead == 4 && aHeadData.getLength() == 4 ) + nLen = ( ( ( static_cast<sal_uInt32>(aHeadData[3]) * 0x100 + static_cast<sal_uInt32>(aHeadData[2]) ) * 0x100 ) + static_cast<sal_uInt32>(aHeadData[1]) ) * 0x100 + static_cast<sal_uInt32>(aHeadData[0]); + if ( nLen > 4 ) + { + xInStream->skipBytes( nLen - 4 ); + nHeaderOffset += nLen - 4; + } + + } + else if ( nRead > 4 ) + { + // check whether the first bytes represent the size + sal_uInt32 nSize = 0; + for ( sal_Int32 nInd = 3; nInd >= 0; nInd-- ) + nSize = ( nSize << 8 ) + static_cast<sal_uInt8>(aData[nInd]); + + if ( nSize == xSeek->getLength() - 4 ) + nHeaderOffset = 4; + } + + if ( nHeaderOffset ) + { + // this is either a bitmap or a metafile clipboard format, retrieve the pure stream + uno::Reference < io::XStream > xResult( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + uno::Reference < io::XSeekable > xResultSeek( xResult, uno::UNO_QUERY_THROW ); + uno::Reference < io::XOutputStream > xResultOut = xResult->getOutputStream(); + uno::Reference < io::XInputStream > xResultIn = xResult->getInputStream(); + if ( !xResultOut.is() || !xResultIn.is() ) + throw uno::RuntimeException(); + + xSeek->seek( nHeaderOffset ); // header size for these formats + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xResultOut ); + xResultOut->closeOutput(); + xResultSeek->seek( 0 ); + xSeek->seek( 0 ); + + return xResult; + } + + return uno::Reference< io::XStream >(); +} + + +void OleEmbeddedObject::InsertVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream, + const uno::Reference< io::XStream >& xCachedVisualRepresentation ) +{ + OSL_ENSURE( xTargetStream.is() && xCachedVisualRepresentation.is(), "Invalid arguments!" ); + + if ( !xTargetStream.is() || !xCachedVisualRepresentation.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY_THROW ); + xCachedSeek->seek( 0 ); + + uno::Reference < io::XStream > xTempFile( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW ); + uno::Reference< io::XOutputStream > xTempOutStream = xTempFile->getOutputStream(); + if ( !xTempOutStream.is() ) + throw io::IOException(); // TODO: + + // the OlePres stream must have additional header + // TODO/LATER: might need to be extended in future (actually makes sense only for SO7 format) + uno::Reference< io::XInputStream > xInCacheStream = xCachedVisualRepresentation->getInputStream(); + if ( !xInCacheStream.is() ) + throw uno::RuntimeException(); + + // write 0xFFFFFFFF at the beginning + uno::Sequence< sal_Int8 > aData( 4 ); + auto pData = aData.getArray(); + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + + xTempOutStream->writeBytes( aData ); + + // write clipboard format + uno::Sequence< sal_Int8 > aSigData( 2 ); + xInCacheStream->readBytes( aSigData, 2 ); + if ( aSigData.getLength() < 2 ) + throw io::IOException(); + + if ( aSigData[0] == 'B' && aSigData[1] == 'M' ) + { + // it's a bitmap + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + else + { + // treat it as a metafile + pData[0] = 0x03; pData[1] = 0; pData[2] = 0; pData[3] = 0; + } + xTempOutStream->writeBytes( aData ); + + // write job related information + pData[0] = 0x04; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write aspect + pData[0] = 0x01; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write l-index + * reinterpret_cast<sal_uInt32*>(pData) = 0xFFFFFFFF; + xTempOutStream->writeBytes( aData ); + + // write adv. flags + pData[0] = 0x02; pData[1] = 0; pData[2] = 0; pData[3] = 0; + xTempOutStream->writeBytes( aData ); + + // write compression + * reinterpret_cast<sal_uInt32*>(pData) = 0x0; + xTempOutStream->writeBytes( aData ); + + // get the size + awt::Size aSize = getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + sal_Int32 nIndex = 0; + + // write width + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Width % 0x100 ); + aSize.Width /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write height + for ( nIndex = 0; nIndex < 4; nIndex++ ) + { + pData[nIndex] = static_cast<sal_Int8>( aSize.Height % 0x100 ); + aSize.Height /= 0x100; + } + xTempOutStream->writeBytes( aData ); + + // write garbage, it will be overwritten by the size + xTempOutStream->writeBytes( aData ); + + // write first bytes that was used to detect the type + xTempOutStream->writeBytes( aSigData ); + + // write the rest of the stream + ::comphelper::OStorageHelper::CopyInputToOutput( xInCacheStream, xTempOutStream ); + + // write the size of the stream + sal_Int64 nLength = xTempSeek->getLength() - 40; + if ( nLength < 0 || nLength >= 0xFFFFFFFF ) + { + SAL_WARN( "embeddedobj.ole", "Length is not acceptable!" ); + return; + } + for ( sal_Int32 nInd = 0; nInd < 4; nInd++ ) + { + pData[nInd] = static_cast<sal_Int8>( static_cast<sal_uInt64>(nLength) % 0x100 ); + nLength /= 0x100; + } + xTempSeek->seek( 36 ); + xTempOutStream->writeBytes( aData ); + + xTempOutStream->flush(); + + xTempSeek->seek( 0 ); + if ( xCachedSeek.is() ) + xCachedSeek->seek( 0 ); + + // insert the result file as replacement image + OUString aCacheName = "\002OlePres000"; + if ( xNameContainer->hasByName( aCacheName ) ) + xNameContainer->replaceByName( aCacheName, uno::Any( xTempFile ) ); + else + xNameContainer->insertByName( aCacheName, uno::Any( xTempFile ) ); + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::RemoveVisualCache_Impl( const uno::Reference< io::XStream >& xTargetStream ) +{ + OSL_ENSURE( xTargetStream.is(), "Invalid argument!" ); + if ( !xTargetStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xTargetStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + if ( xNameContainer->hasByName( aStreamName ) ) + xNameContainer->removeByName( aStreamName ); + } + + uno::Reference< embed::XTransactedObject > xTransacted( xNameContainer, uno::UNO_QUERY_THROW ); + xTransacted->commit(); +} + + +void OleEmbeddedObject::SetVisReplInStream( bool bExists ) +{ + m_bVisReplInitialized = true; + m_bVisReplInStream = bExists; +} + + +bool OleEmbeddedObject::HasVisReplInStream() +{ + if ( !m_bVisReplInitialized ) + { + if ( m_xCachedVisualRepresentation.is() ) + SetVisReplInStream( true ); + else + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::HasVisualReplInStream, analyzing" ); + + uno::Reference< io::XInputStream > xStream; + + OSL_ENSURE( !m_pOleComponent || !m_aTempURL.isEmpty(), "The temporary file must exist if there is a component!" ); + if ( !m_aTempURL.isEmpty() ) + { + try + { + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + xStream = xTempAccess->openFileRead( m_aTempURL ); + } + catch( const uno::Exception& ) + {} + } + + if ( !xStream.is() ) + xStream = m_xObjectStream->getInputStream(); + + if ( xStream.is() ) + { + bool bExists = false; + + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + uno::Reference< container::XNameContainer > xNameContainer( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10 && !bExists; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + try + { + bExists = xNameContainer->hasByName( aStreamName ); + } + catch( const uno::Exception& ) + {} + } + } + + SetVisReplInStream( bExists ); + } + } + } + + return m_bVisReplInStream; +} + + +uno::Reference< io::XStream > OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation_Impl( + const uno::Reference< io::XStream >& xStream, + bool bAllowToRepair50 ) + noexcept +{ + uno::Reference< io::XStream > xResult; + + if ( xStream.is() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::TryToRetrieveCachedVisualRepresentation, retrieving" ); + + uno::Reference< container::XNameContainer > xNameContainer; + uno::Sequence< uno::Any > aArgs{ uno::Any(xStream), + uno::Any(true) }; // do not create copy + try + { + xNameContainer.set( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY ); + } + catch( const uno::Exception& ) + {} + + if ( xNameContainer.is() ) + { + for ( sal_uInt8 nInd = 0; nInd < 10; nInd++ ) + { + OUString aStreamName = "\002OlePres00" + OUString::number( nInd ); + uno::Reference< io::XStream > xCachedCopyStream; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + + if ( nInd == 0 ) + { + // to be compatible with the old versions Ole10Native is checked after OlePress000 + aStreamName = "\001Ole10Native"; + try + { + if ( ( xNameContainer->getByName( aStreamName ) >>= xCachedCopyStream ) && xCachedCopyStream.is() ) + { + xResult = TryToGetAcceptableFormat_Impl( xCachedCopyStream ); + if ( xResult.is() ) + break; + } + } + catch( const uno::Exception& ) + {} + } + } + + try + { + if ( bAllowToRepair50 && !xResult.is() ) + { + OUString aOrigContName( "Ole-Object" ); + if ( xNameContainer->hasByName( aOrigContName ) ) + { + uno::Reference< embed::XClassifiedObject > xClassified( xNameContainer, uno::UNO_QUERY_THROW ); + if ( MimeConfigurationHelper::ClassIDsEqual( xClassified->getClassID(), MimeConfigurationHelper::GetSequenceClassID( SO3_OUT_CLASSID ) ) ) + { + // this is an OLE object wrongly stored in 5.0 format + // this object must be repaired since SO7 has done it + + uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); + uno::Reference< io::XTruncate > xTruncate( xOutputStream, uno::UNO_QUERY_THROW ); + + uno::Reference< io::XInputStream > xOrigInputStream; + if ( ( xNameContainer->getByName( aOrigContName ) >>= xOrigInputStream ) + && xOrigInputStream.is() ) + { + // the provided input stream must be based on temporary medium and must be independent + // from the stream the storage is based on + uno::Reference< io::XSeekable > xOrigSeekable( xOrigInputStream, uno::UNO_QUERY ); + if ( xOrigSeekable.is() ) + xOrigSeekable->seek( 0 ); + + uno::Reference< lang::XComponent > xNameContDisp( xNameContainer, uno::UNO_QUERY_THROW ); + xNameContDisp->dispose(); // free the original stream + + xTruncate->truncate(); + ::comphelper::OStorageHelper::CopyInputToOutput( xOrigInputStream, xOutputStream ); + xOutputStream->flush(); + + if ( xStream == m_xObjectStream ) + { + if ( !m_aTempURL.isEmpty() ) + { + // this is the own stream, so the temporary URL must be cleaned if it exists + KillFile_Impl( m_aTempURL, m_xContext ); + m_aTempURL.clear(); + } + +#ifdef _WIN32 + // retry to create the component after recovering + GetRidOfComponent(); + + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + GetRidOfComponent(); + } +#endif + } + + xResult = TryToRetrieveCachedVisualRepresentation_Impl( xStream ); + } + } + } + } + } + catch( const uno::Exception& ) + {} + } + } + + return xResult; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const uno::Reference< io::XStream >& xNewObjectStream, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + { + SAL_WARN_IF( xNewObjectStream != m_xObjectStream, "embeddedobj.ole", "The streams must be the same!" ); + return; + } + + uno::Reference<io::XSeekable> xNewSeekable(xNewObjectStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence(stream version): " + "empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewObjectStream->getOutputStream(); + xOldSeekable->seek(0); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + try { + uno::Reference< lang::XComponent > xComponent( m_xObjectStream, uno::UNO_QUERY ); + OSL_ENSURE( !m_xObjectStream.is() || xComponent.is(), "Wrong stream implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + + m_xObjectStream = xNewObjectStream; + m_xParentStorage = xNewParentStorage; + m_aEntryName = aNewName; +} + + +void OleEmbeddedObject::SwitchOwnPersistence( const uno::Reference< embed::XStorage >& xNewParentStorage, + const OUString& aNewName ) +{ + if ( xNewParentStorage == m_xParentStorage && aNewName == m_aEntryName ) + return; + + sal_Int32 nStreamMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; + + uno::Reference< io::XStream > xNewOwnStream = xNewParentStorage->openStreamElement( aNewName, nStreamMode ); + + uno::Reference<io::XSeekable> xNewSeekable (xNewOwnStream, uno::UNO_QUERY); + if (xNewSeekable.is() && xNewSeekable->getLength() == 0) + { + uno::Reference<io::XSeekable> xOldSeekable(m_xObjectStream, uno::UNO_QUERY); + if (xOldSeekable.is() && xOldSeekable->getLength() > 0) + { + SAL_WARN("embeddedobj.ole", "OleEmbeddedObject::SwitchOwnPersistence: empty new stream, reusing old one"); + uno::Reference<io::XInputStream> xInput = m_xObjectStream->getInputStream(); + uno::Reference<io::XOutputStream> xOutput = xNewOwnStream->getOutputStream(); + comphelper::OStorageHelper::CopyInputToOutput(xInput, xOutput); + xNewSeekable->seek(0); + } + } + + SAL_WARN_IF( !xNewOwnStream.is(), "embeddedobj.ole", "The method can not return empty reference!" ); + + SwitchOwnPersistence( xNewParentStorage, xNewOwnStream, aNewName ); +} + +#ifdef _WIN32 + +bool OleEmbeddedObject::SaveObject_Impl() +{ + bool bResult = false; + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->saveObject(); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +bool OleEmbeddedObject::OnShowWindow_Impl( bool bShow ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + bool bResult = false; + + SAL_WARN_IF( m_nObjectState == -1, "embeddedobj.ole", "The object has no persistence!" ); + SAL_WARN_IF( m_nObjectState == embed::EmbedStates::LOADED, "embeddedobj.ole", "The object get OnShowWindow in loaded state!" ); + if ( m_nObjectState == -1 || m_nObjectState == embed::EmbedStates::LOADED ) + return false; + + // the object is either activated or deactivated + sal_Int32 nOldState = m_nObjectState; + if ( bShow && m_nObjectState == embed::EmbedStates::RUNNING ) + { + m_nObjectState = embed::EmbedStates::ACTIVE; + m_aVerbExecutionController.ObjectIsActive(); + + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + else if ( !bShow && m_nObjectState == embed::EmbedStates::ACTIVE ) + { + m_nObjectState = embed::EmbedStates::RUNNING; + aGuard.clear(); + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } + + if ( m_xClientSite.is() ) + { + try + { + m_xClientSite->visibilityChanged( bShow ); + bResult = true; + } + catch( const uno::Exception& ) + { + } + } + + return bResult; +} + + +void OleEmbeddedObject::OnIconChanged_Impl() +{ + // TODO/LATER: currently this notification seems to be impossible + // MakeEventListenerNotification_Impl( OUString( "OnIconChanged" ) ); +} + + +void OleEmbeddedObject::OnViewChanged_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + // For performance reasons the notification currently is ignored, STAMPIT object is the exception, + // it can never be active and never call SaveObject, so it is the only way to detect that it is changed + + // ==== the STAMPIT related solution ============================= + // the following variable is used to detect whether the object was modified during verb execution + m_aVerbExecutionController.ModificationNotificationIsDone(); + + // The following things are controlled by VerbExecutionController: + // - if the verb execution is in progress and the view is changed the object will be stored + // after the execution, so there is no need to send the notification. + // - the STAMPIT object can never be active. + if (m_aVerbExecutionController.CanDoNotification() && + m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && + (MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0x852ee1c9, 0x9058, 0x44ba, 0x8c, 0x6c, 0x0c, 0x5f, 0xc6, 0x6b, 0xdb, 0x8d)) || + MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(0xcf1b4491, 0xbea3, 0x4c9f, 0xa7, 0x0f, 0x22, 0x1b, 0x1e, 0xca, 0xef, 0x3e))) + ) + { + // The view is changed while the object is in running state, save the new object + m_xCachedVisualRepresentation.clear(); + SaveObject_Impl(); + MakeEventListenerNotification_Impl( "OnVisAreaChanged" ); + } + +} + + +void OleEmbeddedObject::OnClosed_Impl() +{ + if ( m_bDisposed ) + throw lang::DisposedException(); + + if ( m_nObjectState != embed::EmbedStates::LOADED ) + { + sal_Int32 nOldState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + StateChangeNotification_Impl( false, nOldState, m_nObjectState ); + } +} + + +OUString OleEmbeddedObject::CreateTempURLEmpty_Impl() +{ + SAL_WARN_IF( !m_aTempURL.isEmpty(), "embeddedobj.ole", "The object has already the temporary file!" ); + m_aTempURL = GetNewTempFileURL_Impl( m_xContext ); + + return m_aTempURL; +} + + +OUString OleEmbeddedObject::GetTempURL_Impl() +{ + if ( m_aTempURL.isEmpty() ) + { + SAL_INFO( "embeddedobj.ole", "embeddedobj (mv76033) OleEmbeddedObject::GetTempURL_Impl, tempfile creation" ); + + // if there is no temporary file, it will be created from the own entry + uno::Reference< embed::XOptimizedStorage > xOptParStorage( m_xParentStorage, uno::UNO_QUERY ); + if ( xOptParStorage.is() ) + { + m_aTempURL = GetNewFilledTempFile_Impl( xOptParStorage, m_aEntryName, m_xContext ); + } + else if ( m_xObjectStream.is() ) + { + // load object from the stream + uno::Reference< io::XInputStream > xInStream = m_xObjectStream->getInputStream(); + if ( !xInStream.is() ) + throw io::IOException(); // TODO: access denied + + m_aTempURL = GetNewFilledTempFile_Impl( xInStream, m_xContext ); + } + } + + return m_aTempURL; +} + + +void OleEmbeddedObject::CreateOleComponent_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + m_pOleComponent = pOleComponent ? pOleComponent : new OleComponent( m_xContext, this ); + + if ( !m_xClosePreventer.is() ) + m_xClosePreventer = new OClosePreventer; + + m_pOleComponent->addCloseListener( m_xClosePreventer ); + } +} + + +void OleEmbeddedObject::CreateOleComponentAndLoad_Impl( + rtl::Reference<OleComponent> const & pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + + GetTempURL_Impl(); + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); // TODO + + m_pOleComponent->LoadEmbeddedObject( m_aTempURL ); + } +} + + +void OleEmbeddedObject::CreateOleComponentFromClipboard_Impl( OleComponent* pOleComponent ) +{ + if ( !m_pOleComponent ) + { + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); + + CreateOleComponent_Impl( pOleComponent ); + + // after the loading the object can appear as a link + // will be detected later by olecomponent + m_pOleComponent->CreateObjectFromClipboard(); + } +} + + +uno::Reference< io::XOutputStream > OleEmbeddedObject::GetStreamForSaving() +{ + if ( !m_xObjectStream.is() ) + throw uno::RuntimeException(); //TODO: + + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + uno::Reference< io::XTruncate > xTruncate( xOutStream, uno::UNO_QUERY_THROW ); + xTruncate->truncate(); + + return xOutStream; +} + + +void OleEmbeddedObject::StoreObjectToStream( uno::Reference< io::XOutputStream > const & xOutStream ) +{ + // this method should be used only on windows + if ( m_pOleComponent ) + m_pOleComponent->StoreOwnTmpIfNecessary(); + + // now all the changes should be in temporary location + if ( m_aTempURL.isEmpty() ) + throw uno::RuntimeException(); + + // open temporary file for reading + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xTempInStream = xTempAccess->openFileRead( m_aTempURL ); + SAL_WARN_IF( !xTempInStream.is(), "embeddedobj.ole", "The object's temporary file can not be reopened for reading!" ); + + // TODO: use bStoreVisReplace + + if ( !xTempInStream.is() ) + { + throw io::IOException(); // TODO: + } + + // write all the contents to XOutStream + uno::Reference< io::XTruncate > xTrunc( xOutStream, uno::UNO_QUERY_THROW ); + xTrunc->truncate(); + + ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutStream ); + + // TODO: should the view replacement be in the stream ??? + // probably it must be specified on storing +} +#endif + +void OleEmbeddedObject::StoreToLocation_Impl( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs, + bool bSaveAs ) +{ + // TODO: use lObjArgs + // TODO: exchange StoreVisualReplacement by SO file format version? + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + bool bVisReplIsStored = false; + + bool bTryOptimization = false; + bool bStoreVis = m_bStoreVisRepl; + uno::Reference< io::XStream > xCachedVisualRepresentation; + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= bStoreVis; + else if ( prop.Name == "VisualReplacement" ) + prop.Value >>= xCachedVisualRepresentation; + else if ( prop.Name == "CanTryOptimization" ) + prop.Value >>= bTryOptimization; + } + + // ignore visual representation provided from outside if it should not be stored + if ( !bStoreVis ) + xCachedVisualRepresentation.clear(); + + if ( bStoreVis && !HasVisReplInStream() && !xCachedVisualRepresentation.is() ) + throw io::IOException(); // TODO: there is no cached visual representation and nothing is provided from outside + + // if the representation is provided from outside it should be copied to a local stream + bool bNeedLocalCache = xCachedVisualRepresentation.is(); + + uno::Reference< io::XStream > xTargetStream; + + bool bStoreLoaded = false; + if ( m_nObjectState == embed::EmbedStates::LOADED +#ifdef _WIN32 + // if the object was NOT modified after storing it can be just copied + // as if it was in loaded state + || ( m_pOleComponent && !m_pOleComponent->IsDirty() ) +#endif + ) + { + bool bOptimizedCopyingDone = false; + + if ( bTryOptimization && bStoreVis == HasVisReplInStream() ) + { + try + { + uno::Reference< embed::XOptimizedStorage > xSourceOptStor( m_xParentStorage, uno::UNO_QUERY_THROW ); + uno::Reference< embed::XOptimizedStorage > xTargetOptStor( xStorage, uno::UNO_QUERY_THROW ); + xSourceOptStor->copyElementDirectlyTo( m_aEntryName, xTargetOptStor, sEntName ); + bOptimizedCopyingDone = true; + } + catch( const uno::Exception& ) + { + } + } + + if ( !bOptimizedCopyingDone ) + { + // if optimized copying fails a normal one should be tried + m_xParentStorage->copyElementTo( m_aEntryName, xStorage, sEntName ); + } + + // the locally retrieved representation is always preferable + // since the object is in loaded state the representation is unchanged + if ( m_xCachedVisualRepresentation.is() ) + { + xCachedVisualRepresentation = m_xCachedVisualRepresentation; + bNeedLocalCache = false; + } + + bVisReplIsStored = HasVisReplInStream(); + bStoreLoaded = true; + } +#ifdef _WIN32 + else if ( m_pOleComponent ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( xTargetStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = xTargetStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + StoreObjectToStream( xOutStream ); + bVisReplIsStored = true; + + if ( bSaveAs ) + { + // no need to do it on StoreTo since in this case the replacement is in the stream + // and there is no need to cache it even if it is thrown away because the object + // is not changed by StoreTo action + + uno::Reference< io::XStream > xTmpCVRepresentation = + TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + // the locally retrieved representation is always preferable + if ( xTmpCVRepresentation.is() ) + { + xCachedVisualRepresentation = xTmpCVRepresentation; + bNeedLocalCache = false; + } + } + } +#endif + else if (true) // loplugin:flatten + { + throw io::IOException(); // TODO + } + + if ( !xTargetStream.is() ) + { + xTargetStream = + xStorage->openStreamElement( sEntName, embed::ElementModes::READWRITE ); + if ( !xTargetStream.is() ) + throw io::IOException(); //TODO: access denied + } + + LetCommonStoragePassBeUsed_Impl( xTargetStream ); + + if ( bStoreVis != bVisReplIsStored ) + { + if ( bStoreVis ) + { + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + SAL_WARN_IF( !xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + + // the following copying will be done in case it is SaveAs anyway + // if it is not SaveAs the seekable access is not required currently + // TODO/LATER: may be required in future + if ( bSaveAs ) + { + uno::Reference< io::XSeekable > xCachedSeek( xCachedVisualRepresentation, uno::UNO_QUERY ); + if ( !xCachedSeek.is() ) + { + xCachedVisualRepresentation + = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + bNeedLocalCache = false; + } + } + + InsertVisualCache_Impl( xTargetStream, xCachedVisualRepresentation ); + } + else + { + // the removed representation could be cached by this method + if ( !xCachedVisualRepresentation.is() ) + xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( xTargetStream ); + + if (!m_bStreamReadOnly) + RemoveVisualCache_Impl(xTargetStream); + } + } + + if ( bSaveAs ) + { + m_bWaitSaveCompleted = true; + m_xNewObjectStream = xTargetStream; + m_xNewParentStorage = xStorage; + m_aNewEntryName = sEntName; + m_bNewVisReplInStream = bStoreVis; + m_bStoreLoaded = bStoreLoaded; + + if ( xCachedVisualRepresentation.is() ) + { + if ( bNeedLocalCache ) + m_xNewCachedVisRepl = GetNewFilledTempStream_Impl( xCachedVisualRepresentation->getInputStream() ); + else + m_xNewCachedVisRepl = xCachedVisualRepresentation; + } + + // TODO: register listeners for storages above, in case they are disposed + // an exception will be thrown on saveCompleted( true ) + } + else + { + uno::Reference< lang::XComponent > xComp( xTargetStream, uno::UNO_QUERY ); + if ( xComp.is() ) + { + try { + xComp->dispose(); + } catch( const uno::Exception& ) + { + } + } + } +} + + +void SAL_CALL OleEmbeddedObject::setPersistentEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 nEntryConnectionMode, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setPersistentEntry( xStorage, sEntName, nEntryConnectionMode, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + // the type of the object must be already set + // a kind of typedetection should be done in the factory; + // the only exception is object initialized from a stream, + // the class ID will be detected from the stream + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // May be LOADED should be forbidden here ??? + if ( ( m_nObjectState != -1 || nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + && ( m_nObjectState == -1 || nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) ) + { + // if the object is not loaded + // it can not get persistent representation without initialization + + // if the object is loaded + // it can switch persistent representation only without initialization + + throw embed::WrongStateException( + "Can't change persistent representation of activated object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + { + if ( nEntryConnectionMode != embed::EntryInitModes::NO_INIT ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + saveCompleted( m_xParentStorage != xStorage || m_aEntryName != sEntName ); + } + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + bool bElExists = xNameAccess->hasByName( sEntName ); + + m_bReadOnly = false; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "ReadOnly" ) + prop.Value >>= m_bReadOnly; + +#ifdef _WIN32 + sal_Int32 nStorageMode = m_bReadOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE; +#endif + + SwitchOwnPersistence( xStorage, sEntName ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + if ( prop.Name == "StoreVisualReplacement" ) + prop.Value >>= m_bStoreVisRepl; + +#ifdef _WIN32 + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT ) + { + if ( m_bFromClipboard ) + { + // the object should be initialized from clipboard + // impossibility to initialize the object means error here + CreateOleComponentFromClipboard_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( bElExists ) + { + // load object from the stream + // after the loading the object can appear as a link + // will be detected by olecomponent + try + { + CreateOleComponentAndLoad_Impl(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + } + catch( const uno::Exception& ) + { + // TODO/LATER: detect classID of the object if possible + // means that the object inprocess server could not be successfully instantiated + GetRidOfComponent(); + } + + m_nObjectState = embed::EmbedStates::LOADED; + } + else + { + // create a new object + CreateOleComponent_Impl(); + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + } + else + { + if ( ( nStorageMode & embed::ElementModes::READWRITE ) != embed::ElementModes::READWRITE ) + throw io::IOException(); + + if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // the document just already changed its stream to store to; + // the links to OLE documents switch their persistence in the same way + // as normal embedded objects + } + else if ( nEntryConnectionMode == embed::EntryInitModes::TRUNCATE_INIT ) + { + // create a new object, that will be stored in specified stream + CreateOleComponent_Impl(); + + m_pOleComponent->CreateNewEmbeddedObject( m_aClassID ); + m_pOleComponent->RunObject(); + m_nObjectState = embed::EmbedStates::RUNNING; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT ) + { + // use URL ( may be content or stream later ) from MediaDescriptor to initialize object + OUString aURL; + for ( beans::PropertyValue const & prop : lArguments ) + if ( prop.Name == "URL" ) + prop.Value >>= aURL; + + if ( aURL.isEmpty() ) + throw lang::IllegalArgumentException( + "Empty URL is provided in the media descriptor!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + CreateOleComponent_Impl(); + + // TODO: the m_bIsLink value must be set already + if ( !m_bIsLink ) + m_pOleComponent->CreateObjectFromFile( aURL ); + else + m_pOleComponent->CreateLinkFromFile( aURL ); + + m_pOleComponent->RunObject(); + m_aClassID = m_pOleComponent->GetCLSID(); // was not set during construction + + m_nObjectState = embed::EmbedStates::RUNNING; + } + //else if ( nEntryConnectionMode == embed::EntryInitModes::TRANSFERABLE_INIT ) + //{ + //TODO: + //} + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + } +#else + // On Unix the OLE object can not do anything except storing itself somewhere + if ( nEntryConnectionMode == embed::EntryInitModes::DEFAULT_INIT && bElExists ) + { + // TODO/LATER: detect classID of the object + // can be a real problem for the links + + m_nObjectState = embed::EmbedStates::LOADED; + } + else if ( nEntryConnectionMode == embed::EntryInitModes::NO_INIT ) + { + // do nothing, the object has already switched it's persistence + } + else + throw lang::IllegalArgumentException( "Wrong connection mode is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + +#endif +} + + +void SAL_CALL OleEmbeddedObject::storeToEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeToEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, false ); + + // TODO: should the listener notification be done? +} + + +void SAL_CALL OleEmbeddedObject::storeAsEntry( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeAsEntry( xStorage, sEntName, lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + StoreToLocation_Impl( xStorage, sEntName, lObjArgs, true ); + + // TODO: should the listener notification be done here or in saveCompleted? +} + + +void SAL_CALL OleEmbeddedObject::saveCompleted( sal_Bool bUseNew ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->saveCompleted( bUseNew ); + return; + } + // end wrapping related part ==================== + + osl::ClearableMutexGuard aGuard(m_aMutex); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + // it is allowed to call saveCompleted( false ) for nonstored objects + if ( !m_bWaitSaveCompleted && !bUseNew ) + return; + + SAL_WARN_IF( !m_bWaitSaveCompleted, "embeddedobj.ole", "Unexpected saveCompleted() call!" ); + if ( !m_bWaitSaveCompleted ) + throw io::IOException(); // TODO: illegal call + + OSL_ENSURE( m_xNewObjectStream.is() && m_xNewParentStorage.is() , "Internal object information is broken!" ); + if ( !m_xNewObjectStream.is() || !m_xNewParentStorage.is() ) + throw uno::RuntimeException(); // TODO: broken internal information + + if ( bUseNew ) + { + SwitchOwnPersistence( m_xNewParentStorage, m_xNewObjectStream, m_aNewEntryName ); + m_bStoreVisRepl = m_bNewVisReplInStream; + SetVisReplInStream( m_bNewVisReplInStream ); + m_xCachedVisualRepresentation = m_xNewCachedVisRepl; + } + else + { + // close remembered stream + try { + uno::Reference< lang::XComponent > xComponent( m_xNewObjectStream, uno::UNO_QUERY ); + SAL_WARN_IF( !xComponent.is(), "embeddedobj.ole", "Wrong storage implementation!" ); + if ( xComponent.is() ) + xComponent->dispose(); + } + catch ( const uno::Exception& ) + { + } + } + + bool bStoreLoaded = m_bStoreLoaded; + + m_xNewObjectStream.clear(); + m_xNewParentStorage.clear(); + m_aNewEntryName.clear(); + m_bWaitSaveCompleted = false; + m_bNewVisReplInStream = false; + m_xNewCachedVisRepl.clear(); + m_bStoreLoaded = false; + + if ( bUseNew && m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded + && m_nObjectState != embed::EmbedStates::LOADED ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + if ( bUseNew ) + { + MakeEventListenerNotification_Impl( "OnSaveAsDone"); + + // the object can be changed only on windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); + } + } +} + + +sal_Bool SAL_CALL OleEmbeddedObject::hasEntry() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->hasEntry(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_xObjectStream.is() ) + return true; + + return false; +} + + +OUString SAL_CALL OleEmbeddedObject::getEntryName() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getEntryName(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_aEntryName; +} + + +void SAL_CALL OleEmbeddedObject::storeOwn() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->storeOwn(); + return; + } + // end wrapping related part ==================== + + // during switching from Activated to Running and from Running to Loaded states the object will + // ask container to store the object, the container has to make decision + // to do so or not + + osl::ClearableMutexGuard aGuard(m_aMutex); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + VerbExecutionControllerGuard aVerbGuard( m_aVerbExecutionController ); + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "Can't store object without persistence!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: access denied + + LetCommonStoragePassBeUsed_Impl( m_xObjectStream ); + + bool bStoreLoaded = true; + +#ifdef _WIN32 + if ( m_nObjectState != embed::EmbedStates::LOADED && m_pOleComponent && m_pOleComponent->IsDirty() ) + { + bStoreLoaded = false; + + OSL_ENSURE( m_xParentStorage.is() && m_xObjectStream.is(), "The object has no valid persistence!" ); + + if ( !m_xObjectStream.is() ) + throw io::IOException(); //TODO: access denied + + SetStreamMediaType_Impl( m_xObjectStream, "application/vnd.sun.star.oleobject" ); + uno::Reference< io::XOutputStream > xOutStream = m_xObjectStream->getOutputStream(); + if ( !xOutStream.is() ) + throw io::IOException(); //TODO: access denied + + // TODO: does this work for links too? + StoreObjectToStream( GetStreamForSaving() ); + + // the replacement is changed probably, and it must be in the object stream + if ( !m_pOleComponent->IsWorkaroundActive() ) + m_xCachedVisualRepresentation.clear(); + SetVisReplInStream( true ); + } +#endif + + if ( m_bStoreVisRepl != HasVisReplInStream() ) + { + if ( m_bStoreVisRepl ) + { + // the m_xCachedVisualRepresentation must be set or it should be already stored + if ( m_xCachedVisualRepresentation.is() ) + InsertVisualCache_Impl( m_xObjectStream, m_xCachedVisualRepresentation ); + else + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SAL_WARN_IF( !m_xCachedVisualRepresentation.is(), "embeddedobj.ole", "No representation is available!" ); + } + } + else + { + if ( !m_xCachedVisualRepresentation.is() ) + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + RemoveVisualCache_Impl( m_xObjectStream ); + } + + SetVisReplInStream( m_bStoreVisRepl ); + } + + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + { + // the object replacement image should be updated, so the cached size as well + m_bHasCachedSize = false; + try + { + // the call will cache the size in case of success + // probably it might need to be done earlier, while the object is in active state + getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const uno::Exception& ) + {} + } + + aGuard.clear(); + + MakeEventListenerNotification_Impl( "OnSaveDone"); + + // the object can be changed only on Windows + // the notification should be done only if the object is not in loaded state + if ( m_pOleComponent && m_nUpdateMode == embed::EmbedUpdateModes::ALWAYS_UPDATE && !bStoreLoaded ) + MakeEventListenerNotification_Impl( "OnVisAreaChanged"); +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isReadonly() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isReadonly(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return m_bReadOnly; +} + + +void SAL_CALL OleEmbeddedObject::reload( + const uno::Sequence< beans::PropertyValue >& lArguments, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbedPersist > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->reload( lArguments, lObjArgs ); + return; + } + // end wrapping related part ==================== + + // TODO: use lObjArgs + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_nObjectState == -1 ) + { + // the object is still not loaded + throw embed::WrongStateException( "The object persistence is not initialized!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: + // throw away current document + // load new document from current storage + // use meaningful part of lArguments +} + + +void SAL_CALL OleEmbeddedObject::breakLink( const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->breakLink( xStorage, sEntName ); + return; + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + // TODO: The object must be at least in Running state; + if ( !m_bIsLink || m_nObjectState == -1 || !m_pOleComponent ) + { + // it must be a linked initialized object + throw embed::WrongStateException( + "The object is not a valid linked object!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + if ( m_bReadOnly ) + throw io::IOException(); // TODO: Access denied + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + +#ifdef _WIN32 + // TODO: create an object based on the link + + // disconnect the old temporary URL + OUString aOldTempURL = m_aTempURL; + m_aTempURL.clear(); + + rtl::Reference<OleComponent> pNewOleComponent = new OleComponent(m_xContext, this); + try { + pNewOleComponent->InitEmbeddedCopyOfLink(m_pOleComponent); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + try { + GetRidOfComponent(); + } + catch (const uno::Exception&) + { + if (!m_aTempURL.isEmpty()) + KillFile_Impl(m_aTempURL, m_xContext); + m_aTempURL = aOldTempURL; + throw; + } + + KillFile_Impl(aOldTempURL, m_xContext); + + CreateOleComponent_Impl(pNewOleComponent); + + if (m_xParentStorage != xStorage || !m_aEntryName.equals(sEntName)) + SwitchOwnPersistence(xStorage, sEntName); + + if (m_nObjectState != embed::EmbedStates::LOADED) + { + // TODO: should we activate the new object if the link was activated? + + const sal_Int32 nTargetState = m_nObjectState; + m_nObjectState = embed::EmbedStates::LOADED; + + if (nTargetState == embed::EmbedStates::RUNNING) + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + else // nTargetState == embed::EmbedStates::ACTIVE + { + m_pOleComponent->RunObject(); // the object already was in running state, the server must be installed + m_pOleComponent->ExecuteVerb(embed::EmbedVerbs::MS_OLEVERB_OPEN); + } + + m_nObjectState = nTargetState; + } + + m_bIsLink = false; + m_aLinkURL.clear(); +#else // ! _WIN32 + throw io::IOException(); //TODO: +#endif // _WIN32 +} + + +sal_Bool SAL_CALL OleEmbeddedObject::isLink() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->isLink(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + return m_bIsLink; +} + + +OUString SAL_CALL OleEmbeddedObject::getLinkURL() +{ + // begin wrapping related part ==================== + uno::Reference< embed::XLinkageSupport > xWrappedObject( m_xWrappedObject, uno::UNO_QUERY ); + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getLinkURL(); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + if ( m_bWaitSaveCompleted ) + throw embed::WrongStateException( + "The object waits for saveCompleted() call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( !m_bIsLink ) + throw embed::WrongStateException( + "The object is not a link object!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: probably the link URL can be retrieved from OLE + + return m_aLinkURL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olepersist.hxx b/embeddedobj/source/msole/olepersist.hxx new file mode 100644 index 000000000..9a3e76c68 --- /dev/null +++ b/embeddedobj/source/msole/olepersist.hxx @@ -0,0 +1,42 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace io { class XInputStream; } + namespace uno { class XComponentContext; } +} + +OUString GetNewTempFileURL_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/// @throws css::io::IOException +/// @throws css::uno::RuntimeException +OUString GetNewFilledTempFile_Impl( + css::uno::Reference<css::io::XInputStream > const & xInStream, + css::uno::Reference<css::uno::XComponentContext> const & xContext); + +bool KillFile_Impl( const OUString& aURL, const css::uno::Reference< css::uno::XComponentContext >& xContext ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olevisual.cxx b/embeddedobj/source/msole/olevisual.cxx new file mode 100644 index 000000000..2201d7113 --- /dev/null +++ b/embeddedobj/source/msole/olevisual.cxx @@ -0,0 +1,432 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/WrongStateException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> + +#include <oleembobj.hxx> +#if defined (_WIN32) +#include <comphelper/mimeconfighelper.hxx> +#endif +#include <comphelper/seqstream.hxx> +#include <filter/msfilter/classids.hxx> +#include <sal/log.hxx> + +#if defined(_WIN32) +#include "olecomponent.hxx" +#include <tools/diagnose_ex.h> +#endif + +using namespace ::com::sun::star; +using namespace ::comphelper; + +embed::VisualRepresentation OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl( + const uno::Reference< io::XStream >& xCachedVisRepr ) +{ + embed::VisualRepresentation aVisualRepr; + + // TODO: detect the format in the future for now use workaround + uno::Reference< io::XInputStream > xInStream = xCachedVisRepr->getInputStream(); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + uno::Reference< io::XSeekable > xSeekable( xCachedVisRepr, uno::UNO_QUERY_THROW ); + + uno::Sequence< sal_Int8 > aSeq( 2 ); + xInStream->readBytes( aSeq, 2 ); + xSeekable->seek( 0 ); + if ( aSeq.getLength() == 2 && aSeq[0] == 'B' && aSeq[1] == 'M' ) + { + // it's a bitmap + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", + "Bitmap", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + else + { + // it's a metafile + aVisualRepr.Flavor = datatransfer::DataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + } + + sal_Int32 nStreamLength = static_cast<sal_Int32>(xSeekable->getLength()); + uno::Sequence< sal_Int8 > aRepresent( nStreamLength ); + xInStream->readBytes( aRepresent, nStreamLength ); + aVisualRepr.Data <<= aRepresent; + + return aVisualRepr; +} + +void SAL_CALL OleEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + xWrappedObject->setVisualAreaSize( nAspect, aSize ); + return; + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + +#ifdef _WIN32 + // RECOMPOSE_ON_RESIZE misc flag means that the object has to be switched to running state on resize. + // SetExtent() is called only for objects that require it, + // it should not be called for MSWord documents to workaround problem i49369 + // If cached size is not set, that means that this is the size initialization, so there is no need to set the real size + bool bAllowToSetExtent = + ( ( getStatus( nAspect ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) + && !MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(MSO_WW8_CLASSID)) + && m_bHasCachedSize ); + + if ( m_nObjectState == embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + aGuard.clear(); + try { + changeState( embed::EmbedStates::RUNNING ); + } + catch( const uno::Exception& ) + { + SAL_WARN( "embeddedobj.ole", "The object should not be resized without activation!" ); + } + aGuard.reset(); + } + + if ( m_pOleComponent && m_nObjectState != embed::EmbedStates::LOADED && bAllowToSetExtent ) + { + awt::Size aSizeToSet = aSize; + aGuard.clear(); + try { + m_pOleComponent->SetExtent( aSizeToSet, nAspect ); // will throw an exception in case of failure + m_bHasSizeToSet = false; + } + catch( const uno::Exception& ) + { + // some objects do not allow to set the size even in running state + m_bHasSizeToSet = true; + m_aSizeToSet = aSizeToSet; + m_nAspectToSet = nAspect; + } + aGuard.reset(); + } +#endif + + // cache the values + m_bHasCachedSize = true; + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; +} + +awt::Size SAL_CALL OleEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getVisualAreaSize( nAspect ); + } + // end wrapping related part ==================== + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + awt::Size aResult; + +#ifdef _WIN32 + // TODO/LATER: Support different aspects + if ( m_pOleComponent && !m_bHasSizeToSet && nAspect == embed::Aspects::MSOLE_CONTENT ) + { + try + { + // the cached size updated every time the object is stored + if ( m_bHasCachedSize ) + { + aResult = m_aCachedSize; + } + else + { + // there is no internal cache + awt::Size aSize; + aGuard.clear(); + + bool bBackToLoaded = false; + + bool bSuccess = false; + if ( getCurrentState() == embed::EmbedStates::LOADED ) + { + SAL_WARN( "embeddedobj.ole", "Loaded object has no cached size!" ); + + // try to switch the object to RUNNING state and request the value again + try { + changeState( embed::EmbedStates::RUNNING ); + // the links should be switched back to loaded state to avoid too + // many open MathType instances + bBackToLoaded = true; + } + catch( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + + try + { + // first try to get size using replacement image + aSize = m_pOleComponent->GetExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetExtent() failed:"); + } + + if (bBackToLoaded) + { + try + { + changeState(embed::EmbedStates::LOADED); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "ignoring "); + } + } + + if ( !bSuccess ) + { + try + { + // second try the cached replacement image + aSize = m_pOleComponent->GetCachedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetCachedExtent() failed:"); + } + } + + if ( !bSuccess ) + { + try + { + // third try the size reported by the object + aSize = m_pOleComponent->GetRecommendedExtent( nAspect ); // will throw an exception in case of failure + bSuccess = true; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OleEmbeddedObject::getVisualAreaSize: GetRecommendedExtent() failed:"); + } + } + + if ( !bSuccess ) + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + + aGuard.reset(); + + m_aCachedSize = aSize; + m_nCachedAspect = nAspect; + m_bHasCachedSize = true; + + aResult = m_aCachedSize; + } + } + catch ( const embed::NoVisualAreaSizeException& ) + { + throw; + } + catch ( const uno::Exception& ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + } + else +#endif + { + // return cached value + if ( !m_bHasCachedSize ) + { + throw embed::NoVisualAreaSizeException( + "No size available!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + SAL_WARN_IF( nAspect != m_nCachedAspect, "embeddedobj.ole", "Unexpected aspect is requested!" ); + aResult = m_aCachedSize; + } + + return aResult; +} + +embed::VisualRepresentation SAL_CALL OleEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getPreferredVisualRepresentation( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: if the object has cached representation then it should be returned + // TODO: if the object has no cached representation and is in loaded state it should switch itself to the running state + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + // TODO: in case of different aspects they must be applied to the mediatype and XTransferable must be used + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) + && m_nObjectState == embed::EmbedStates::LOADED ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream, true ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + +#ifdef _WIN32 + if ( !m_xCachedVisualRepresentation.is() && m_pOleComponent ) + { + try + { + if ( m_nObjectState == embed::EmbedStates::LOADED ) + changeState( embed::EmbedStates::RUNNING ); + + datatransfer::DataFlavor aDataFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Windows Metafile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + embed::VisualRepresentation aVisualRepr; + aVisualRepr.Data = m_pOleComponent->getTransferData( aDataFlavor ); + aVisualRepr.Flavor = aDataFlavor; + + uno::Sequence< sal_Int8 > aVisReplSeq; + aVisualRepr.Data >>= aVisReplSeq; + if ( aVisReplSeq.getLength() ) + { + m_xCachedVisualRepresentation = GetNewFilledTempStream_Impl( + uno::Reference< io::XInputStream >( + new ::comphelper::SequenceInputStream(aVisReplSeq))); + } + + return aVisualRepr; + } + catch( const uno::Exception& ) + {} + } +#endif + + // the cache is used only as a fallback if object is not in loaded state + if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) ) + { + m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream ); + SetVisReplInStream( m_xCachedVisualRepresentation.is() ); + } + + if ( !m_xCachedVisualRepresentation.is() ) + { + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + } + + return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation ); +} + +sal_Int32 SAL_CALL OleEmbeddedObject::getMapUnit( sal_Int64 nAspect ) +{ + // begin wrapping related part ==================== + uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject; + if ( xWrappedObject.is() ) + { + // the object was converted to OOo embedded object, the current implementation is now only a wrapper + return xWrappedObject->getMapUnit( nAspect ); + } + // end wrapping related part ==================== + + ::osl::MutexGuard aGuard( m_aMutex ); + if ( m_bDisposed ) + throw lang::DisposedException(); // TODO + + SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + // no representation can be retrieved + throw embed::WrongStateException( "Illegal call!", + static_cast< ::cppu::OWeakObject* >(this) ); + + if ( m_nObjectState == -1 ) + throw embed::WrongStateException( "The object is not loaded!", + static_cast< ::cppu::OWeakObject* >(this) ); + + return embed::EmbedMapUnits::ONE_100TH_MM; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.cxx b/embeddedobj/source/msole/olewrapclient.cxx new file mode 100644 index 000000000..15d5f1663 --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.cxx @@ -0,0 +1,148 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> + +#include "olewrapclient.hxx" +#include "olecomponent.hxx" + +// TODO: May be a mutex must be introduced + +OleWrapperClientSite::OleWrapperClientSite( OleComponent* pOleComp ) +: m_nRefCount( 0 ) +, m_pOleComp( pOleComp ) +{ + OSL_ENSURE( m_pOleComp, "No ole component is provided!" ); +} + +OleWrapperClientSite::~OleWrapperClientSite() +{ +} + +STDMETHODIMP OleWrapperClientSite::QueryInterface( REFIID riid , void** ppv ) +{ + *ppv=nullptr; + + if ( riid == IID_IUnknown ) + *ppv = static_cast<IUnknown*>(this); + + if ( riid == IID_IOleClientSite ) + *ppv = static_cast<IOleClientSite*>(this); + + if ( *ppv != nullptr ) + { + static_cast<IUnknown*>(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::AddRef() +{ + return osl_atomic_increment( &m_nRefCount); +} + +STDMETHODIMP_(ULONG) OleWrapperClientSite::Release() +{ + ULONG nReturn = --m_nRefCount; + if ( m_nRefCount == 0 ) + delete this; + + return nReturn; +} + +void OleWrapperClientSite::disconnectOleComponent() +{ + // must not be called from the descructor of OleComponent!!! + osl::MutexGuard aGuard( m_aMutex ); + m_pOleComp = nullptr; +} + +STDMETHODIMP OleWrapperClientSite::SaveObject() +{ + OleComponent* pLockComponent = nullptr; + HRESULT hResult = E_FAIL; + + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + if ( pLockComponent->SaveObject_Impl() ) + hResult = S_OK; + + pLockComponent->release(); + } + + return hResult; +} + +STDMETHODIMP OleWrapperClientSite::GetMoniker( DWORD, DWORD, IMoniker **ppmk ) +{ + *ppmk = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::GetContainer( IOleContainer** ppContainer ) +{ + *ppContainer = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP OleWrapperClientSite::ShowObject() +{ + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::OnShowWindow( BOOL bShow ) +{ + OleComponent* pLockComponent = nullptr; + + // TODO/LATER: redirect the notification to the main thread so that SolarMutex can be locked + { + osl::MutexGuard aGuard( m_aMutex ); + if ( m_pOleComp ) + { + pLockComponent = m_pOleComp; + pLockComponent->acquire(); + } + } + + if ( pLockComponent ) + { + pLockComponent->OnShowWindow_Impl( bShow ); // the result is not interesting + pLockComponent->release(); + } + + return S_OK; +} + +STDMETHODIMP OleWrapperClientSite::RequestNewObjectLayout() +{ + return E_NOTIMPL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/olewrapclient.hxx b/embeddedobj/source/msole/olewrapclient.hxx new file mode 100644 index 000000000..99f4e16ea --- /dev/null +++ b/embeddedobj/source/msole/olewrapclient.hxx @@ -0,0 +1,51 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include "platform.h" + +class OleComponent; +class OleWrapperClientSite : public IOleClientSite +{ + osl::Mutex m_aMutex; + oslInterlockedCount m_nRefCount; + OleComponent* m_pOleComp; + +public: + explicit OleWrapperClientSite(OleComponent* pOleComp); + virtual ~OleWrapperClientSite(); + + void disconnectOleComponent(); + + STDMETHODIMP QueryInterface(REFIID, void**) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + STDMETHODIMP SaveObject() override; + STDMETHODIMP GetMoniker(DWORD, DWORD, IMoniker**) override; + STDMETHODIMP GetContainer(IOleContainer**) override; + STDMETHODIMP ShowObject() override; + STDMETHODIMP OnShowWindow(BOOL) override; + STDMETHODIMP RequestNewObjectLayout() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.cxx b/embeddedobj/source/msole/ownview.cxx new file mode 100644 index 000000000..26f74f039 --- /dev/null +++ b/embeddedobj/source/msole/ownview.cxx @@ -0,0 +1,609 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <tools/diagnose_ex.h> + +#include "olepersist.hxx" +#include "ownview.hxx" + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +namespace { + +class DummyHandler_Impl : public ::cppu::WeakImplHelper< task::XInteractionHandler > +{ +public: + DummyHandler_Impl() {} + + virtual void SAL_CALL handle( const uno::Reference< task::XInteractionRequest >& xRequest ) override; +}; + +} + +void SAL_CALL DummyHandler_Impl::handle( const uno::Reference< task::XInteractionRequest >& ) +{ +} + + +// Object viewer + + +OwnView_Impl::OwnView_Impl( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XInputStream >& xInputStream ) +: m_xContext( xContext ) +, m_bBusy( false ) +, m_bUseNative( false ) +{ + if ( !xContext.is() || !xInputStream.is() ) + throw uno::RuntimeException(); + + m_aTempFileURL = GetNewFilledTempFile_Impl( xInputStream, m_xContext ); +} + + +OwnView_Impl::~OwnView_Impl() +{ + try { + KillFile_Impl( m_aTempFileURL, m_xContext ); + } catch( uno::Exception& ) {} + + try { + if ( !m_aNativeTempURL.isEmpty() ) + KillFile_Impl( m_aNativeTempURL, m_xContext ); + } catch( uno::Exception& ) {} +} + + +bool OwnView_Impl::CreateModelFromURL( const OUString& aFileURL ) +{ + bool bResult = false; + + if ( !aFileURL.isEmpty() ) + { + try { + uno::Reference < frame::XDesktop2 > xDocumentLoader = frame::Desktop::create(m_xContext); + + uno::Sequence< beans::PropertyValue > aArgs( m_aFilterName.isEmpty() ? 4 : 5 ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + pArgs[0].Value <<= aFileURL; + + pArgs[1].Name = "ReadOnly"; + pArgs[1].Value <<= true; + + pArgs[2].Name = "InteractionHandler"; + pArgs[2].Value <<= uno::Reference< task::XInteractionHandler >( new DummyHandler_Impl() ); + + pArgs[3].Name = "DontEdit"; + pArgs[3].Value <<= true; + + if ( !m_aFilterName.isEmpty() ) + { + pArgs[4].Name = "FilterName"; + pArgs[4].Value <<= m_aFilterName; + } + + uno::Reference< frame::XModel > xModel( xDocumentLoader->loadComponentFromURL( + aFileURL, + "_blank", + 0, + aArgs ), + uno::UNO_QUERY ); + + if ( xModel.is() ) + { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->addEventListener( uno::Reference< document::XEventListener >(this) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener( uno::Reference< util::XCloseListener >(this) ); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_xModel = xModel; + bResult = true; + } + } + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:"); + } + } + + return bResult; +} + + +bool OwnView_Impl::CreateModel( bool bUseNative ) +{ + bool bResult = false; + + try { + bResult = CreateModelFromURL( bUseNative ? m_aNativeTempURL : m_aTempFileURL ); + } + catch( uno::Exception& ) + { + } + + return bResult; +} + + +OUString OwnView_Impl::GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const uno::Reference< io::XInputStream >& xInputStream ) +{ + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< document::XTypeDetection > xTypeDetection( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + uno::UNO_QUERY_THROW ); + + OUString aTypeName; + + if ( !aNameWithExtention.empty() ) + { + OUString aURLToAnalyze = OUString::Concat("file:///") + aNameWithExtention; + aTypeName = xTypeDetection->queryTypeByURL( aURLToAnalyze ); + } + + uno::Sequence< beans::PropertyValue > aArgs( aTypeName.isEmpty() ? 2 : 3 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "URL"; + pArgs[0].Value <<= OUString( "private:stream" ); + pArgs[1].Name = "InputStream"; + pArgs[1].Value <<= xInputStream; + if ( !aTypeName.isEmpty() ) + { + pArgs[2].Name = "TypeName"; + pArgs[2].Value <<= aTypeName; + } + + aTypeName = xTypeDetection->queryTypeByDescriptor( aArgs, true ); + + OUString aFilterName; + for ( beans::PropertyValue const & prop : std::as_const(aArgs) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( aFilterName.isEmpty() && !aTypeName.isEmpty() ) + { + // get the default filter name for the type + uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aTypes; + + if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) ) + { + for ( beans::PropertyValue const & prop : std::as_const(aTypes) ) + { + if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) ) + { + prop.Value >>= aFilterName; + break; + } + } + } + } + + return aFilterName; +} + + +bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference< io::XInputStream >& xInStream, + bool bParseHeader ) +{ + uno::Reference< io::XSeekable > xSeekable( xInStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + + // create m_aNativeTempURL + OUString aNativeTempURL; + uno::Reference < io::XTempFile > xNativeTempFile( + io::TempFile::create(m_xContext), + uno::UNO_SET_THROW ); + uno::Reference < io::XOutputStream > xNativeOutTemp = xNativeTempFile->getOutputStream(); + uno::Reference < io::XInputStream > xNativeInTemp = xNativeTempFile->getInputStream(); + if ( !xNativeOutTemp.is() || !xNativeInTemp.is() ) + throw uno::RuntimeException(); + + try { + xNativeTempFile->setRemoveFile( false ); + aNativeTempURL = xNativeTempFile->getUri(); + } + catch ( uno::Exception& ) + { + } + + bool bFailed = false; + OUString aFileSuffix; + + if ( bParseHeader ) + { + uno::Sequence< sal_Int8 > aReadSeq( 4 ); + // read the complete size of the Object Package + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + // read the first header ( have no idea what does this header mean ) + if ( xInStream->readBytes( aReadSeq, 2 ) != 2 || aReadSeq[0] != 2 || aReadSeq[1] != 0 ) + return false; + + // read file name + // only extension is interesting so only subset of symbols is accepted + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + + if ( + (aReadSeq[0] >= '0' && aReadSeq[0] <= '9') || + (aReadSeq[0] >= 'a' && aReadSeq[0] <= 'z') || + (aReadSeq[0] >= 'A' && aReadSeq[0] <= 'Z') || + aReadSeq[0] == '.' + ) + { + aFileSuffix += OUStringChar( sal_Unicode(aReadSeq[0]) ); + } + + } while( aReadSeq[0] ); + + // skip url + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + } while( aReadSeq[0] ); + + // check the next header + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 + || aReadSeq[0] || aReadSeq[1] || aReadSeq[2] != 3 || aReadSeq[3] ) + return false; + + // get the size of the next entry + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nUrlSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + sal_Int64 nTargetPos = xSeekable->getPosition() + nUrlSize; + + xSeekable->seek( nTargetPos ); + + // get the size of stored data + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nDataSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + + aReadSeq.realloc( 32000 ); + sal_uInt32 nRead = 0; + while ( nRead < nDataSize ) + { + sal_uInt32 nToRead = std::min<sal_uInt32>( nDataSize - nRead, 32000 ); + sal_uInt32 nLocalRead = xInStream->readBytes( aReadSeq, nToRead ); + + + if ( !nLocalRead ) + { + bFailed = true; + break; + } + else if ( nLocalRead == 32000 ) + xNativeOutTemp->writeBytes( aReadSeq ); + else + { + uno::Sequence< sal_Int8 > aToWrite( aReadSeq ); + aToWrite.realloc( nLocalRead ); + xNativeOutTemp->writeBytes( aToWrite ); + } + + nRead += nLocalRead; + } + } + else + { + uno::Sequence< sal_Int8 > aData( 8 ); + if ( xInStream->readBytes( aData, 8 ) == 8 + && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 + && ( aData[4] == 2 || aData[4] == 3 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + // the header has to be removed + xSeekable->seek( 40 ); + } + else + { + // the usual Ole10Native format + xSeekable->seek( 4 ); + } + + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xNativeOutTemp ); + } + + xNativeOutTemp->closeOutput(); + + // The temporary native file is created, now the filter must be detected + if ( !bFailed ) + { + m_aFilterName = GetFilterNameFromExtentionAndInStream( m_xContext, aFileSuffix, xNativeInTemp ); + m_aNativeTempURL = aNativeTempURL; + } + + return !bFailed; +} + + +void OwnView_Impl::CreateNative() +{ + if ( !m_aNativeTempURL.isEmpty() ) + return; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xInStream = xAccess->openFileRead( m_aTempFileURL ); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xInStream) }; + uno::Reference< container::XNameAccess > xNameAccess( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + OUString aSubStreamName = "\1Ole10Native"; + uno::Reference< embed::XClassifiedObject > xStor( xNameAccess, uno::UNO_QUERY_THROW ); + uno::Sequence< sal_Int8 > aStorClassID = xStor->getClassID(); + + if ( xNameAccess->hasByName( aSubStreamName ) ) + { + sal_uInt8 const aClassID[] = + { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence< sal_Int8 > aPackageClassID( reinterpret_cast<sal_Int8 const *>(aClassID), 16 ); + + uno::Reference< io::XStream > xSubStream; + xNameAccess->getByName( aSubStreamName ) >>= xSubStream; + if ( xSubStream.is() ) + { + bool bOk = false; + + if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID, aStorClassID ) ) + { + // the storage represents Object Package + + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), true ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + + if ( !bOk ) + { + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), false ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + } + } + else + { + // TODO/LATER: No native stream, needs a new solution + } + } + catch( uno::Exception& ) + {} +} + + +bool OwnView_Impl::Open() +{ + bool bResult = false; + + uno::Reference< frame::XModel > xExistingModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + xExistingModel = m_xModel; + if ( m_bBusy ) + return false; + + m_bBusy = true; + } + + if ( xExistingModel.is() ) + { + try { + uno::Reference< frame::XController > xController = xExistingModel->getCurrentController(); + if ( xController.is() ) + { + uno::Reference< frame::XFrame > xFrame = xController->getFrame(); + if ( xFrame.is() ) + { + xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( xFrame->getContainerWindow(), uno::UNO_QUERY ); + if(xTopWindow.is()) + xTopWindow->toFront(); + + bResult = true; + } + } + } + catch( uno::Exception& ) + { + } + } + else + { + bResult = CreateModel( m_bUseNative ); + + if ( !bResult && !m_bUseNative ) + { + // the original storage can not be recognized + if ( m_aNativeTempURL.isEmpty() ) + { + // create a temporary file for the native representation if there is no + CreateNative(); + } + + if ( !m_aNativeTempURL.isEmpty() ) + { + bResult = CreateModel( true ); + if ( bResult ) + m_bUseNative = true; + } + } + } + + m_bBusy = false; + + return bResult; +} + + +void OwnView_Impl::Close() +{ + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xModel.is() ) + return; + xModel = m_xModel; + m_xModel.clear(); + + if ( m_bBusy ) + return; + + m_bBusy = true; + } + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + xCloseable->close( true ); + } + } + catch( uno::Exception& ) + {} + + m_bBusy = false; +} + + +void SAL_CALL OwnView_Impl::notifyEvent( const document::EventObject& aEvent ) +{ + + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( aEvent.Source == m_xModel && aEvent.EventName == "OnSaveAsDone" ) + { + // SaveAs operation took place, so just forget the model and deregister listeners + xModel = m_xModel; + m_xModel.clear(); + } + } + + if ( !xModel.is() ) + return; + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + } + catch( uno::Exception& ) + {} +} + + +void SAL_CALL OwnView_Impl::queryClosing( const lang::EventObject&, sal_Bool ) +{ +} + + +void SAL_CALL OwnView_Impl::notifyClosing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +} + + +void SAL_CALL OwnView_Impl::disposing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/ownview.hxx b/embeddedobj/source/msole/ownview.hxx new file mode 100644 index 000000000..d939aff86 --- /dev/null +++ b/embeddedobj/source/msole/ownview.hxx @@ -0,0 +1,83 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +#include <osl/mutex.hxx> + +class OwnView_Impl : public ::cppu::WeakImplHelper < css::util::XCloseListener, + css::document::XEventListener > +{ + ::osl::Mutex m_aMutex; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XModel > m_xModel; + + OUString m_aTempFileURL; + OUString m_aNativeTempURL; + + OUString m_aFilterName; + + bool m_bBusy; + + bool m_bUseNative; + +private: + bool CreateModelFromURL( const OUString& aFileURL ); + + bool CreateModel( bool bUseNative ); + + bool ReadContentsAndGenerateTempFile( const css::uno::Reference< css::io::XInputStream >& xStream, bool bParseHeader ); + + void CreateNative(); + +public: + static OUString GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const css::uno::Reference< css::io::XInputStream >& xInputStream ); + + OwnView_Impl( const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XInputStream >& xStream ); + virtual ~OwnView_Impl() override; + + bool Open(); + + void Close(); + + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/platform.h b/embeddedobj/source/msole/platform.h new file mode 100644 index 000000000..68127b662 --- /dev/null +++ b/embeddedobj/source/msole/platform.h @@ -0,0 +1,31 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#define STRICT +#define _ATL_APARTMENT_THREADED + +#if !defined WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <ole2.h> + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.cxx b/embeddedobj/source/msole/xdialogcreator.cxx new file mode 100644 index 000000000..7e4e99156 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.cxx @@ -0,0 +1,356 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/embed/OLEEmbeddedObjectFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/task/XStatusIndicatorFactory.hpp> + +#include <osl/thread.h> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <comphelper/classids.hxx> + +#include "platform.h" +#include <comphelper/mimeconfighelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include "xdialogcreator.hxx" +#include <oleembobj.hxx> + + +#ifdef _WIN32 + +#include <oledlg.h> +#include <vcl/winscheduler.hxx> + +namespace { + +class InitializedOleGuard +{ +public: + InitializedOleGuard() + { + if ( !SUCCEEDED( OleInitialize( nullptr ) ) ) + throw css::uno::RuntimeException(); + } + + ~InitializedOleGuard() + { + OleUninitialize(); + } +}; + +} + +extern "C" { +typedef UINT STDAPICALLTYPE OleUIInsertObjectA_Type(LPOLEUIINSERTOBJECTA); +} + +#endif + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +static uno::Sequence< sal_Int8 > GetRelatedInternalID_Impl( const uno::Sequence< sal_Int8 >& aClassID ) +{ + // Writer + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SW_CLASSID_60 ); + + // Calc + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SC_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SC_CLASSID_60 ); + + // Impress + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SIMPRESS_CLASSID_60 ); + + // Draw + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SDRAW_CLASSID_60 ); + + // Chart + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SCH_CLASSID_60 ); + + // Math + if ( MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_60 ) ) + || MimeConfigurationHelper::ClassIDsEqual( aClassID, MimeConfigurationHelper::GetSequenceClassID( SO3_SM_OLE_EMBED_CLASSID_8 ) ) ) + return MimeConfigurationHelper::GetSequenceClassID( SO3_SM_CLASSID_60 ); + + return aClassID; +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceByDialog( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aInObjArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + uno::Sequence< beans::PropertyValue > aObjArgs( aInObjArgs ); + +#ifdef _WIN32 + + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + InitializedOleGuard aGuard; + + OLEUIINSERTOBJECT io = {}; + char szFile[MAX_PATH]; + UINT uTemp; + + io.cbStruct = sizeof(io); + io.hWndOwner = GetActiveWindow(); + + szFile[0] = 0; + io.lpszFile = szFile; + io.cchFile = MAX_PATH; + + io.dwFlags = IOF_SELECTCREATENEW | IOF_DISABLELINK; + + + ::osl::Module aOleDlgLib; + if( !aOleDlgLib.load( "oledlg" )) + throw uno::RuntimeException(); + + OleUIInsertObjectA_Type * pInsertFct = reinterpret_cast<OleUIInsertObjectA_Type *>( + aOleDlgLib.getSymbol( "OleUIInsertObjectA" )); + if( !pInsertFct ) + throw uno::RuntimeException(); + + // Disable any event loop shortcuts by enabling a real timer. + // This way the native windows dialog won't block our own processing. + WinScheduler::SetForceRealTimer(); + + uTemp=pInsertFct(&io); + + if ( OLEUI_OK != uTemp ) + throw ucb::CommandAbortedException(); + + if (io.dwFlags & IOF_SELECTCREATENEW) + { + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + + uno::Sequence< sal_Int8 > aClassID = MimeConfigurationHelper::GetSequenceClassID( io.clsid.Data1, + io.clsid.Data2, + io.clsid.Data3, + io.clsid.Data4[0], + io.clsid.Data4[1], + io.clsid.Data4[2], + io.clsid.Data4[3], + io.clsid.Data4[4], + io.clsid.Data4[5], + io.clsid.Data4[6], + io.clsid.Data4[7] ); + + aClassID = GetRelatedInternalID_Impl( aClassID ); + + //TODO: retrieve ClassName + aObjectInfo.Object.set( xEmbCreator->createInstanceInitNew( aClassID, OUString(), xStorage, sEntName, aObjArgs ), + uno::UNO_QUERY ); + } + else + { + OUString aFileName + = OStringToOUString( std::string_view( szFile ), osl_getThreadTextEncoding() ); + OUString aFileURL; + if ( osl::FileBase::getFileURLFromSystemPath( aFileName, aFileURL ) != osl::FileBase::E_None ) + throw uno::RuntimeException(); + + uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue("URL", + aFileURL) }; + + // TODO: use config helper for type detection + uno::Reference< embed::XEmbeddedObjectCreator > xEmbCreator; + ::comphelper::MimeConfigurationHelper aHelper( m_xContext ); + + if ( aHelper.AddFilterNameCheckOwnFile( aMediaDescr ) ) + xEmbCreator = embed::EmbeddedObjectCreator::create( m_xContext ); + else + xEmbCreator = embed::OLEEmbeddedObjectFactory::create( m_xContext ); + + if ( !xEmbCreator.is() ) + throw uno::RuntimeException(); + + uno::Reference<task::XStatusIndicator> xProgress; + OUString aProgressText; + comphelper::SequenceAsHashMap aMap(aInObjArgs); + auto it = aMap.find("StatusIndicator"); + if (it != aMap.end()) + { + it->second >>= xProgress; + } + it = aMap.find("StatusIndicatorText"); + if (it != aMap.end()) + { + it->second >>= aProgressText; + } + if (xProgress.is()) + { + xProgress->start(aProgressText, 100); + } + + aObjectInfo.Object.set( xEmbCreator->createInstanceInitFromMediaDescriptor( xStorage, sEntName, aMediaDescr, aObjArgs ), + uno::UNO_QUERY ); + + if (xProgress.is()) + { + xProgress->end(); + } + } + + if ( ( io.dwFlags & IOF_CHECKDISPLAYASICON) && io.hMetaPict != nullptr ) + { + METAFILEPICT* pMF = static_cast<METAFILEPICT*>(GlobalLock( io.hMetaPict )); + if ( pMF ) + { + sal_uInt32 nBufSize = GetMetaFileBitsEx( pMF->hMF, 0, nullptr ); + uno::Sequence< sal_Int8 > aMetafile( nBufSize + 22 ); + sal_Int8* pBuf = aMetafile.getArray(); + *reinterpret_cast<long*>( pBuf ) = 0x9ac6cdd7L; + *reinterpret_cast<short*>( pBuf+6 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+8 ) = SHORT(0); + *reinterpret_cast<short*>( pBuf+10 ) = static_cast<SHORT>(pMF->xExt); + *reinterpret_cast<short*>( pBuf+12 ) = static_cast<SHORT>(pMF->yExt); + *reinterpret_cast<short*>( pBuf+14 ) = USHORT(2540); + + if ( nBufSize && nBufSize == GetMetaFileBitsEx( pMF->hMF, nBufSize, pBuf+22 ) ) + { + datatransfer::DataFlavor aFlavor( + "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", + "Image WMF", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + aObjectInfo.Options = { { "Icon", css::uno::Any(aMetafile) }, + { "IconFormat", css::uno::Any(aFlavor) } }; + } + + GlobalUnlock( io.hMetaPict ); + } + } + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +embed::InsertedObjectInfo SAL_CALL MSOLEDialogObjectCreator::createInstanceInitFromClipboard( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntryName, + const uno::Sequence< beans::PropertyValue >& aObjectArgs ) +{ + embed::InsertedObjectInfo aObjectInfo; + +#ifdef _WIN32 + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( !sEntryName.getLength() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< embed::XEmbeddedObject > xResult( + new OleEmbeddedObject( m_xContext ) ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntryName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + aObjectArgs ); + + aObjectInfo.Object = xResult; + + // TODO/LATER: in case of iconify object the icon should be stored in aObjectInfo + + OSL_ENSURE( aObjectInfo.Object.is(), "No object was created!" ); + if ( !aObjectInfo.Object.is() ) + throw uno::RuntimeException(); + + return aObjectInfo; +#else + throw lang::NoSupportException(); // TODO: +#endif +} + + +OUString SAL_CALL MSOLEDialogObjectCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.MSOLEObjectSystemCreator"; +} + + +sal_Bool SAL_CALL MSOLEDialogObjectCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL MSOLEDialogObjectCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.MSOLEObjectSystemCreator", + "com.sun.star.comp.embed.MSOLEObjectSystemCreator" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_MSOLEDialogObjectCreator_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MSOLEDialogObjectCreator(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xdialogcreator.hxx b/embeddedobj/source/msole/xdialogcreator.hxx new file mode 100644 index 000000000..a7c28df80 --- /dev/null +++ b/embeddedobj/source/msole/xdialogcreator.hxx @@ -0,0 +1,59 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XInsertObjectDialog.hpp> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class MSOLEDialogObjectCreator : public ::cppu::WeakImplHelper< + css::embed::XInsertObjectDialog, + css::embed::XEmbedObjectClipboardCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit MSOLEDialogObjectCreator( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XInsertObjectDialog + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceByDialog( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectClipboardCreator + virtual css::embed::InsertedObjectInfo SAL_CALL createInstanceInitFromClipboard( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntryName, const css::uno::Sequence< css::beans::PropertyValue >& aObjectArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xolefactory.cxx b/embeddedobj/source/msole/xolefactory.cxx new file mode 100644 index 000000000..1a7728ef8 --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.cxx @@ -0,0 +1,251 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EntryInitModes.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "xolefactory.hxx" +#include <oleembobj.hxx> + +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> + +using namespace ::com::sun::star; + +// TODO: do not create OLE objects that represent OOo documents + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitFromEntry( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMedDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< container::XNameAccess > xNameAccess( xStorage, uno::UNO_QUERY_THROW ); + + // detect entry existence + if ( !xNameAccess->hasByName( sEntName ) ) + throw container::NoSuchElementException(); + + if ( !xStorage->isStreamElement( sEntName ) ) + { + // if it is not an OLE object throw an exception + throw io::IOException(); // TODO: + } + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + aMedDescr, + lObjArgs ); + + for ( beans::PropertyValue const & prop : lObjArgs ) + { + if ( prop.Name == "CloneFrom" ) + { + try + { + uno::Reference < embed::XEmbeddedObject > xObj; + uno::Reference < embed::XEmbeddedObject > xNew( xResult, uno::UNO_QUERY ); + prop.Value >>= xObj; + if ( xObj.is() ) + xNew->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); + } + catch ( const uno::Exception& ) {} + break; + } + } + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitFromMediaDescriptor( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, false ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceInitNew( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 3 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 4 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::TRUNCATE_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceLink( + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + const uno::Sequence< beans::PropertyValue >& aMediaDescr, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, true ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::MEDIA_DESCRIPTOR_INIT, + aMediaDescr, + lObjArgs ); + + return xResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL OleEmbeddedObjectFactory::createInstanceUserInit( + const uno::Sequence< sal_Int8 >& aClassID, + const OUString& aClassName, + const uno::Reference< embed::XStorage >& xStorage, + const OUString& sEntName, + sal_Int32 /*nEntryConnectionMode*/, + const uno::Sequence< beans::PropertyValue >& /*lArguments*/, + const uno::Sequence< beans::PropertyValue >& lObjArgs ) +{ + // the initialization is completely controlled by user + if ( !xStorage.is() ) + throw lang::IllegalArgumentException( "No parent storage is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 1 ); + + if ( sEntName.isEmpty() ) + throw lang::IllegalArgumentException( "Empty element name is provided!", + static_cast< ::cppu::OWeakObject* >(this), + 2 ); + + uno::Reference< uno::XInterface > xResult( + static_cast< ::cppu::OWeakObject* > ( new OleEmbeddedObject( m_xContext, aClassID, aClassName ) ), + uno::UNO_QUERY ); + + uno::Reference< embed::XEmbedPersist > xPersist( xResult, uno::UNO_QUERY_THROW ); + xPersist->setPersistentEntry( xStorage, + sEntName, + embed::EntryInitModes::DEFAULT_INIT, + uno::Sequence< beans::PropertyValue >(), + lObjArgs ); + + return xResult; +} + + +OUString SAL_CALL OleEmbeddedObjectFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.OLEEmbeddedObjectFactory"; +} + +sal_Bool SAL_CALL OleEmbeddedObjectFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL OleEmbeddedObjectFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.OLEEmbeddedObjectFactory", + "com.sun.star.comp.embed.OLEEmbeddedObjectFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embeddedobj_OleEmbeddedObjectFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OleEmbeddedObjectFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/source/msole/xolefactory.hxx b/embeddedobj/source/msole/xolefactory.hxx new file mode 100644 index 000000000..56e632202 --- /dev/null +++ b/embeddedobj/source/msole/xolefactory.hxx @@ -0,0 +1,62 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/embed/XEmbeddedObjectCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + + +class OleEmbeddedObjectFactory : public ::cppu::WeakImplHelper< + css::embed::XEmbeddedObjectCreator, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit OleEmbeddedObjectFactory( + const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XEmbedObjectCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitNew( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromEntry( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMedDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceInitFromMediaDescriptor( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XEmbedObjectFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceUserInit( const css::uno::Sequence< sal_Int8 >& aClassID, const OUString& aClassName, const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, sal_Int32 nEntryConnectionMode, const css::uno::Sequence< css::beans::PropertyValue >& lArguments, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XLinkCreator + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceLink( const css::uno::Reference< css::embed::XStorage >& xStorage, const OUString& sEntName, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescr, const css::uno::Sequence< css::beans::PropertyValue >& lObjArgs ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/Container1/BitmapPainter.java b/embeddedobj/test/Container1/BitmapPainter.java new file mode 100644 index 000000000..759536d5f --- /dev/null +++ b/embeddedobj/test/Container1/BitmapPainter.java @@ -0,0 +1,281 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package embeddedobj.test; + +import java.awt.*; +import java.applet.*; +import java.awt.event.*; +import java.net.*; +import java.io.*; + +import com.sun.star.awt.XBitmap; +import com.sun.star.awt.XDevice; +import com.sun.star.awt.XDisplayBitmap; +import com.sun.star.awt.XGraphics; +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XWindowPeer; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XSystemChildFactory; +import com.sun.star.awt.WindowDescriptor; +import com.sun.star.awt.WindowClass; +import com.sun.star.awt.WindowAttribute; + +import com.sun.star.awt.XPaintListener; +import com.sun.star.awt.PaintEvent; +import com.sun.star.awt.XMouseListener; +import com.sun.star.awt.XMouseMotionListener; +import com.sun.star.awt.MouseEvent; +import com.sun.star.awt.Point; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Any; +import com.sun.star.lang.XMultiServiceFactory; + +import com.sun.star.task.XJob; +import com.sun.star.beans.NamedValue; + + +class BitmapPainter implements XPaintListener, XMouseListener, XMouseMotionListener, XJob +{ + private XWindow m_xWindow; + private XBitmap m_xBitmap; + + private com.sun.star.awt.Rectangle m_aDrawRect; + + private Object m_oImageLock; + + private PaintThread m_aPaintThread; + + private boolean m_bFree = true; + + private boolean m_bProceedWithPainting = true; + +// Methods + + public BitmapPainter( XJob xJob, XWindow xWindow, XBitmap xBitmap, com.sun.star.awt.Rectangle aDrawRect ) + { + if ( xJob == null ) + { + System.out.println( "No mainthreadexecutor is provided to BitmapPainter on init!" ); + throw new com.sun.star.uno.RuntimeException(); + } + + if ( xWindow == null ) + { + System.out.println( "No window is provided to BitmapPainter on init!" ); + throw new com.sun.star.uno.RuntimeException(); + } + + m_xWindow = xWindow; + m_xBitmap = xBitmap; + + m_aDrawRect = aDrawRect; + + m_oImageLock = new Object(); + + m_aPaintThread = new PaintThread( m_xWindow ); + m_aPaintThread.start(); + + m_xWindow.addPaintListener( this ); + m_xWindow.addMouseListener( this ); + m_xWindow.addMouseMotionListener( this ); + } + + + public void disconnectListener() + { + m_aPaintThread.disposeThread(); + m_xWindow.removePaintListener( this ); + m_xWindow.removeMouseListener( this ); + m_xWindow.removeMouseMotionListener( this ); + } + + + public void setBitmap( XBitmap xBitmap ) + { + synchronized( m_oImageLock ) + { + m_xBitmap = xBitmap; + } + } + + + public void setPos( com.sun.star.awt.Point aPoint ) + { + synchronized( m_oImageLock ) + { + m_aDrawRect.X = aPoint.X; + m_aDrawRect.Y = aPoint.Y; + } + } + + + public void setRect( com.sun.star.awt.Rectangle aRect ) + { + synchronized( m_oImageLock ) + { + m_aDrawRect = aRect; + } + } + + + public void setSize( com.sun.star.awt.Size aSize ) + { + synchronized( m_oImageLock ) + { + m_aDrawRect.Width = aSize.Width; + m_aDrawRect.Height = aSize.Height; + } + } + + + public void stopPainting() + { + m_bProceedWithPainting = false; + } + + + public void startPainting() + { + m_bProceedWithPainting = true; + } + + // XPaintListener + + public void windowPaint( PaintEvent e ) + { + if ( !m_bProceedWithPainting ) + return; + + XBitmap xBitmap = null; + com.sun.star.awt.Rectangle aRect = null; + + synchronized( m_oImageLock ) + { + xBitmap = m_xBitmap; + aRect = m_aDrawRect; + } + + m_aPaintThread.setPaintRequest( xBitmap, aRect, e.UpdateRect ); + + System.out.println( "VCL window paint event!" ); + } + + // XMouseListener + + public void mousePressed( MouseEvent e ) + { + } + + + public void mouseReleased( MouseEvent e ) + { + } + + + public void mouseEntered( MouseEvent e ) + { + } + + + public void mouseExited( MouseEvent e ) + { + } + + // XMouseMotionListener + + public void mouseDragged( MouseEvent e ) + { + // TODO: react to resizing of object bitmap + // if the object is inplace active the object must control resizing + } + + + public void mouseMoved( MouseEvent e ) + { + + } + + // XEventListener + + public void disposing( com.sun.star.lang.EventObject e ) + { + // do nothing, the window can die only when the application is closed + } + + // XJob + + public Object execute( NamedValue[] pValues ) + { +/* + // means request for painting + + XBitmap xBitmap = null; + com.sun.star.awt.Rectangle aRect = null; + + synchronized( m_oImageLock ) + { + xBitmap = m_xBitmap; + aRect = m_aDrawRect; + } + + System.out.println( "The bitmap is going to be painted!" ); + + try { + XDevice xDevice = (XDevice)UnoRuntime.queryInterface( XDevice.class, m_xWindow ); + if ( xDevice != null ) + { + System.out.println( "Step1" ); + XGraphics xGraphics = xDevice.createGraphics(); + if ( xBitmap != null ) + { + System.out.println( "Step2" ); + XDisplayBitmap xDisplayBitmap = xDevice.createDisplayBitmap( xBitmap ); + + com.sun.star.awt.Size aSize = xBitmap.getSize(); + xGraphics.draw( xDisplayBitmap, 0, 0, aSize.Width, aSize.Height, + aRect.X, aRect.Y, aRect.Width, aRect.Height ); + } + + System.out.println( "Step3" ); + xGraphics.drawRect( aRect.X - 1, aRect.Y - 1, aRect.Width + 2, aRect.Height + 2 ); + + // draw resize squares + System.out.println( "Step4" ); + xGraphics.drawRect( aRect.X - 2, aRect.Y - 2, 4, 4 ); + xGraphics.drawRect( aRect.X + aRect.Width - 2, aRect.Y - 2, 4, 4 ); + xGraphics.drawRect( aRect.X - 2, aRect.Y + aRect.Height - 2, 4, 4 ); + xGraphics.drawRect( aRect.X + aRect.Width - 2, aRect.Y + aRect.Height - 2, 4, 4 ); + + System.out.println( "Step5" ); + + System.out.println( "The bitmap is painted by BitmapPainter!" ); + } + } + catch ( Exception e ) + { + } + + m_bFree = true; + +*/ + return Any.VOID; + } + +}; + diff --git a/embeddedobj/test/Container1/EmbedContApp.java b/embeddedobj/test/Container1/EmbedContApp.java new file mode 100644 index 000000000..14f5fa83e --- /dev/null +++ b/embeddedobj/test/Container1/EmbedContApp.java @@ -0,0 +1,1681 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package embeddedobj.test; + +import java.awt.*; +import java.applet.*; +import java.awt.event.*; +import java.net.*; +import java.io.*; +import java.util.Vector; + +import javax.swing.JOptionPane; +import javax.swing.Timer; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.Any; + +import com.sun.star.lang.XComponent; + +import com.sun.star.util.XCloseable; +import com.sun.star.util.XURLTransformer; +import com.sun.star.util.URL; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.NamedValue; + +import com.sun.star.datatransfer.DataFlavor; +import com.sun.star.datatransfer.XTransferable; + +import com.sun.star.container.XNameAccess; + +import com.sun.star.io.XStream; +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XTruncate; + +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XBitmap; + +import com.sun.star.task.XJob; + +import com.sun.star.embed.*; + + +class ActionObject +{ + public byte m_nID; + public String m_sParam; + + public ActionObject() + { + m_nID = 0; + m_sParam = null; + } + + public ActionObject( byte nID ) + { + m_nID = nID; + m_sParam = null; + } + + public ActionObject( byte nID, String sParam ) + { + m_nID = nID; + m_sParam = sParam; + } + + public ActionObject( ActionObject aObject ) + { + m_nID = aObject.m_nID; + m_sParam = aObject.m_sParam; + } +}; + +public class EmbedContApp extends Applet + implements MouseListener, XEmbeddedClient, ActionListener, XJob, XInplaceClient, XWindowSupplier +{ + private XMultiServiceFactory m_xServiceFactory; + + private final boolean m_bStoreVisRepl = false; + + private XJob m_xMainThreadExecutor; + private NamedValue[] m_pValuesForExecutor; + + private XEmbeddedObject m_xEmbedObj; + private XStorage m_xStorage; + private float m_nXScaling; + private float m_nYScaling; + private float m_nXPixelSize; + private float m_nYPixelSize; + + private Frame m_aFrame; + private Menu m_aFileMenu; + private Menu m_aObjectMenu; + private Toolkit m_aToolkit; + + private Image m_aImage; + private Object m_oImageLock; + + private boolean m_bOwnFile = false; + + private boolean m_bLinkObj = false; + private String m_aLinkURI; + + private Object m_oActionsNumberLock; + + private Timer m_aTimer; + private boolean m_bDestroyed = false; + + private Object m_oInHandlerLock; + private boolean m_bInHandler = false; + + private XURLTransformer m_xTransformer; + + private NativeView m_aNativeView; + private XWindow m_xVCLWindow; + + private XBitmap m_xBitmap; + private BitmapPainter m_aBitmapPainter; + +// Constants + private final byte DESTROY = 1; + private final byte ACTIVATE_OUTPLACE = 2; + private final byte NEW_DOCUMENT = 3; + private final byte SAVE_AS = 4; + private final byte OPEN_FILE = 5; + private final byte SAVE = 6; + private final byte NEW_OBJECT = 7; + private final byte OBJECT_FROM_FILE = 8; + private final byte LINK_FROM_FILE = 9; + private final byte CONVERT_LINK_TO_OBJECT = 10; + private final byte ACTIVATE_INPLACE = 11; + private final byte DEACTIVATE = 12; + +// Methods + public EmbedContApp( Frame aFrame, XMultiServiceFactory xServiceFactory ) + { + m_aFrame = aFrame; + m_xServiceFactory = xServiceFactory; + } + + public void init() + { + resize( 800, 600 ); + setBackground( Color.gray ); + + m_aToolkit = Toolkit.getDefaultToolkit(); + + try { + Object oTransformer = m_xServiceFactory.createInstance( "com.sun.star.util.URLTransformer" ); + m_xTransformer = (XURLTransformer)UnoRuntime.queryInterface( XURLTransformer.class, oTransformer ); + } catch( Exception e ) { System.exit( 0 ); } + + m_oActionsNumberLock = new Object(); + m_aActionsList = new ArrayList(); + + m_oInHandlerLock = new Object(); + m_oImageLock = new Object(); + + try { + Object oJob = m_xServiceFactory.createInstance( "com.sun.star.comp.thread.MainThreadExecutor" ); + m_xMainThreadExecutor = (XJob)UnoRuntime.queryInterface( XJob.class, oJob ); + } catch( Exception e ) {} + + if ( m_xMainThreadExecutor == null ) + { + System.out.println( "Can't create MainThreadExecutor! The application is unusable!" ); + System.exit( 0 ); + } + + m_nXScaling = 1; + m_nYScaling = 1; + m_nXPixelSize = 1; + m_nYPixelSize = 1; + + m_pValuesForExecutor = new NamedValue[1]; + m_pValuesForExecutor[0] = new NamedValue( "JobToExecute", (Object)this ); + + m_aTimer = new Timer( 100, this ); + m_aTimer.start(); + + // Get a menu bar. + MenuBar aMenuBar = m_aFrame.getMenuBar(); + if( aMenuBar == null ) + { + aMenuBar = new MenuBar(); + m_aFrame.setMenuBar( aMenuBar ); + } + + // Create menus for the menu bar. + + // File menu + m_aFileMenu = new Menu( "File", true ); + aMenuBar.add( m_aFileMenu ); + + MenuItem aItem = new NewMenuItem(); + m_aFileMenu.add( aItem ); + + aItem = new OpenFileMenuItem(); + m_aFileMenu.add( aItem ); + + aItem = new SaveMenuItem(); + m_aFileMenu.add( aItem ); + + aItem = new SaveAsMenuItem(); + m_aFileMenu.add( aItem ); + + // Object menu + m_aObjectMenu = new Menu( "Object", true ); + aMenuBar.add( m_aObjectMenu ); + + aItem = new NewObjectMenuItem(); + m_aObjectMenu.add( aItem ); + + aItem = new LoadObjectMenuItem(); + m_aObjectMenu.add( aItem ); + + aItem = new LinkObjectMenuItem(); + m_aObjectMenu.add( aItem ); + + aItem = new ConvertLinkToEmbedMenuItem(); + m_aObjectMenu.add( aItem ); + + // Activation menu + m_aObjectMenu = new Menu( "Activation", true ); + aMenuBar.add( m_aObjectMenu ); + + aItem = new ActivateOutplaceMenuItem(); + m_aObjectMenu.add( aItem ); + + aItem = new ActivateInplaceMenuItem(); + m_aObjectMenu.add( aItem ); + + aItem = new DeactivateMenuItem(); + m_aObjectMenu.add( aItem ); + + m_aNativeView = new NativeView(); + m_aNativeView.resize( 800, 600 ); + this.add( m_aNativeView ); + + + public void actionPerformed( ActionEvent evt ) + { + synchronized( m_oInHandlerLock ) + { + if ( m_bInHandler ) + return; + m_bInHandler = true; + } + + synchronized( m_oActionsNumberLock ) + { + if ( m_aActionsList.size() > 0 ) + { + try { + m_xMainThreadExecutor.execute( m_pValuesForExecutor ); + } + catch( Exception e ) + { + System.out.println( "Exception in actionPerformed() : " + e ); + } + } + else + { + synchronized( m_oInHandlerLock ) + { + m_bInHandler = false; + } + } + } + } + + // XWindowSupplier + public XWindow getWindow() + { + return m_xVCLWindow; + } + + // XEmbeddedClient + public void saveObject() + throws com.sun.star.uno.Exception + { + if ( m_xEmbedObj != null ) + { + try { + XEmbedPersist xPersist = (XEmbedPersist)UnoRuntime.queryInterface( XEmbedPersist.class, m_xEmbedObj ); + if ( xPersist != null ) + { + xPersist.storeOwn(); + generateNewImage(); + } + else + JOptionPane.showMessageDialog( m_aFrame, "No XEmbedPersist!", "Error:", JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in saveObject:", JOptionPane.ERROR_MESSAGE ); + } + } + + generateNewImage(); + repaint(); + } + + public void onShowWindow( boolean bVisible ) + { + // for now nothing to do + } + + // XInplaceClient + public boolean canInplaceActivate() + { + return true; + } + + public void onInplaceActivate() + { + // TODO + // prepare for inplace activation + + // REMOVE + // workaround for CLIPCHILDREN problem + if ( m_aBitmapPainter != null ) + m_aBitmapPainter.stopPainting(); + } + + public void onUIActivate() + { + // TODO + // prepare for UI activate + } + + public void onInplaceDeactivate() + { + // TODO + // inplace deactivation is done + + // REMOVE + // workaround for CLIPCHILDREN problem + if ( m_aBitmapPainter != null ) + m_aBitmapPainter.startPainting(); + } + + public void onUIDeactivate() + { + // TODO + // prepare for UI deactivate + } + + public XIPMainContainerWindow getTopmostWindow() + { + // TODO + // return an implementation of XIPMainContainerWindow + // mainly required for ui activation + // dummy implementation is enough for inplace activation + + return null; + } + + public XInplaceUIWindow getDocumentWindow() + { + // TODO + // return implementation of XInplaceUIWindow + // mainly required for ui activation + // dummy implementation is enough for inplace activation + + return null; + } + + public com.sun.star.awt.Rectangle getPosRect() + { + // provide position rectangle to the object + try { + // here object bitmap and scaling factor hold the size + com.sun.star.awt.Size aBitmapSize = m_xBitmap.getSize(); + com.sun.star.awt.Size aVisSize = new com.sun.star.awt.Size( + (int)( aBitmapSize.Width * m_nXScaling ), + (int)( aBitmapSize.Height * m_nYScaling ) ); + return new com.sun.star.awt.Rectangle( 10, 10, aVisSize.Width, aVisSize.Height ); + } + catch( Exception e ) + { + System.out.println( "Position rectangle generation failed!" ); + } + + return new com.sun.star.awt.Rectangle( 10, 10, 110, 110 ); + } + + public com.sun.star.awt.Rectangle getClipRect() + { + // provide clip rectangle to the object + // in this application position and clip rectangles are the same + + try { + // here object bitmap and scaling factor hold the size + com.sun.star.awt.Size aBitmapSize = m_xBitmap.getSize(); + com.sun.star.awt.Size aVisSize = new com.sun.star.awt.Size( + (int)( aBitmapSize.Width * m_nXScaling ), + (int)( aBitmapSize.Height * m_nYScaling ) ); + return new com.sun.star.awt.Rectangle( 10, 10, aVisSize.Width, aVisSize.Height ); + } + catch( Exception e ) + { + System.out.println( "Clip rectangle generation failed!" ); + } + + return new com.sun.star.awt.Rectangle( 10, 10, 110, 110 ); + } + + public void translateAccelerators( com.sun.star.awt.KeyEvent[] aKeys ) + { + // TODO + // an accelerator table for object + // ui activation related + } + + public void scrollObj( com.sun.star.awt.Size aOffset ) + { + // TODO + // scrolls the object to a specified offset + // not mandatory for the testing application :) + } + + public void onPosRectChange( com.sun.star.awt.Rectangle aPosRect ) + { + // object asks to change the position + if ( m_xEmbedObj != null ) + { + try { + int nState = m_xEmbedObj.getCurrentState(); + // such a position change make sense only when object is + // either inplace or ui active + if ( nState == EmbedStates.EMBED_INPLACE_ACTIVE + || nState == EmbedStates.EMBED_UI_ACTIVE ) + { + XInplaceObject xInplObj = (XInplaceObject)UnoRuntime.queryInterface( XInplaceObject.class, m_xEmbedObj ); + if ( xInplObj != null ) + { + xInplObj.setObjectRects( aPosRect, aPosRect ); // show the whole object + if ( m_aBitmapPainter != null ) + m_aBitmapPainter.setRect( aPosRect ); + } + else + System.out.println( "Why object that does not support inplace activation behave like inplace object?!" ); + } + else + System.out.println( "The object is not active but asks to change visual area!" ); + } catch( Exception e ) + { + System.out.println( "Exception is thrown in onPosRectChange: " + e ); + } + } + else + System.out.println( "Who asks to change visual area?!!" ); + } + + // XJob + public Object execute( NamedValue[] pValues ) + { + for( int nInd = 0; nInd < m_aActionsList.size(); nInd++ ) + { + ActionObject aAction = m_aActionsList.get( nInd ); + if ( aAction != null ) + { + if ( aAction.m_nID == DESTROY ) + { + // free all resources + clearObjectAndStorage(); + m_bDestroyed = true; + } + else if ( aAction.m_nID == ACTIVATE_OUTPLACE ) + { + // activate object if exists and not active + if ( m_xEmbedObj != null ) + { + try { + m_xEmbedObj.changeState( EmbedStates.EMBED_ACTIVE ); + } + catch( Exception ex ) + { + System.out.println( "Exception on mouse click" + ex ); + } + } + } + else if ( aAction.m_nID == NEW_DOCUMENT ) + { + // clear everything + clearObjectAndStorage(); + + repaint(); + } + else if ( aAction.m_nID == SAVE_AS ) + { + // open SaveAs dialog and store + + if ( m_xStorage != null && m_xEmbedObj != null ) + { + try { + /* + if ( m_bLinkObj ) + storeLinkAsFileURI( aFileURI ); + else + */ + saveObjectAsFileURI( aAction.m_sParam ); + } + catch( Exception ex ) + { + System.out.println( "Exception in SaveAsMenuItem: " + ex ); + } + } + } + else if ( aAction.m_nID == OPEN_FILE ) + { + // clear everything + clearObjectAndStorage(); + + // load from specified file + loadFileURI( aAction.m_sParam ); + + if ( m_xEmbedObj != null ) + { + try { + m_xEmbedObj.setClientSite( this ); + } + catch( Exception ex ) + { + System.out.println( "Exception in OpenFileMenuItem: " + ex ); + } + } + + generateNewImage(); + repaint(); + } + else if ( aAction.m_nID == SAVE ) + { + if ( m_xStorage != null && m_xEmbedObj != null ) + { + // if has persistence store there + // if not it is and error, SaveAs had to be used + + if ( m_bOwnFile ) + { + if ( m_xStorage != null ) + { + try { + saveObject(); + + if ( m_bLinkObj ) + storeLinkToStorage(); + + XTransactedObject xTransact = (XTransactedObject)UnoRuntime.queryInterface( XTransactedObject.class, + m_xStorage ); + if ( xTransact != null ) + xTransact.commit(); + } + catch( Exception ex ) + { + System.out.println( "Exception during save operation in SaveMenuItem:" + ex ); + } + } + else + { + System.out.println( "No storage for owned file!" ); + } + } + else + { + System.out.println( "No owned file!" ); + } + } + } + else if ( aAction.m_nID == NEW_OBJECT ) + { + // remove current object an init a new one + clearObjectAndStorage(); + + if ( aAction.m_sParam != null ) + { + m_xStorage = createTempStorage(); + + if ( m_xStorage != null ) + m_xEmbedObj = createEmbedObject( aAction.m_sParam ); + else + System.out.println( "Can't create temporary storage!" ); + + if ( m_xEmbedObj != null ) + { + try { + m_xEmbedObj.setClientSite( this ); + } + catch( Exception ex ) + { + System.out.println( "Exception in NewObjectMenuItem:" + ex ); + } + } + } + + generateNewImage(); + repaint(); + } + else if ( aAction.m_nID == OBJECT_FROM_FILE ) + { + // first remove current object + clearObjectAndStorage(); + + // create object from specified file + m_xStorage = createTempStorage(); + + if ( m_xStorage != null ) + m_xEmbedObj = loadEmbedObject( aAction.m_sParam ); + + if ( m_xEmbedObj != null ) + { + try { + m_xEmbedObj.setClientSite( this ); + } + catch( Exception ex ) + { + System.out.println( "Exception in LoadObjectMenuItem: " + ex ); + } + } + + generateNewImage(); + repaint(); + } + else if ( aAction.m_nID == LINK_FROM_FILE ) + { + // first remove current object + clearObjectAndStorage(); + + m_xStorage = createTempStorage(); + + // create object from specified file + m_xEmbedObj = createLinkObject( aAction.m_sParam ); + + if ( m_xEmbedObj != null ) + { + m_aLinkURI = aAction.m_sParam; + m_bLinkObj = true; + + try { + m_xEmbedObj.setClientSite( this ); + } + catch( Exception ex ) + { + System.out.println( "Exception in LinkObjectMenuItem:" + ex ); + } + } + + generateNewImage(); + repaint(); + } + else if ( aAction.m_nID == CONVERT_LINK_TO_OBJECT ) + { + if ( !m_bLinkObj ) + { + System.out.println( "The object is not a link!" ); + continue; + } + + if ( m_xEmbedObj != null ) + { + if ( m_xStorage != null ) + { + try { + XNameAccess xNameAccess = (XNameAccess)UnoRuntime.queryInterface( XNameAccess.class, + m_xStorage ); + if ( xNameAccess != null && xNameAccess.hasByName( "LinkName" ) ) + m_xStorage.removeElement( "LinkName" ); + + XLinkageSupport xLinkage = (XLinkageSupport)UnoRuntime.queryInterface( XLinkageSupport.class, + m_xEmbedObj ); + if ( xLinkage != null ) + { + xLinkage.breakLink( m_xStorage, "EmbedSub" ); + m_bLinkObj = false; + m_aLinkURI = null; + } + else + System.out.println( "No XLinkageSupport in ConvertLink... !" ); + } + catch( Exception e1 ) + { + System.out.println( "Exception in ConvertLinkToEmbed:try 1 :" + e1 ); + } + } + } + } + else if ( aAction.m_nID == ACTIVATE_INPLACE ) + { + // activate object + if ( m_xEmbedObj != null ) + { + // in general it is better to check acceptable states + try { + m_xEmbedObj.changeState( EmbedStates.EMBED_INPLACE_ACTIVE ); + } + catch( Exception ex ) + { + System.out.println( "Exception on inplace activation " + ex ); + } + } + } + else if ( aAction.m_nID == DEACTIVATE ) + { + // activate object + + if ( m_xEmbedObj != null ) + { + int nOldState = -1; + try { + nOldState = m_xEmbedObj.getCurrentState(); + } catch( Exception e ) + {} + + if ( nOldState == EmbedStates.EMBED_ACTIVE + || nOldState == EmbedStates.EMBED_INPLACE_ACTIVE + || nOldState == EmbedStates.EMBED_UI_ACTIVE ) + { + try { + m_xEmbedObj.changeState( EmbedStates.EMBED_RUNNING ); + } + catch( Exception ex ) + { + System.out.println( "Exception on inplace activation " + ex ); + } + } + else + { + System.out.println( "Deactivation of nonactive object!" ); + } + } + } + else + { + System.out.println( "Unknown action is requested: " + aAction.m_nID + "\n" ); + } + } + } + + m_aActionsList.clear(); + + synchronized( m_oInHandlerLock ) + { + m_bInHandler = false; + } + + return Any.VOID; + } + + public void actionRegister( byte nActionID, String sParam ) + { + synchronized( m_oActionsNumberLock ) + { + int nSize = m_aActionsList.size(); + if ( nSize < 199 ) + { + if ( nSize == 0 ) + m_aActionsList.add( new ActionObject( nActionID, sParam ) ); + else + { + ActionObject aAction = m_aActionsList.get( nSize - 1 ); + if ( aAction != null && aAction.m_nID != DESTROY ) + m_aActionsList.add( new ActionObject( nActionID, sParam ) ); + } + } + } + } + + public void SaveAsOperation() + { + if ( m_xStorage != null && m_xEmbedObj != null ) + { + FileDialog aFileDialog = new FileDialog( m_aFrame, "SaveAs", FileDialog.SAVE ); + aFileDialog.show(); + if ( aFileDialog.getFile() != null ) + { + String aFileName = aFileDialog.getDirectory() + aFileDialog.getFile(); + File aFile = new File( aFileName ); + if ( aFile != null ) + { + // create object from specified file + String aFileURI = getValidURL( aFile.toURI().toASCIIString() ); + actionRegister( SAVE_AS, aFileURI ); + } + } + } + else + JOptionPane.showMessageDialog( m_aFrame, "No document is embedded!", "Error:", JOptionPane.ERROR_MESSAGE ); + + } + + public void destroy() + { + // redirect the call through the timer and call super.destroy(); + actionRegister( DESTROY, null ); + + for ( int i = 0; i < 3 && !m_bDestroyed; i++ ) + { + try { + Thread.sleep( 200 ); + } catch ( Exception e ) + {} + } + + if ( !m_bDestroyed ) + System.out.println( "The object application can not exit correctly!" ); + + m_aTimer.stop(); + + super.destroy(); + } + + public void update( Graphics g ) + { + paint( g ); + } + + public void paint( Graphics g ) + { + super.paint( g ); + + createVclWindow(); + } + + public void createVclWindow() + { + synchronized( m_oImageLock ) + { + if ( m_xVCLWindow == null && m_xServiceFactory != null && m_xEmbedObj != null && m_xBitmap != null ) + { + java.awt.Rectangle aBounds = getBounds(); + m_xVCLWindow = WindowHelper.createWindow( m_xServiceFactory, m_aNativeView, aBounds ); + m_xVCLWindow.setVisible( true ); + + com.sun.star.awt.Size aBitmapSize = new com.sun.star.awt.Size( 200, 100 ); + + XVisualObject xVisObj = (XVisualObject)UnoRuntime.queryInterface( XVisualObject.class, m_xEmbedObj ); + try { + com.sun.star.awt.Size aVisSize = xVisObj.getVisAreaSize( Aspects.MSASPECT_CONTENT ); + m_nXPixelSize = aVisSize.Width / aBitmapSize.Width; + m_nYPixelSize = aVisSize.Height / aBitmapSize.Height; + } + catch( Exception e ) + { + } + + if ( m_xBitmap != null ) + aBitmapSize = m_xBitmap.getSize(); + + System.out.println( "The visual area is Width = " + aBitmapSize.Width + "; Height = " + aBitmapSize.Height ); + + com.sun.star.awt.Rectangle aRect = new com.sun.star.awt.Rectangle( + 10, + 10, + Math.min( (int)aBounds.getWidth() - 20, aBitmapSize.Width ), + Math.min( (int)aBounds.getHeight() - 20, aBitmapSize.Height ) ); + + m_aBitmapPainter = new BitmapPainter( m_xMainThreadExecutor, m_xVCLWindow, m_xBitmap, aRect ); + } + } + } + + public void generateNewImage() + { + if ( m_xEmbedObj != null ) + { + try { + int nOldState = m_xEmbedObj.getCurrentState(); + int nState = nOldState; + if ( nOldState == EmbedStates.EMBED_LOADED ) + { + m_xEmbedObj.changeState( EmbedStates.EMBED_RUNNING ); + nState = EmbedStates.EMBED_RUNNING; + } + + if ( nState == EmbedStates.EMBED_UI_ACTIVE || nState == EmbedStates.EMBED_INPLACE_ACTIVE + || nState == EmbedStates.EMBED_ACTIVE || nState == EmbedStates.EMBED_RUNNING ) + { + XComponentSupplier xCompProv = (XComponentSupplier)UnoRuntime.queryInterface( + XComponentSupplier.class, + m_xEmbedObj ); + if ( xCompProv != null ) + { + XCloseable xCloseable = xCompProv.getComponent(); + XTransferable xTransfer = (XTransferable)UnoRuntime.queryInterface( + XTransferable.class, + xCloseable ); + if ( xTransfer != null ) + { + DataFlavor aFlavor = new DataFlavor(); + aFlavor.MimeType = "application/x-openoffice;windows_formatname=\"Bitmap\""; + aFlavor.HumanPresentableName = "Bitmap"; + aFlavor.DataType = new Type( byte[].class ); + + Object aAny = xTransfer.getTransferData( aFlavor ); + if ( aAny != null && AnyConverter.isArray( aAny ) ) + { + synchronized( m_oImageLock ) + { + m_xBitmap = WindowHelper.getVCLBitmapFromBytes( m_xServiceFactory, aAny ); + if ( m_aBitmapPainter != null ) + { + m_aBitmapPainter.setBitmap( m_xBitmap ); + + if ( m_xBitmap != null ) + { + try { + com.sun.star.awt.Size aBitmapSize = m_xBitmap.getSize(); + com.sun.star.awt.Size aVisSize = new com.sun.star.awt.Size( + (int)( aBitmapSize.Width * m_nXScaling ), + (int)( aBitmapSize.Height * m_nYScaling ) ); + m_aBitmapPainter.setSize( aVisSize ); + } + catch( Exception e ) + { + } + } + } + } + } + } + else + System.out.println( "paint() : can not get XTransferable for the component!\n" ); + } + else + System.out.println( "paint() : XComponentSupplier is not implemented!\n" ); + } + } + catch( com.sun.star.uno.Exception e ) + { + // dialogs should not be used in paint() + System.out.println( "Exception in paint(): " + e ); + } + } + } + + public void mouseClicked( MouseEvent e ) + { + if( e.getModifiers() == InputEvent.BUTTON1_MASK ) + { + actionRegister( ACTIVATE_OUTPLACE, null ); + } + } + + public void mousePressed( MouseEvent e ){}; + public void mouseEntered( MouseEvent e ){}; + public void mouseExited( MouseEvent e ){}; + public void mouseReleased( MouseEvent e ){}; + + class NewMenuItem extends MenuItem implements ActionListener // Menu New + { + public NewMenuItem() + { + super( "New", new MenuShortcut( KeyEvent.VK_A )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + actionRegister( NEW_DOCUMENT, null ); + } + } + + class SaveAsMenuItem extends MenuItem implements ActionListener // Menu SaveAs... + { + public SaveAsMenuItem() + { + super( "SaveAs..." ); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + // open SaveAs dialog and store + + SaveAsOperation(); + } + } + + class OpenFileMenuItem extends MenuItem implements ActionListener // Menu Open + { + public OpenFileMenuItem() + { + super( "Open", new MenuShortcut( KeyEvent.VK_C )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + // open OpenFile dialog and load doc + FileDialog aFileDialog = new FileDialog( m_aFrame, "Open" ); + aFileDialog.show(); + if ( aFileDialog.getFile() != null ) + { + String aFileName = aFileDialog.getDirectory() + aFileDialog.getFile(); + File aFile = new File( aFileName ); + if ( aFile != null ) + { + // create object from specified file + String aFileURI = getValidURL( aFile.toURI().toASCIIString() ); + actionRegister( OPEN_FILE, aFileURI ); + } + } + } + } + + class SaveMenuItem extends MenuItem implements ActionListener // Menu Save + { + public SaveMenuItem() + { + super( "Save", new MenuShortcut( KeyEvent.VK_D )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + // if has persistence store there + // if not open SaveAs dialog and store + if ( m_xStorage != null && m_xEmbedObj != null ) + { + if ( m_bOwnFile ) + { + if ( m_xStorage == null ) + { + JOptionPane.showMessageDialog( m_aFrame, + "No storage for owned file!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + + return; + } + + actionRegister( SAVE, null ); + } + else + { + SaveAsOperation(); + } + } + else + JOptionPane.showMessageDialog( m_aFrame, "No document is embedded!", "Error:", JOptionPane.ERROR_MESSAGE ); + } + } + + class NewObjectMenuItem extends MenuItem implements ActionListener // Menu NewObject + { + public NewObjectMenuItem() + { + super( "Create", new MenuShortcut( KeyEvent.VK_N )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + Object[] possibleValues = { "com.sun.star.comp.Writer.TextDocument", + "com.sun.star.comp.Writer.GlobalDocument", + "com.sun.star.comp.Writer.WebDocument", + "com.sun.star.comp.Calc.SpreadsheetDocument", + "com.sun.star.comp.Draw.PresentationDocument", + "com.sun.star.comp.Draw.DrawingDocument", + "com.sun.star.comp.Math.FormulaDocument", + "BitmapImage" }; + + String selectedValue = (String)JOptionPane.showInputDialog( null, "DocumentType", "Select", + JOptionPane.INFORMATION_MESSAGE, null, + possibleValues, possibleValues[0] ); + + actionRegister( NEW_OBJECT, selectedValue ); + } + } + + class LoadObjectMenuItem extends MenuItem implements ActionListener // Menu LoadObject + { + public LoadObjectMenuItem() + { + super( "Load from file", new MenuShortcut( KeyEvent.VK_L )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + // open OpenFile dialog and load doc + FileDialog aFileDialog = new FileDialog( m_aFrame, "Select sources to use for object init" ); + aFileDialog.show(); + if ( aFileDialog.getFile() != null ) + { + String aFileName = aFileDialog.getDirectory() + aFileDialog.getFile(); + File aFile = new File( aFileName ); + if ( aFile != null ) + { + // create object from specified file + String aFileURI = getValidURL( aFile.toURI().toASCIIString() ); + actionRegister( OBJECT_FROM_FILE, aFileURI ); + } + } + } + } + + class LinkObjectMenuItem extends MenuItem implements ActionListener // Menu LinkObject + { + public LinkObjectMenuItem() + { + super( "Create link", new MenuShortcut( KeyEvent.VK_M )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + // open OpenFile dialog and load doc + FileDialog aFileDialog = new FileDialog( m_aFrame, "Select sources to use for object init" ); + aFileDialog.show(); + if ( aFileDialog.getFile() != null ) + { + String aFileName = aFileDialog.getDirectory() + aFileDialog.getFile(); + File aFile = new File( aFileName ); + if ( aFile != null ) + { + // create object from specified file + String aFileURI = getValidURL( aFile.toURI().toASCIIString() ); + actionRegister( LINK_FROM_FILE, aFileURI ); + } + } + } + } + + class ConvertLinkToEmbedMenuItem extends MenuItem implements ActionListener // Menu LinkObject + { + public ConvertLinkToEmbedMenuItem() + { + super( "Convert link to embed", new MenuShortcut( KeyEvent.VK_M )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + actionRegister( CONVERT_LINK_TO_OBJECT, null ); + } + } + + class ActivateOutplaceMenuItem extends MenuItem implements ActionListener // Menu ActiveteOutplace + { + public ActivateOutplaceMenuItem() + { + super( "Activate outplace", new MenuShortcut( KeyEvent.VK_A )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + actionRegister( ACTIVATE_OUTPLACE, null ); + } + } + + class ActivateInplaceMenuItem extends MenuItem implements ActionListener // Menu ActivateInplace + { + public ActivateInplaceMenuItem() + { + super( "Activate inplace", new MenuShortcut( KeyEvent.VK_I )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + actionRegister( ACTIVATE_INPLACE, null ); + } + } + + class DeactivateMenuItem extends MenuItem implements ActionListener // Menu Deactivate + { + public DeactivateMenuItem() + { + super( "Deactivate", new MenuShortcut( KeyEvent.VK_D )); + addActionListener( this ); + } + + public void actionPerformed( ActionEvent e ) + { + actionRegister( DEACTIVATE, null ); + } + } + + // Helper methods + public XEmbeddedObject createEmbedObject( String aServiceName ) + { + XEmbeddedObject xEmbObj = null; + byte[] pClassID = new byte[16]; + + if ( aServiceName.equals( "com.sun.star.comp.Writer.TextDocument" ) ) + { + int[] pTempClassID = { 0x8B, 0xC6, 0xB1, 0x65, 0xB1, 0xB2, 0x4E, 0xDD, + 0xAA, 0x47, 0xDA, 0xE2, 0xEE, 0x68, 0x9D, 0xD6 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Writer.GlobalDocument" ) ) + { + int[] pTempClassID = { 0xB2, 0x1A, 0x0A, 0x7C, 0xE4, 0x03, 0x41, 0xFE, + 0x95, 0x62, 0xBD, 0x13, 0xEA, 0x6F, 0x15, 0xA0 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Writer.WebDocument" ) ) + { + int[] pTempClassID = { 0xA8, 0xBB, 0xA6, 0x0C, 0x7C, 0x60, 0x45, 0x50, + 0x91, 0xCE, 0x39, 0xC3, 0x90, 0x3F, 0xAC, 0x5E }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Calc.SpreadsheetDocument" ) ) + { + int[] pTempClassID = { 0x47, 0xBB, 0xB4, 0xCB, 0xCE, 0x4C, 0x4E, 0x80, + 0xA5, 0x91, 0x42, 0xD9, 0xAE, 0x74, 0x95, 0x0F }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Draw.PresentationDocument" ) ) + { + int[] pTempClassID = { 0x91, 0x76, 0xE4, 0x8A, 0x63, 0x7A, 0x4D, 0x1F, + 0x80, 0x3B, 0x99, 0xD9, 0xBF, 0xAC, 0x10, 0x47 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Draw.DrawingDocument" ) ) + { + int[] pTempClassID = { 0x4B, 0xAB, 0x89, 0x70, 0x8A, 0x3B, 0x45, 0xB3, + 0x99, 0x1C, 0xCB, 0xEE, 0xAC, 0x6B, 0xD5, 0xE3 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "com.sun.star.comp.Math.FormulaDocument" ) ) + { + int[] pTempClassID = { 0x07, 0x8B, 0x7A, 0xBA, 0x54, 0xFC, 0x45, 0x7F, + 0x85, 0x51, 0x61, 0x47, 0xE7, 0x76, 0xA9, 0x97 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + else if ( aServiceName.equals( "BitmapImage" ) ) + { + int[] pTempClassID = { 0xD3, 0xE3, 0x4B, 0x21, 0x9D, 0x75, 0x10, 0x1A, + 0x8C, 0x3D, 0x00, 0xAA, 0x00, 0x1A, 0x16, 0x52 }; + for ( int ind = 0; ind < 16; ind++ ) + pClassID[ind] = (byte)pTempClassID[ind]; + } + + if ( pClassID != null ) + { + // create embedded object based on the class ID + try { + Object oEmbedCreator = m_xServiceFactory.createInstance( "com.sun.star.embed.EmbeddedObjectCreator" ); + XEmbedObjectCreator xEmbedCreator = (XEmbedObjectCreator)UnoRuntime.queryInterface( + XEmbedObjectCreator.class, + oEmbedCreator ); + if ( xEmbedCreator != null ) + { + Object oEmbObj = xEmbedCreator.createInstanceInitNew( pClassID, + "Dummy name", + m_xStorage, + "EmbedSub", + new PropertyValue[0] ); + xEmbObj = (XEmbeddedObject)UnoRuntime.queryInterface( XEmbeddedObject.class, oEmbObj ); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create EmbedCreator!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in createInstanceInitNew():", JOptionPane.ERROR_MESSAGE ); + } + } + else + JOptionPane.showMessageDialog( m_aFrame, "Can't retrieve class ID!", "Error:", JOptionPane.ERROR_MESSAGE ); + + return xEmbObj; + } + + public XEmbeddedObject createLinkObject( String aLinkURL ) + { + XEmbeddedObject xEmbObj = null; + + try { + Object oLinkCreator = m_xServiceFactory.createInstance( "com.sun.star.embed.EmbeddedObjectCreator" ); + XLinkCreator xLinkCreator = (XLinkCreator)UnoRuntime.queryInterface( + XLinkCreator.class, + oLinkCreator ); + if ( xLinkCreator != null ) + { + PropertyValue[] aMedDescr = { new PropertyValue(), new PropertyValue() }; + aMedDescr[0].Name = "URL"; + aMedDescr[0].Value = (Object) aLinkURL; + aMedDescr[1].Name = "ReadOnly"; + aMedDescr[1].Value = (Object) Boolean.FALSE; + Object oEmbObj = xLinkCreator.createInstanceLink( m_xStorage, "EmbedSub", aMedDescr, new PropertyValue[0] ); + xEmbObj = (XEmbeddedObject)UnoRuntime.queryInterface( XEmbeddedObject.class, oEmbObj ); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create LinkCreator!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in createLinkObject():", JOptionPane.ERROR_MESSAGE ); + } + + + return xEmbObj; + } + + + public XEmbeddedObject loadEmbedObject( String aFileURI ) + { + XEmbeddedObject xEmbObj = null; + try { + Object oEmbedCreator = m_xServiceFactory.createInstance( "com.sun.star.embed.EmbeddedObjectCreator" ); + XEmbedObjectCreator xEmbedCreator = (XEmbedObjectCreator)UnoRuntime.queryInterface( + XEmbedObjectCreator.class, + oEmbedCreator ); + if ( xEmbedCreator != null ) + { + PropertyValue[] aMedDescr = { new PropertyValue(), new PropertyValue() }; + aMedDescr[0].Name = "URL"; + aMedDescr[0].Value = (Object) aFileURI; + aMedDescr[1].Name = "ReadOnly"; + aMedDescr[1].Value = (Object) Boolean.FALSE; + Object oEmbObj = xEmbedCreator.createInstanceInitFromMediaDescriptor( m_xStorage, + "EmbedSub", + aMedDescr, + new PropertyValue[0] ); + xEmbObj = (XEmbeddedObject)UnoRuntime.queryInterface( XEmbeddedObject.class, oEmbObj ); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create EmbedFactory!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in loadEmbedObject():", JOptionPane.ERROR_MESSAGE ); + } + + return xEmbObj; + } + + public void clearObjectAndStorage() + { + synchronized( m_oImageLock ) + { + m_aImage = null; + } + + m_nXScaling = 1; + m_nYScaling = 1; + m_nXPixelSize = 1; + m_nYPixelSize = 1; + + m_bOwnFile = false; + + m_aLinkURI = null; + m_bLinkObj = false; + + if ( m_xEmbedObj != null ) + { + try { + XCloseable xClose = (XCloseable)UnoRuntime.queryInterface( XCloseable.class, m_xEmbedObj ); + if ( xClose != null ) + xClose.close( true ); + } + catch ( Exception ex ) + {} + m_xEmbedObj = null; + } + + if ( m_xStorage != null ) + { + try { + XComponent xComponent = (XComponent)UnoRuntime.queryInterface( XComponent.class, m_xStorage ); + if ( xComponent != null ) + xComponent.dispose(); + } + catch ( Exception ex ) + {} + m_xStorage = null; + } + } + + public XStorage createTempStorage() + { + XStorage xTempStorage = null; + + try { + Object oStorageFactory = m_xServiceFactory.createInstance( "com.sun.star.embed.StorageFactory" ); + XSingleServiceFactory xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( + XSingleServiceFactory.class, + oStorageFactory ); + if ( xStorageFactory != null ) + { + Object oStorage = xStorageFactory.createInstance(); + xTempStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oStorage ); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create StorageFactory!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in createTempStorage():", JOptionPane.ERROR_MESSAGE ); + } + + return xTempStorage; + } + + public void saveObjectAsFileURI( String aFileURI ) + { + try { + Object oStorageFactory = m_xServiceFactory.createInstance( "com.sun.star.embed.StorageFactory" ); + XSingleServiceFactory xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( + XSingleServiceFactory.class, + oStorageFactory ); + if ( xStorageFactory != null ) + { + XEmbedPersist xPersist = (XEmbedPersist)UnoRuntime.queryInterface( XEmbedPersist.class, m_xEmbedObj ); + if ( xPersist != null ) + { + Object aArgs[] = new Object[2]; + aArgs[0] = aFileURI; + aArgs[1] = Integer.valueOf( ElementModes.ELEMENT_READWRITE ); + + Object oStorage = xStorageFactory.createInstanceWithArguments( aArgs ); + XStorage xTargetStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oStorage ); + + PropertyValue aProps[] = { new PropertyValue() }; + aProps[0].Name = "StoreVisualReplacement"; + aProps[0].Value = Boolean.valueOf( m_bStoreVisRepl ); + + xPersist.storeAsEntry( xTargetStorage, "EmbedSub", new PropertyValue[0], aProps ); + xPersist.saveCompleted( true ); + + // the object must be already based on new storage + XComponent xComponent = (XComponent)UnoRuntime.queryInterface( XComponent.class, m_xStorage ); + xComponent.dispose(); + + m_xStorage = xTargetStorage; + m_bOwnFile = true; + + XTransactedObject xTransact = (XTransactedObject)UnoRuntime.queryInterface( XTransactedObject.class, + m_xStorage ); + if ( xTransact != null ) + xTransact.commit(); + } + else + JOptionPane.showMessageDialog( m_aFrame, "No XEmbedPersist!", "Error:", JOptionPane.ERROR_MESSAGE ); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create StorageFactory!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in saveStorageToFileURI():", JOptionPane.ERROR_MESSAGE ); + } + + } + + public void loadFileURI( String aFileURI ) + { + try + { + Object oStorageFactory = m_xServiceFactory.createInstance( "com.sun.star.embed.StorageFactory" ); + XSingleServiceFactory xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( + XSingleServiceFactory.class, + oStorageFactory ); + Object aArgs[] = new Object[2]; + aArgs[0] = aFileURI; + aArgs[1] = Integer.valueOf( ElementModes.ELEMENT_READWRITE ); + + Object oStorage = xStorageFactory.createInstanceWithArguments( aArgs ); + XStorage xTargetStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oStorage ); + + Object oEmbedCreator = m_xServiceFactory.createInstance( "com.sun.star.embed.EmbeddedObjectCreator" ); + XEmbedObjectCreator xEmbedCreator = (XEmbedObjectCreator)UnoRuntime.queryInterface( + XEmbedObjectCreator.class, + oEmbedCreator ); + + XNameAccess xNameAccess = (XNameAccess)UnoRuntime.queryInterface( XNameAccess.class, + xTargetStorage ); + if ( xNameAccess == null ) + { + JOptionPane.showMessageDialog( m_aFrame, "No XNameAccess!", "Error:", JOptionPane.ERROR_MESSAGE ); + return; + } + + Object oEmbObj = null; + if ( xNameAccess.hasByName( "LinkName" ) && xTargetStorage.isStreamElement( "LinkName" ) ) + { + } + else + oEmbObj = xEmbedCreator.createInstanceInitFromEntry( xTargetStorage, + "EmbedSub", + false, + new PropertyValue[0] ); + + m_xEmbedObj = (XEmbeddedObject)UnoRuntime.queryInterface( XEmbeddedObject.class, oEmbObj ); + + if ( m_xEmbedObj != null ) + { + m_xStorage = xTargetStorage; + m_bOwnFile = true; + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create EmbedObject from storage!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in loadFileURI():", JOptionPane.ERROR_MESSAGE ); + } + } + + public void storeLinkToStorage() + { + if ( m_xStorage != null && m_bLinkObj ) + { + try { + XStream xLinkStream = m_xStorage.openStreamElement( "LinkName", ElementModes.ELEMENT_WRITE ); + + if ( xLinkStream != null ) + { + XOutputStream xLinkOutStream = xLinkStream.getOutputStream(); + XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, + xLinkOutStream ); + if ( xLinkOutStream != null && xTruncate != null ) + { + xTruncate.truncate(); + + char[] aLinkChar = m_aLinkURI.toCharArray(); + byte[] aLinkBytes = new byte[ aLinkChar.length ]; + for ( int ind = 0; ind < aLinkChar.length; ind++ ) + aLinkBytes[ind] = (byte)aLinkChar[ind]; + + xLinkOutStream.writeBytes( aLinkBytes ); + xLinkOutStream.closeOutput(); + + XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, + xLinkStream ); + if ( xComponent != null ) + xComponent.dispose(); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "The substream can not be truncated or written!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create/open substream!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, + e, + "Exception in storeLinkToStorage:", + JOptionPane.ERROR_MESSAGE ); + + } + } + } + + public void storeLinkAsFileURI( String aFileURI ) + { + try { + Object oStorageFactory = m_xServiceFactory.createInstance( "com.sun.star.embed.StorageFactory" ); + XSingleServiceFactory xStorageFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( + XSingleServiceFactory.class, + oStorageFactory ); + if ( xStorageFactory != null ) + { + Object aArgs[] = new Object[2]; + aArgs[0] = aFileURI; + aArgs[1] = Integer.valueOf( ElementModes.ELEMENT_READWRITE ); + + Object oStorage = xStorageFactory.createInstanceWithArguments( aArgs ); + XStorage xTargetStorage = (XStorage)UnoRuntime.queryInterface( XStorage.class, oStorage ); + + XComponent xComponent = (XComponent)UnoRuntime.queryInterface( XComponent.class, m_xStorage ); + xComponent.dispose(); + + m_xStorage = xTargetStorage; + m_bOwnFile = true; + + storeLinkToStorage(); + + XTransactedObject xTransact = (XTransactedObject)UnoRuntime.queryInterface( XTransactedObject.class, + m_xStorage ); + if ( xTransact != null ) + xTransact.commit(); + } + else + JOptionPane.showMessageDialog( m_aFrame, + "Can't create StorageFactory!", + "Error:", + JOptionPane.ERROR_MESSAGE ); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in saveStorageToFileURI():", JOptionPane.ERROR_MESSAGE ); + } + } + + public String getValidURL( String sFileURL ) + { + // m_xTransformer must be set! + URL[] aURLs = { new URL() }; + aURLs[0].Complete = sFileURL; + + try { + if ( !m_xTransformer.parseSmart( aURLs, "" ) ) + throw new Exception(); + } + catch( Exception e ) + { + JOptionPane.showMessageDialog( m_aFrame, e, "Exception in getValidURL():", JOptionPane.ERROR_MESSAGE ); + } + + return aURLs[0].Complete; + } + + public void disposeObject() + { + // TODO: + // usage of object, storage and bitmap painter should be locked + // but since possibility of race condition is very low + // it is not really required for testing application + + clearObjectAndStorage(); + + if ( m_aBitmapPainter != null ) + { + m_aBitmapPainter.disconnectListener(); + m_aBitmapPainter = null; + } + } +} + diff --git a/embeddedobj/test/Container1/EmbedContFrame.java b/embeddedobj/test/Container1/EmbedContFrame.java new file mode 100644 index 000000000..d8b927d3b --- /dev/null +++ b/embeddedobj/test/Container1/EmbedContFrame.java @@ -0,0 +1,136 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package embeddedobj.test; + +import java.awt.*; +import java.awt.event.*; + +import com.sun.star.comp.servicemanager.ServiceManager; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.connection.XConnector; +import com.sun.star.connection.XConnection; + +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.XNamingService; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.container.*; +import com.sun.star.beans.*; +import com.sun.star.lang.*; + + +public class EmbedContFrame extends Frame +{ + private EmbedContApp m_aApp; + + WindowListener m_aCloser = new WindowAdapter() + { + public void windowClosing( WindowEvent e ) + { + if ( m_aApp != null ) + { + m_aApp.disposeObject(); + m_aApp = null; + } + + dispose(); + System.exit( 0 ); + } + }; + + public EmbedContFrame( String sName ) + { + super( sName ); + addWindowListener( m_aCloser ); + } + + public static void start() + { + EmbedContFrame aFrame = new EmbedContFrame( "Testing container." ); + + // connect to the office + XMultiServiceFactory aServiceFactory = null; + try { + aServiceFactory = connectOfficeGetServiceFactory(); + } + catch( Exception e ) + {} + + if ( aServiceFactory == null ) + { + System.out.println( "Can't get service manager!\n" ); + System.exit( 1 ); + } + + aFrame.m_aApp = new EmbedContApp( aFrame, aServiceFactory ); + aFrame.m_aApp.init(); + aFrame.m_aApp.start(); + + Dimension aSize = aFrame.m_aApp.getSize(); + + aFrame.add( "Center", aFrame.m_aApp ); + aFrame.pack(); + aFrame.setSize( aSize ); + + aFrame.setVisible( true ); + } + + public static void main( String args[] ) + { + EmbedContFrame.start(); + } + + public static XMultiServiceFactory connectOfficeGetServiceFactory() + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException, + Exception + { + String sConnectionString = "uno:socket,host=localhost,port=8100;urp;StarOffice.NamingService"; + + // Get component context + XComponentContext xComponentContext = + com.sun.star.comp.helper.Bootstrap.createInitialComponentContext( null ); + + // initial serviceManager + XMultiComponentFactory xLocalServiceManager = xComponentContext.getServiceManager(); + + // create a connector, so that it can contact the office + Object oUrlResolver = xLocalServiceManager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", + xComponentContext ); + XUnoUrlResolver xUrlResolver = (XUnoUrlResolver)UnoRuntime.queryInterface( XUnoUrlResolver.class, oUrlResolver ); + + Object oInitialObject = xUrlResolver.resolve( sConnectionString ); + XNamingService xName = (XNamingService)UnoRuntime.queryInterface( XNamingService.class, oInitialObject ); + + XMultiServiceFactory xMSF = null; + if( xName != null ) { + Object oMSF = xName.getRegisteredObject( "StarOffice.ServiceManager" ); + xMSF = (XMultiServiceFactory)UnoRuntime.queryInterface( XMultiServiceFactory.class, oMSF ); + } + else + System.out.println( "Error: Can't get XNamingService interface from url resolver!" ); + + return xMSF; + } +} + diff --git a/embeddedobj/test/Container1/JavaWindowPeerFake.java b/embeddedobj/test/Container1/JavaWindowPeerFake.java new file mode 100644 index 000000000..68fd708c2 --- /dev/null +++ b/embeddedobj/test/Container1/JavaWindowPeerFake.java @@ -0,0 +1,100 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package embeddedobj.test; + +import java.awt.*; + +import com.sun.star.uno.*; +import com.sun.star.lang.*; +import com.sun.star.awt.*; +import com.sun.star.util.*; +import com.sun.star.beans.*; +import com.sun.star.container.*; + +/** <p>Class to pass the system window handle to the OpenOffice.org toolkit.</p> + */ +class JavaWindowPeerFake implements XSystemDependentWindowPeer, + XWindowPeer +{ + NativeView maView; + + public JavaWindowPeerFake( NativeView aNative ) + { + maView = aNative; + } + + + /** + * Implementation of XSystemDependentWindowPeer ( that's all we really need ). + * This method is called back from the Office toolkit to retrieve the system data. + */ + public java.lang.Object getWindowHandle( byte[] aProcessId, short aSystem ) + throws com.sun.star.uno.RuntimeException + { + Object aReturn = null; + if( aSystem == maView.maSystem ) + aReturn = ( Object )maView.maHandle; + + return aReturn; + } + + /** not really needed. + */ + public XToolkit getToolkit() + throws com.sun.star.uno.RuntimeException + { + return null; + } + + public void setPointer( XPointer xPointer ) + throws com.sun.star.uno.RuntimeException + { + } + + public void setBackground( int nColor ) + throws com.sun.star.uno.RuntimeException + { + } + + public void invalidate( short nFlags ) + throws com.sun.star.uno.RuntimeException + { + } + + public void invalidateRect( com.sun.star.awt.Rectangle aRect,short nFlags ) + throws com.sun.star.uno.RuntimeException + { + } + + public void dispose() + throws com.sun.star.uno.RuntimeException + { + } + + public void addEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + } + + public void removeEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + } +} + diff --git a/embeddedobj/test/Container1/NativeView.java b/embeddedobj/test/Container1/NativeView.java new file mode 100644 index 000000000..d94eeece1 --- /dev/null +++ b/embeddedobj/test/Container1/NativeView.java @@ -0,0 +1,166 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +package embeddedobj.test; + +// __________ Imports __________ + +import java.awt.*; +import java.lang.*; +import java.awt.event.*; + +// __________ Implementation __________ + +/** + * Class to pass the system window handle to the OpenOffice.org toolkit. + * It use special JNI methods to get the system handle of used java window. + * + * Attention! + * Use JNI functions on already visible canvas objects only! + * Otherwise they can make some trouble. + * + */ + +public class NativeView extends java.awt.Canvas +{ + + + /** + * ctor + * Does nothing really. + * We can use our JNI mechanism for an already visible + * canvas only. So we override the method for showing ( "setVisible()" ) + * and make our initialization there. But we try to show an empty clean + * window till there. + */ + public NativeView() + { + maHandle = null; + maSystem = 0; + this.setBackground( Color.white ); + } + + + + /** + * Override this method to make necessary initializations here. + * ( e.g. get the window handle and necessary system information ) + * + * Why here? + * Because the handle seems to be available for already-visible windows + * only. So it's the best place to get it. The special helper method + * can be called more than once - but call native code one time only + * and save the handle and the system type on our members maHandle/maSystem! + */ + public void setVisible( boolean bState ) + { + getHWND(); + } + + + + /** + * to guarantee right resize handling inside a swing container + * ( e.g. JSplitPane ) we must provide some information about our + * preferred/minimum and maximum size. + */ + public Dimension getPreferredSize() + { + return new Dimension( 800, 600 ); + } + + public Dimension getMaximumSize() + { + return new Dimension( 1024, 768 ); + } + + public Dimension getMinimumSize() + { + return new Dimension( 300, 300 ); + } + + + + /** + * override paint routine to show provide against + * repaint errors if no office view is really plugged + * into this canvas. + * If handle is present - we shouldn't paint anything further. + * May the remote window is already plugged. In such case we + * shouldn't paint it over. + */ + public void paint( Graphics aGraphic ) + { + if( maHandle == null ) + { + Dimension aSize = getSize(); + aGraphic.clearRect( 0, 0, aSize.width, aSize.height ); + } + } + + + + /** + * JNI interface of this class + * These two methods are implemented by using JNI mechanismen. + * The will be used to get the platform dependent window handle + * of a java awt canvas. This handle can be used to create an office + * window as direct child of it. So it's possible to plug Office + * windows in a java UI container. + * + * Note: + * Native code for Windows registers a special function pointer to handle + * window messages... But if it doesn't check for an already-registered + * instance of this handler it will do it twice and produce a stack overflow + * because such method calls itself in a never-ending loop... + * So we try to use the JNI code one time only and save already-obtained + * information inside this class. + */ + public native int getNativeWindowSystemType(); + private native long getNativeWindow(); // private! => use getHWND() with cache mechanism! + + public Integer getHWND() + { + if( maHandle == null ) + { + maHandle = Integer.valueOf( (int )getNativeWindow() ); + maSystem = getNativeWindowSystemType(); + } + return maHandle; + } + + + + /** + * for using of the JNI methods it's necessary to load + * system library which exports it. + */ + static + { + System.loadLibrary( "nativeview" ); + } + + + + /** + * @member maHandle system window handle + * @member maSystem info about currently used platform + */ + public Integer maHandle ; + public int maSystem ; +} + diff --git a/embeddedobj/test/Container1/PaintThread.java b/embeddedobj/test/Container1/PaintThread.java new file mode 100644 index 000000000..f7eb63ef2 --- /dev/null +++ b/embeddedobj/test/Container1/PaintThread.java @@ -0,0 +1,141 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package embeddedobj.test; + +import java.awt.*; +import java.applet.*; +import java.awt.event.*; +import java.net.*; +import java.io.*; +import java.lang.Thread; + +import com.sun.star.awt.XBitmap; +import com.sun.star.awt.XDevice; +import com.sun.star.awt.XDisplayBitmap; +import com.sun.star.awt.XGraphics; +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XWindowPeer; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XSystemChildFactory; +import com.sun.star.awt.WindowDescriptor; +import com.sun.star.awt.WindowClass; +import com.sun.star.awt.WindowAttribute; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiServiceFactory; + +class PaintThread extends java.lang.Thread +{ + private XWindow m_xWindow; + private XBitmap m_xBitmap; + private com.sun.star.awt.Rectangle m_aRect; + + private Object m_oRequestsLock; + private boolean m_bToPaint = false; + + private boolean m_bDisposed = false; + + public static boolean interceptedRects( com.sun.star.awt.Rectangle aRect1, com.sun.star.awt.Rectangle aRect2 ) + { + return ( ( aRect1.X <= aRect2.X && aRect2.X <= aRect1.X + aRect1.Width + || aRect1.X <= aRect2.X + aRect2.Width && aRect2.X + aRect2.Width <= aRect1.X + aRect1.Width + || aRect2.X <= aRect1.X && aRect1.X <= aRect2.X + aRect2.Width + || aRect2.X <= aRect1.X + aRect1.Width && aRect1.X + aRect1.Width <= aRect2.X + aRect2.Width ) + && ( aRect1.Y <= aRect2.Y && aRect2.Y <= aRect1.Y + aRect1.Height + || aRect1.Y <= aRect2.Y + aRect2.Height && aRect2.Y + aRect2.Height <= aRect1.Y + aRect1.Height + || aRect2.Y <= aRect1.Y && aRect1.Y <= aRect2.Y + aRect2.Height + || aRect2.Y <= aRect1.Y + aRect1.Height && aRect1.Y + aRect1.Height <= aRect2.Y + aRect2.Height ) ); + } + + public PaintThread( XWindow xWindow ) + { + m_oRequestsLock = new Object(); + m_xWindow = xWindow; + } + + public void setPaintRequest( XBitmap xBitmap, com.sun.star.awt.Rectangle aRect, com.sun.star.awt.Rectangle aClip ) + { + synchronized( m_oRequestsLock ) + { + if ( PaintThread.interceptedRects( aRect, aClip ) ) + { + m_xBitmap = xBitmap; + m_aRect = aRect; + m_bToPaint = true; + } + } + } + + public void disposeThread() + { + m_bDisposed = true; + } + + public void run() + { + while( !m_bDisposed ) + { + try { + Thread.sleep( 200 ); + } catch( Exception e ) {} + + XBitmap xBitmap = null; + com.sun.star.awt.Rectangle aRect = null; + boolean bPaint = false; + + synchronized( m_oRequestsLock ) + { + if ( m_bToPaint ) + { + xBitmap = m_xBitmap; + aRect = m_aRect; + m_bToPaint = false; + bPaint = true; + } + } + + if ( bPaint ) + { + XDevice xDevice = (XDevice)UnoRuntime.queryInterface( XDevice.class, m_xWindow ); + if ( xDevice != null ) + { + XGraphics xGraphics = xDevice.createGraphics(); + if ( xBitmap != null ) + { + XDisplayBitmap xDisplayBitmap = xDevice.createDisplayBitmap( xBitmap ); + + com.sun.star.awt.Size aSize = xBitmap.getSize(); + xGraphics.draw( xDisplayBitmap, 0, 0, aSize.Width, aSize.Height, + aRect.X, aRect.Y, aRect.Width, aRect.Height ); + } + + xGraphics.drawLine( aRect.X - 1, aRect.Y - 1, + aRect.X + aRect.Width + 1, aRect.Y - 1 ); + xGraphics.drawLine( aRect.X + aRect.Width + 1, aRect.Y - 1, + aRect.X + aRect.Width + 1, aRect.Y + aRect.Height + 1 ); + xGraphics.drawLine( aRect.X + aRect.Width + 1, aRect.Y + aRect.Height + 1, + aRect.X - 1, aRect.Y + aRect.Height + 1 ); + xGraphics.drawLine( aRect.X - 1, aRect.Y + aRect.Height + 1, + aRect.X - 1, aRect.Y - 1 ); + } + } + } + } +}; + diff --git a/embeddedobj/test/Container1/WindowHelper.java b/embeddedobj/test/Container1/WindowHelper.java new file mode 100644 index 000000000..66de646dd --- /dev/null +++ b/embeddedobj/test/Container1/WindowHelper.java @@ -0,0 +1,155 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package embeddedobj.test; + +import java.awt.*; +import java.applet.*; +import java.awt.event.*; +import java.net.*; +import java.io.*; + +import com.sun.star.awt.XBitmap; +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XWindowPeer; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XSystemChildFactory; +import com.sun.star.awt.WindowDescriptor; +import com.sun.star.awt.WindowClass; +import com.sun.star.awt.WindowAttribute; +import com.sun.star.awt.VclWindowPeerAttribute; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Any; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +class WindowHelper { + + public static XWindow createWindow( XMultiServiceFactory xFactory, NativeView aParent, java.awt.Rectangle aBounds ) + { + XWindow xWindow = null; + XToolkit xToolkit = null; + + // get access to toolkit of remote office to create the container window of new target frame + try{ + xToolkit = (XToolkit)UnoRuntime.queryInterface( XToolkit.class, + xFactory.createInstance("com.sun.star.awt.Toolkit") ); + } + catch( Exception ex ) + { + return null; + } + + XSystemChildFactory xChildFactory = (XSystemChildFactory)UnoRuntime.queryInterface( + XSystemChildFactory.class, + xToolkit); + + try + { + XWindowPeer xPeer = null; + Integer nHandle = aParent.getHWND(); + short nSystem = (short)aParent.getNativeWindowSystemType(); + byte[] lProcID = new byte[0]; +/* + try { + xPeer = xChildFactory.createSystemChild((Object)nHandle, lProcID, nSystem); + } + catch( Exception e ) + {} +*/ + if (xPeer==null) + { + JavaWindowPeerFake aWrapper = new JavaWindowPeerFake(aParent); + + XWindowPeer xParentPeer = (XWindowPeer)UnoRuntime.queryInterface( + XWindowPeer.class, + aWrapper); + + WindowDescriptor aDescriptor = new WindowDescriptor(); + aDescriptor.Type = WindowClass.TOP; + aDescriptor.WindowServiceName = "workwindow"; + aDescriptor.ParentIndex = 1; + aDescriptor.Parent = xParentPeer; + aDescriptor.Bounds = new com.sun.star.awt.Rectangle( (int)aBounds.getX(), + (int)aBounds.getY(), + (int)aBounds.getWidth(), + (int)aBounds.getHeight() ); + + System.out.println( "The rectangle for vcl window is:\nx = " + (int)aBounds.getX() + + "; y = " + (int)aBounds.getY() + + "; width = " + (int)aBounds.getWidth() + + "; height = " + (int)aBounds.getHeight() ); + + if (nSystem == com.sun.star.lang.SystemDependent.SYSTEM_WIN32) + aDescriptor.WindowAttributes = WindowAttribute.SHOW; + else + aDescriptor.WindowAttributes = WindowAttribute.SYSTEMDEPENDENT; + + aDescriptor.WindowAttributes |= VclWindowPeerAttribute.CLIPCHILDREN; + + xPeer = xToolkit.createWindow( aDescriptor ); + } + + xWindow = (XWindow)UnoRuntime.queryInterface( XWindow.class, xPeer); + if ( xWindow != null ) + xWindow.setPosSize( (int)aBounds.getX(), + (int)aBounds.getY(), + (int)aBounds.getWidth(), + (int)aBounds.getHeight(), + com.sun.star.awt.PosSize.POSSIZE ); + } + catch( Exception ex1 ) + { + System.out.println( "Exception on VCL window creation: " + ex1 ); + xWindow = null; + } + + return xWindow; + } + + public static XBitmap getVCLBitmapFromBytes( XMultiServiceFactory xFactory, Object aAny ) + { + if ( !AnyConverter.isArray( aAny ) ) + throw new com.sun.star.uno.RuntimeException(); + + Object[] aArgs = new Object[1]; + aArgs[0] = aAny; + XBitmap xResult = null; + + try { + XSingleServiceFactory xBitmapFactory = (XSingleServiceFactory)UnoRuntime.queryInterface( + XSingleServiceFactory.class, + xFactory.createInstance( "com.sun.star.embed.BitmapCreator" ) ); + + xResult = (XBitmap)UnoRuntime.queryInterface( + XBitmap.class, + xBitmapFactory.createInstanceWithArguments( aArgs ) ); + } + catch( Exception e ) + { + System.out.println( "Could not create VCL bitmap based on sequence," ); + System.out.println( "exception: " + e ); + } + + return xResult; + } +}; + diff --git a/embeddedobj/test/Container1/makefile.mk b/embeddedobj/test/Container1/makefile.mk new file mode 100644 index 000000000..f2523cae5 --- /dev/null +++ b/embeddedobj/test/Container1/makefile.mk @@ -0,0 +1,78 @@ +# +# 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ = ..$/.. +TARGET = EmbedContFrame +PRJNAME = embeddedobj +PACKAGE = embeddedobj$/test + +# --- Settings ----------------------------------------------------- +.INCLUDE: settings.mk + +# EXEC_CLASSPATH_TMP = \ +# $(foreach,i,$(JARFILES) $(SOLARBINDIR)$/$i)$(LIBO_PATH_SEPARATOR) +# EXEC_CLASSPATH = \ +# $(strip $(subst,!,$(LIBO_PATH_SEPARATOR) $(EXEC_CLASSPATH_TMP:s/ /!/))) + +#----- compile .java files ----------------------------------------- + +JARFILES = ridl.jar unoil.jar jurt.jar juh.jar java_uno.jar + +JAVAFILES = \ + EmbedContApp.java\ + EmbedContFrame.java\ + NativeView.java\ + WindowHelper.java\ + JavaWindowPeerFake.java\ + BitmapPainter.java\ + PaintThread.java + +CLASSFILES = $(patsubst %.java,$(OUT_COMP_CLASS)/%.class,$(JAVAFILES)) + + +# --- Targets ------------------------------------------------------ + +.INCLUDE: target.mk + +ALL : $(OUT)$/slo$/nativeview.obj + JavaStorageTestExample + +JavaStorageTestExample : $(CLASSFILES) + @echo -------------------------------------------------------------------------------- + @echo "Please use following command to execute the example!" + @echo ------ + @echo "dmake run" + @echo -------------------------------------------------------------------------------- + +# $(OUT)$/slo$/nativeview.obj: + # cd nativelib; dmake debug=t; cd .. + +# echo $(SOLARBINDIR) +# echo $(EXEC_CLASSPATH) + +run: $(CLASSFILES) + +set PATH=$(PATH)$(LIBO_PATH_SEPARATOR)$(JDK14PATH)$/jre$/bin && \ + java -classpath "$(OUT)$/class;$(OUT)$/lib;$(OUT)$/bin;$(JDK14PATH)$/jre$/bin;$(JDK14PATH)$/jre$/lib;$(CLASSPATH)" embeddedobj.test.EmbedContFrame + +debug: $(CLASSFILES) + +set PATH=$(PATH)$(LIBO_PATH_SEPARATOR)$(JDK14PATH)$/jre$/bin && \ + jdb -classpath "$(OUT)$/class;$(OUT)$/lib;$(OUT)$/bin;$(CLASSPATH)" embeddedobj.test.EmbedContFrame + +clean : + -$(DELRECURSIVE) $(subst /,$(PS),$(OUT_COMP_CLASS)) + diff --git a/embeddedobj/test/Container1/nativelib/exports.dxp b/embeddedobj/test/Container1/nativelib/exports.dxp new file mode 100644 index 000000000..e03ee0d2b --- /dev/null +++ b/embeddedobj/test/Container1/nativelib/exports.dxp @@ -0,0 +1,3 @@ +Java_embeddedobj_test_NativeView_getNativeWindowSystemType; +Java_embeddedobj_test_NativeView_getNativeWindow; + diff --git a/embeddedobj/test/Container1/nativelib/makefile.mk b/embeddedobj/test/Container1/nativelib/makefile.mk new file mode 100644 index 000000000..771b12bf4 --- /dev/null +++ b/embeddedobj/test/Container1/nativelib/makefile.mk @@ -0,0 +1,69 @@ +# +# 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=..$/..$/.. +PRJNAME=embeddedobj +TARGET=nativeview +# LIBTARGET=NO +USE_DEFFILE=TRUE +ENABLE_EXCEPTIONS=TRUE +VERSIONOBJ= +PACKAGE=embeddedobj$/test + +USE_JAVAVER:=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +.IF "$(JAVANUMVER:s/.//)" >= "000100040000" + +SLOFILES= $(SLO)$/nativeview.obj + +SHL1TARGET=$(TARGET) +SHL1IMPLIB=i$(SHL1TARGET) + +SHL1STDLIBS= \ + jawt.lib + +SHL1OBJS=$(SLOFILES) +SHL1VERSIONOBJ= + +DEF1NAME=$(SHL1TARGET) +DEF1EXPORTFILE=exports.dxp + +SHL1HEADER=nativeview.h + +.ENDIF # "$(JAVANUMVER:s/.//)" >= "000100040000" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.IF "$(JAVANUMVER:s/.//)" >= "000100040000" + +$(SLO)$/nativeview.obj : $(SHL1HEADER) + +$(SHL1HEADER) : + javah -classpath $(OUT)$/class -o $(SHL1HEADER) embeddedobj.test + +.ENDIF # "$(JAVANUMVER:s/.//)" >= "000100040000" + + diff --git a/embeddedobj/test/Container1/nativelib/nativeview.c b/embeddedobj/test/Container1/nativelib/nativeview.c new file mode 100644 index 000000000..a58ce54ef --- /dev/null +++ b/embeddedobj/test/Container1/nativelib/nativeview.c @@ -0,0 +1,118 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifdef _WIN32 + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +// property name to register own window procedure on hwnd +#define OLD_PROC_KEY "oldwindowproc" +// signature of this window procedure +static LRESULT APIENTRY NativeViewWndProc( HWND , UINT , WPARAM , LPARAM ); + +#endif + +#include "jawt.h" +#include "jawt_md.h" +#include "NativeView.h" + +#define MY_ASSERT(X,S) if (!X) { fprintf(stderr,"%s\n",S); return 0;} + +#define SYSTEM_WIN32 1 +#define SYSTEM_WIN16 2 +#define SYSTEM_JAVA 3 +#define SYSTEM_MAC 5 +#define SYSTEM_XWINDOW 6 + +/***************************************************************************** + * + * Class : NativeView + * Method : getNativeWindowSystemType + * Signature : ()I + * Description: returns an identifier for the current operating system + */ +JNIEXPORT jint JNICALL Java_embeddedobj_test_NativeView_getNativeWindowSystemType + (JNIEnv * env, jobject obj_this) +{ + return SYSTEM_WIN32; +} + +/***************************************************************************** + * + * Class : NativeView + * Method : getNativeWindow + * Signature : ()J + * Description: returns the native systemw window handle of this object + */ +JNIEXPORT jlong JNICALL Java_embeddedobj_test_NativeView_getNativeWindow + (JNIEnv * env, jobject obj_this) +{ + jboolean result ; + jint lock ; + JAWT awt ; + JAWT_DrawingSurface* ds ; + JAWT_DrawingSurfaceInfo* dsi ; +#ifdef _WIN32 + JAWT_Win32DrawingSurfaceInfo* dsi_win ; +#else + // FIXME: Where is dsi_x11 defined? + // Added below because I'm guessing this test breaks + + // JAWT_X11DrawingSurfaceInfo*dsi_x11 ; +#endif + jlong drawable; + + /* Get the AWT */ + awt.version = JAWT_VERSION_1_3; + result = JAWT_GetAWT(env, &awt); + MY_ASSERT(result!=JNI_FALSE,"wrong jawt version"); + + /* Get the drawing surface */ + if ((ds = awt.GetDrawingSurface(env, obj_this)) == NULL) + return 0; + + /* Lock the drawing surface */ + lock = ds->Lock(ds); + MY_ASSERT((lock & JAWT_LOCK_ERROR)==0,"can't lock the drawing surface"); + + /* Get the drawing surface info */ + dsi = ds->GetDrawingSurfaceInfo(ds); + + /* Get the platform-specific drawing info */ +#ifdef _WIN32 + dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo; + drawable = (jlong)dsi_win->hwnd; +#else + dsi_x11 = (JAWT_X11DrawingSurfaceInfo*)dsi->platformInfo; + drawable = (jlong)dsi_x11->drawable; +#endif + + /* Free the drawing surface info */ + ds->FreeDrawingSurfaceInfo(dsi); + /* Unlock the drawing surface */ + ds->Unlock(ds); + /* Free the drawing surface */ + awt.FreeDrawingSurface(ds); + + return drawable; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/Container1/nativelib/nativeview.h b/embeddedobj/test/Container1/nativelib/nativeview.h new file mode 100644 index 000000000..b8687bd09 --- /dev/null +++ b/embeddedobj/test/Container1/nativelib/nativeview.h @@ -0,0 +1,50 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_openoffice_OpenOffice */ + +#ifndef _Included_NativeView +#define _Included_NativeView +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_openoffice_OpenOffice + * Method: getNativeWindowSystemType + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_embeddedobj_test_NativeView_getNativeWindowSystemType + (JNIEnv *, jobject); + +/* + * Class: org_openoffice_OpenOffice + * Method: getNativeWindow + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_embeddedobj_test_NativeView_getNativeWindow + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/MainThreadExecutor/exports.dxp b/embeddedobj/test/MainThreadExecutor/exports.dxp new file mode 100644 index 000000000..51703a046 --- /dev/null +++ b/embeddedobj/test/MainThreadExecutor/exports.dxp @@ -0,0 +1,2 @@ +component_writeInfo +component_getFactory diff --git a/embeddedobj/test/MainThreadExecutor/makefile.mk b/embeddedobj/test/MainThreadExecutor/makefile.mk new file mode 100644 index 000000000..a8fef803e --- /dev/null +++ b/embeddedobj/test/MainThreadExecutor/makefile.mk @@ -0,0 +1,58 @@ +# +# 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=..$/.. + +PRJNAME=embeddedobj +TARGET=mainexec +LIBTARGET=NO +USE_DEFFILE=TRUE + +# --- Settings ----------------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +SHL1TARGET= $(TARGET) + +SHL1IMPLIB= i$(TARGET) +SHL1OBJS= \ + $(SLO)$/xexecutor.obj\ + $(SLO)$/register.obj + +EXCEPTIONSFILES= \ + $(SLO)$/xexecutor.obj\ + $(SLO)$/register.obj + +SHL1STDLIBS=\ + $(SALLIB)\ + $(VCLLIB)\ + $(CPPULIB)\ + $(CPPUHELPERLIB) + +SHL1DEF= $(MISC)$/$(SHL1TARGET).def + +DEF1NAME= $(SHL1TARGET) +DEF1EXPORTFILE= exports.dxp + +# --- Targets ------------------------------------------------------------- + +.INCLUDE : target.mk + + diff --git a/embeddedobj/test/MainThreadExecutor/register.cxx b/embeddedobj/test/MainThreadExecutor/register.cxx new file mode 100644 index 000000000..777222e5d --- /dev/null +++ b/embeddedobj/test/MainThreadExecutor/register.cxx @@ -0,0 +1,85 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <cppuhelper/factory.hxx> + +#include "xexecutor.hxx" + +using namespace ::com::sun::star; + + +extern "C" { + +SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey ) +{ + void * pRet = 0; + + OUString aImplName( OUString::createFromAscii( pImplName ) ); + uno::Reference< lang::XSingleServiceFactory > xFactory; + + if ( pServiceManager && aImplName.equals( UNOMainThreadExecutor::impl_staticGetImplementationName() ) ) + { + xFactory= ::cppu::createOneInstanceFactory( reinterpret_cast< lang::XMultiServiceFactory*>( pServiceManager ), + UNOMainThreadExecutor::impl_staticGetImplementationName(), + UNOMainThreadExecutor::impl_staticCreateSelfInstance, + UNOMainThreadExecutor::impl_staticGetSupportedServiceNames() ); + } + + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +sal_Bool SAL_CALL component_writeInfo( void * pServiceManager, void * pRegistryKey ) +{ + if (pRegistryKey) + { + try + { + uno::Reference< registry::XRegistryKey > xKey( reinterpret_cast< registry::XRegistryKey* >( pRegistryKey ) ); + + uno::Reference< registry::XRegistryKey > xNewKey; + + xNewKey = xKey->createKey( OUString("/") + + UNOMainThreadExecutor::impl_staticGetImplementationName() + + OUString( "/UNO/SERVICES") ); + + uno::Sequence< OUString > &rServices = UNOMainThreadExecutor::impl_staticGetSupportedServiceNames(); + for( sal_Int32 ind = 0; ind < rServices.getLength(); ind++ ) + xNewKey->createKey( rServices.getConstArray()[ind] ); + + return sal_True; + } + catch (registry::InvalidRegistryException &) + { + OSL_FAIL( "### InvalidRegistryException!" ); + } + } + return sal_False; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/MainThreadExecutor/xexecutor.cxx b/embeddedobj/test/MainThreadExecutor/xexecutor.cxx new file mode 100644 index 000000000..96dbd3771 --- /dev/null +++ b/embeddedobj/test/MainThreadExecutor/xexecutor.cxx @@ -0,0 +1,123 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "xexecutor.hxx" +#include <vcl/svapp.hxx> +#include <osl/thread.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace ::com::sun::star; + +void MainThreadExecutor_Impl::execute() +{ + Application::PostUserEvent( LINK( this, MainThreadExecutor_Impl, executor ), NULL ); +} + +IMPL_LINK_NOARG( MainThreadExecutor_Impl, executor, void*, void ) +{ + if ( m_xJob.is() ) + { + try { + m_xJob->execute( m_aArgs ); + } catch( uno::Exception& ) {} + } + + m_bExecuted = sal_True; + delete this; +} + +MainThreadExecutor_Impl::MainThreadExecutor_Impl( const uno::Reference< task::XJob >& xJob, + const uno::Sequence< beans::NamedValue >& aArguments ) +: m_xJob( xJob ) +, m_aArgs( aArguments ) +, m_bExecuted( sal_False ) +{ +} + + +uno::Any SAL_CALL UNOMainThreadExecutor::execute( const uno::Sequence< beans::NamedValue >& aArguments ) + throw ( lang::IllegalArgumentException, + uno::Exception, + uno::RuntimeException ) +{ + uno::Reference< task::XJob > xJob; + + if ( aArguments.getLength() > 0 && aArguments[0].Name == "JobToExecute" ) + aArguments[0].Value >>= xJob; + + if ( !xJob.is() ) + throw lang::IllegalArgumentException(); + + uno::Sequence< beans::NamedValue > aArgsForJob; + if ( aArguments.getLength() > 1 ) + aArgsForJob = uno::Sequence< beans::NamedValue >( aArguments.getConstArray() + 1, aArguments.getLength() - 1 ); + + MainThreadExecutor_Impl* pExecutor = new MainThreadExecutor_Impl( xJob, aArgsForJob ); + pExecutor->execute(); + + // it is not a main thread, so it can be blocked + // while( !pExecutor->isExecuted() ) + // ::osl::Thread::yield(); + + // TODO: implement transferring of the return values and exceptions + + return uno::Any(); +} + + +uno::Sequence< OUString > SAL_CALL UNOMainThreadExecutor::impl_staticGetSupportedServiceNames() +{ + uno::Sequence< OUString > aRet { "com.sun.star.comp.thread.MainThreadExecutor" }; + return aRet; +} + + +OUString SAL_CALL UNOMainThreadExecutor::impl_staticGetImplementationName() +{ + return OUString("com.sun.star.comp.thread.MainThreadExecutor"); +} + + +uno::Reference< uno::XInterface > SAL_CALL UNOMainThreadExecutor::impl_staticCreateSelfInstance( + const uno::Reference< lang::XMultiServiceFactory >& xServiceManager ) +{ + return uno::Reference< uno::XInterface >( *new UNOMainThreadExecutor( xServiceManager ) ); +} + + +OUString SAL_CALL UNOMainThreadExecutor::getImplementationName() + throw ( uno::RuntimeException ) +{ + return impl_staticGetImplementationName(); +} + +sal_Bool SAL_CALL UNOMainThreadExecutor::supportsService( const OUString& ServiceName ) + throw ( uno::RuntimeException ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL UNOMainThreadExecutor::getSupportedServiceNames() + throw ( uno::RuntimeException ) +{ + return impl_staticGetSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/MainThreadExecutor/xexecutor.hxx b/embeddedobj/test/MainThreadExecutor/xexecutor.hxx new file mode 100644 index 000000000..1c7610540 --- /dev/null +++ b/embeddedobj/test/MainThreadExecutor/xexecutor.hxx @@ -0,0 +1,77 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <cppuhelper/implbase.hxx> + +#include <tools/link.hxx> + +class UNOMainThreadExecutor : public ::cppu::WeakImplHelper< css::task::XJob, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::lang::XMultiServiceFactory > m_xFactory; + +public: + explicit UNOMainThreadExecutor( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + : m_xFactory( xFactory ) + { + } + + static css::uno::Sequence< OUString > SAL_CALL impl_staticGetSupportedServiceNames(); + + static OUString SAL_CALL impl_staticGetImplementationName(); + + static css::uno::Reference< css::uno::XInterface > SAL_CALL + impl_staticCreateSelfInstance( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ); + + // XJob + virtual css::uno::Any SAL_CALL execute( const css::uno::Sequence< css::beans::NamedValue >& Arguments ) throw (css::lang::IllegalArgumentException, css::uno::Exception, css::uno::RuntimeException); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() throw (css::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw (css::uno::RuntimeException); + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (css::uno::RuntimeException); + +}; + +class MainThreadExecutor_Impl +{ + css::uno::Reference< css::task::XJob > m_xJob; + css::uno::Sequence< css::beans::NamedValue > m_aArgs; + + sal_Bool m_bExecuted; +public: + MainThreadExecutor_Impl( const css::uno::Reference< css::task::XJob >& xJob, + const css::uno::Sequence< css::beans::NamedValue >& aArguments ); + + void execute(); + + DECL_LINK( executor, void* ); + + sal_Bool isExecuted() const { return m_bExecuted; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/mtexecutor/bitmapcreator.cxx b/embeddedobj/test/mtexecutor/bitmapcreator.cxx new file mode 100644 index 000000000..60a89f8cd --- /dev/null +++ b/embeddedobj/test/mtexecutor/bitmapcreator.cxx @@ -0,0 +1,107 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "bitmapcreator.hxx" + +#include <cppuhelper/supportsservice.hxx> +#include <vcl/bitmapex.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/stream.hxx> + +using namespace ::com::sun::star; + + +uno::Sequence< OUString > SAL_CALL VCLBitmapCreator::impl_staticGetSupportedServiceNames() +{ + return + { + "com.sun.star.embed.BitmapCreator", + "com.sun.star.comp.embed.BitmapCreator" + }; +} + + +OUString SAL_CALL VCLBitmapCreator::impl_staticGetImplementationName() +{ + return OUString("com.sun.star.comp.embed.BitmapCreator"); +} + + +uno::Reference< uno::XInterface > SAL_CALL VCLBitmapCreator::impl_staticCreateSelfInstance( + const uno::Reference< lang::XMultiServiceFactory >& xServiceManager ) +{ + return uno::Reference< uno::XInterface >( *new VCLBitmapCreator( xServiceManager ) ); +} + + +uno::Reference< uno::XInterface > SAL_CALL VCLBitmapCreator::createInstance() + throw ( uno::Exception, + uno::RuntimeException) +{ + BitmapEx aBitmap; + uno::Reference< uno::XInterface> aResult( VCLUnoHelper::CreateBitmap( aBitmap ), uno::UNO_QUERY ); + + return aResult; +} + + +uno::Reference< uno::XInterface > SAL_CALL VCLBitmapCreator::createInstanceWithArguments( + const uno::Sequence< uno::Any >& aArguments ) + throw ( uno::Exception, + uno::RuntimeException) +{ + if ( aArguments.getLength() != 1 ) + throw uno::Exception(); // TODO + + uno::Sequence< sal_Int8 > aOrigBitmap; + if ( !( aArguments[0] >>= aOrigBitmap ) ) + throw uno::Exception(); // TODO + + BitmapEx aBitmap; + SvMemoryStream aStream( aOrigBitmap.getArray(), aOrigBitmap.getLength(), StreamMode::READ ); + aStream >> aBitmap; + if ( aStream.GetError() ) + throw uno::Exception(); // TODO + + uno::Reference< uno::XInterface > aResult( VCLUnoHelper::CreateBitmap( aBitmap ), uno::UNO_QUERY ); + + return aResult; +} + + +OUString SAL_CALL VCLBitmapCreator::getImplementationName() + throw ( uno::RuntimeException ) +{ + return impl_staticGetImplementationName(); +} + +sal_Bool SAL_CALL VCLBitmapCreator::supportsService( const OUString& ServiceName ) + throw ( uno::RuntimeException ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL VCLBitmapCreator::getSupportedServiceNames() + throw ( uno::RuntimeException ) +{ + return impl_staticGetSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/mtexecutor/bitmapcreator.hxx b/embeddedobj/test/mtexecutor/bitmapcreator.hxx new file mode 100644 index 000000000..0faed31cc --- /dev/null +++ b/embeddedobj/test/mtexecutor/bitmapcreator.hxx @@ -0,0 +1,57 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + + +#include <cppuhelper/implbase.hxx> + +class VCLBitmapCreator : public ::cppu::WeakImplHelper< + css::lang::XSingleServiceFactory, + css::lang::XServiceInfo > + +{ +public: + explicit VCLBitmapCreator( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + {} + + static css::uno::Sequence< OUString > SAL_CALL impl_staticGetSupportedServiceNames(); + + static OUString SAL_CALL impl_staticGetImplementationName(); + + static css::uno::Reference< css::uno::XInterface > SAL_CALL impl_staticCreateSelfInstance( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ); + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) throw (css::uno::Exception, css::uno::RuntimeException); + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) throw (css::uno::Exception, css::uno::RuntimeException); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() throw (css::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw (css::uno::RuntimeException); + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (css::uno::RuntimeException); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/mtexecutor/exports.dxp b/embeddedobj/test/mtexecutor/exports.dxp new file mode 100644 index 000000000..51703a046 --- /dev/null +++ b/embeddedobj/test/mtexecutor/exports.dxp @@ -0,0 +1,2 @@ +component_writeInfo +component_getFactory diff --git a/embeddedobj/test/mtexecutor/mainthreadexecutor.cxx b/embeddedobj/test/mtexecutor/mainthreadexecutor.cxx new file mode 100644 index 000000000..82a124a2c --- /dev/null +++ b/embeddedobj/test/mtexecutor/mainthreadexecutor.cxx @@ -0,0 +1,123 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "mainthreadexecutor.hxx" + +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + + +uno::Sequence< OUString > SAL_CALL MainThreadExecutor::impl_staticGetSupportedServiceNames() +{ + return + { + "com.sun.star.thread.MainThreadExecutor", + "com.sun.star.comp.thread.MainThreadExecutor" + } +} + + +OUString SAL_CALL MainThreadExecutor::impl_staticGetImplementationName() +{ + return OUString("com.sun.star.comp.thread.MainThreadExecutor"); +} + + +uno::Reference< uno::XInterface > SAL_CALL MainThreadExecutor::impl_staticCreateSelfInstance( + const uno::Reference< lang::XMultiServiceFactory >& xServiceManager ) +{ + return uno::Reference< uno::XInterface >( *new MainThreadExecutor( xServiceManager ) ); +} + + +uno::Any SAL_CALL MainThreadExecutor::execute( const uno::Sequence< beans::NamedValue >& aArguments ) + throw ( lang::IllegalArgumentException, + uno::Exception, + uno::RuntimeException ) +{ + uno::Reference< task::XJob > xJob; + uno::Sequence< beans::NamedValue > aValues; + sal_Int32 nValuesSize = 0; + + for ( sal_Int32 nInd = 0; nInd < aArguments.getLength(); nInd++ ) + if ( aArguments[nInd].Name == "JobToExecute" ) + aArguments[nInd].Value >>= xJob; + else + { + aValues.realloc( ++nValuesSize ); + aValues[nValuesSize-1].Name = aArguments[nInd].Name; + aValues[nValuesSize-1].Value = aArguments[nInd].Value; + } + + if ( xJob.is() ) + { + MainThreadExecutorRequest* pMainThreadExecutorRequest = new MainThreadExecutorRequest( xJob, aValues ); + Application::PostUserEvent( LINK( NULL, MainThreadExecutor, worker ), pMainThreadExecutorRequest ); + } + + // TODO: wait for result + return uno::Any(); +} + + +IMPL_STATIC_LINK( MainThreadExecutor, worker, MainThreadExecutorRequest*, pThreadExecutorRequest, void ) +{ + pThreadExecutorRequest->doIt(); + + delete pThreadExecutorRequest; +} + + +OUString SAL_CALL MainThreadExecutor::getImplementationName() + throw ( uno::RuntimeException ) +{ + return impl_staticGetImplementationName(); +} + +sal_Bool SAL_CALL MainThreadExecutor::supportsService( const OUString& ServiceName ) + throw ( uno::RuntimeException ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL MainThreadExecutor::getSupportedServiceNames() + throw ( uno::RuntimeException ) +{ + return impl_staticGetSupportedServiceNames(); +} + + +MainThreadExecutorRequest::MainThreadExecutorRequest( const uno::Reference< task::XJob >& xJob, + const uno::Sequence< beans::NamedValue >& aValues ) +: m_xJob( xJob ) +, m_aValues( aValues ) +{ +} + + +void MainThreadExecutorRequest::doIt() +{ + if ( m_xJob.is() ) + m_xJob->execute( m_aValues ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/mtexecutor/mainthreadexecutor.hxx b/embeddedobj/test/mtexecutor/mainthreadexecutor.hxx new file mode 100644 index 000000000..1bb98ee91 --- /dev/null +++ b/embeddedobj/test/mtexecutor/mainthreadexecutor.hxx @@ -0,0 +1,73 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + + +#include <cppuhelper/implbase.hxx> + +#include <tools/link.hxx> + +class MainThreadExecutorRequest +{ + css::uno::Reference< css::task::XJob > m_xJob; + css::uno::Sequence< css::beans::NamedValue > m_aValues; + + public: + MainThreadExecutorRequest( + const css::uno::Reference< css::task::XJob >& xJob, + const css::uno::Sequence< css::beans::NamedValue >& aValues ); + + void doIt(); +}; + +class MainThreadExecutor : public ::cppu::WeakImplHelper< + css::task::XJob, + css::lang::XServiceInfo > + +{ +public: + explicit MainThreadExecutor( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + {} + + static css::uno::Sequence< OUString > SAL_CALL impl_staticGetSupportedServiceNames(); + + static OUString SAL_CALL impl_staticGetImplementationName(); + + static css::uno::Reference< css::uno::XInterface > SAL_CALL impl_staticCreateSelfInstance( + const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ); + + DECL_STATIC_LINK( MainThreadExecutor, worker, MainThreadExecutorRequest*, void ); + + // XJob + virtual css::uno::Any SAL_CALL execute( const css::uno::Sequence< css::beans::NamedValue >& Arguments ) throw (css::lang::IllegalArgumentException, css::uno::Exception, css::uno::RuntimeException); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() throw (css::uno::RuntimeException); + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw (css::uno::RuntimeException); + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (css::uno::RuntimeException); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/test/mtexecutor/makefile.mk b/embeddedobj/test/mtexecutor/makefile.mk new file mode 100644 index 000000000..061f1ecb0 --- /dev/null +++ b/embeddedobj/test/mtexecutor/makefile.mk @@ -0,0 +1,69 @@ +# +# 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/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=..$/.. + +PRJNAME=embeddedobj +TARGET=mainthrexec + + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +LIBTARGET=NO +USE_DEFFILE=NO +INCPRE+=$(ATL_INCLUDE) + +# --- Files -------------------------------------------------------- + +SHL1TARGET= $(TARGET) + +SHL1IMPLIB= i$(TARGET) + +SLOFILES = \ + $(SLO)$/mainthreadexecutor.obj\ + $(SLO)$/bitmapcreator.obj\ + $(SLO)$/mteregister.obj + +EXCEPTIONSFILES = \ + $(SLO)$/mainthreadexecutor.obj\ + $(SLO)$/bitmapcreator.obj\ + $(SLO)$/mteregister.obj + +SHL1OBJS= $(SLOFILES) + +SHL1STDLIBS=\ + $(SALLIB)\ + $(CPPULIB)\ + $(CPPUHELPERLIB)\ + $(TOOLSLIB)\ + $(VCLLIB)\ + itk.lib + +DEF1EXPORTFILE= exports.dxp + +SHL1DEF= $(MISC)$/$(SHL1TARGET).def + +DEF1NAME= $(SHL1TARGET) + + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/embeddedobj/test/mtexecutor/mteregister.cxx b/embeddedobj/test/mtexecutor/mteregister.cxx new file mode 100644 index 000000000..76d820055 --- /dev/null +++ b/embeddedobj/test/mtexecutor/mteregister.cxx @@ -0,0 +1,107 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <cppuhelper/factory.hxx> + +#include "mainthreadexecutor.hxx" +#include "bitmapcreator.hxx" + +using namespace ::com::sun::star; + + +extern "C" { + +SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey ) +{ + void * pRet = 0; + + OUString aImplName( OUString::createFromAscii( pImplName ) ); + uno::Reference< lang::XSingleServiceFactory > xFactory; + + if ( pServiceManager ) + { + if ( aImplName.equals( MainThreadExecutor::impl_staticGetImplementationName() ) ) + { + xFactory= ::cppu::createOneInstanceFactory( reinterpret_cast< lang::XMultiServiceFactory*>( pServiceManager ), + MainThreadExecutor::impl_staticGetImplementationName(), + MainThreadExecutor::impl_staticCreateSelfInstance, + MainThreadExecutor::impl_staticGetSupportedServiceNames() ); + } + else if ( aImplName.equals( VCLBitmapCreator::impl_staticGetImplementationName() ) ) + { + xFactory= ::cppu::createOneInstanceFactory( reinterpret_cast< lang::XMultiServiceFactory*>( pServiceManager ), + VCLBitmapCreator::impl_staticGetImplementationName(), + VCLBitmapCreator::impl_staticCreateSelfInstance, + VCLBitmapCreator::impl_staticGetSupportedServiceNames() ); + + } + + if ( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} + +sal_Bool SAL_CALL component_writeInfo( void * pServiceManager, void * pRegistryKey ) +{ + if (pRegistryKey) + { + try + { + uno::Reference< registry::XRegistryKey > xKey( reinterpret_cast< registry::XRegistryKey* >( pRegistryKey ) ); + + uno::Reference< registry::XRegistryKey > xNewKey; + uno::Sequence< OUString > rServices; + sal_Int32 ind = 0; + + xNewKey = xKey->createKey( OUString("/") + + MainThreadExecutor::impl_staticGetImplementationName() + + OUString( "/UNO/SERVICES") ); + + rServices = MainThreadExecutor::impl_staticGetSupportedServiceNames(); + for( ind = 0; ind < rServices.getLength(); ind++ ) + xNewKey->createKey( rServices.getConstArray()[ind] ); + + xNewKey = xKey->createKey( OUString("/") + + VCLBitmapCreator::impl_staticGetImplementationName() + + OUString( "/UNO/SERVICES") ); + + rServices = VCLBitmapCreator::impl_staticGetSupportedServiceNames(); + for( ind = 0; ind < rServices.getLength(); ind++ ) + xNewKey->createKey( rServices.getConstArray()[ind] ); + + return sal_True; + } + catch (registry::InvalidRegistryException &) + { + OSL_FAIL( "### InvalidRegistryException!" ); + } + } + return sal_False; +} + +} // extern "C" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embeddedobj/util/embobj.component b/embeddedobj/util/embobj.component new file mode 100644 index 000000000..0e68c26b3 --- /dev/null +++ b/embeddedobj/util/embobj.component @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.EmbeddedObjectCreator" + constructor="embeddedobj_UNOEmbeddedObjectCreator_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.EmbeddedObjectCreator"/> + <service name="com.sun.star.embed.EmbeddedObjectCreator"/> + </implementation> + <implementation name="com.sun.star.comp.embed.OOoEmbeddedObjectFactory" + constructor="embeddedobj_OOoEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OOoEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OOoEmbeddedObjectFactory"/> + </implementation> + <implementation name="com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory" + constructor="embeddedobj_OOoSpecialEmbeddedObjectFactory_get_implementation" + single-instance="true"> + <service name="com.sun.star.comp.embed.OOoSpecialEmbeddedObjectFactory"/> + <service name="com.sun.star.embed.OOoSpecialEmbeddedObjectFactory"/> + </implementation> +</component> |